diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 5dd00e1e0..0d934b465 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -2567,6 +2567,9 @@ The behavior of this option depends on the type of windowing you use. In text windowing, text highlighting or inverse video is often used; with tiles, generally displays a small plus-symbol beside the object on the top of the pile. +.lp hitpointbar +Show a hit point bar graph behind your name and title. +Only available for TTY and Windows GUI, and only when statushilites is on. .lp horsename Name your starting horse (ex. ``horsename:Trigger''). Cannot be set with the `O' command. @@ -2910,7 +2913,8 @@ attack to which it is resistant (default on). Persistent. .lp standout Boldface monsters and ``\fB--More--\fP'' (default off). Persistent. .lp statushilites -Enable coloring of status fields (default off). +Controls how many turns status hilite behaviors highlight +the field. If negated or set to zero, disables status hiliting. See ``Configuring Status Hilites'' for further information. .lp status_updates Allow updates to the status lines at the bottom of the screen (default true). @@ -3479,17 +3483,22 @@ Your copy of NetHack may have been compiled with support for ``Status Hilites''. If so, you can customize your game display by setting thresholds to change the color or appearance of fields in the status display. .pg +The format for defining status colors is: +.si +.lp "OPTION=hilite_status: field-name/behavior/color&attributes" +.ei +.pg For example, the following line in your config file will cause the hitpoints field to display in the color red if your hitpoints drop to or below a threshold of 30%: .si -.lp "OPTION=hilite_status: hitpoints/30%/red/normal" +.lp "OPTION=hilite_status: hitpoints/<30%/red/normal" .ei .pg For another example, the following line in your config file will cause wisdom to be displayed red if it drops and green if it rises. .si -.lp "OPTION=hilite_status: wisdom/updown/red/green" +.lp "OPTION=hilite_status: wisdom/down/red/up/green" .ei .pg You can adjust the display of the following status fields: @@ -3508,18 +3517,73 @@ experience condition .\"TABLE_END Do not delete this line. .TE .lp "" +The pseudo-field `characteristics' can be used to set all six +of Str, Dex, Con, Int, Wis, and Cha at once. `HD' is `hit dice', +an approximation of experience level displayed when polymorphed. +`experience', `time', and `score' are conditionally displayed +depending upon your other option settings. +.lp "" +Instead of a behavior, `condition' takes the following condition flags: +stone, slime, strngl, foodpois, termill, blind, deaf, stun, conf, hallu, +lev, fly, and ride. You can use `major_troubles' as an alias +for stone through termill, `minor_troubles' for blind through hallu, +`movement' for lev, fly, and ride, and `all' for every condition. +.lp "" +Allowed behaviors are "always", "up", "down", "changed", a +percentage or absolute number threshold, or a text to match against. +.si +.lp "*" +"always" will set the default attributes for that field. +.lp "*" +"up" and "down" set the field attributes for when the field +value changes upwards or downwards. This attribute times out after +statushilites turns. +.lp "*" +"changed" sets the field attribute for when the field value +changes. This attribute times out after statushilites turns. +.lp "*" +percentage sets the field attribute when the field value +matches the percentage. If the percentage is prefixed with '<' +or '>', it also matches when value is below or above the percentage. +Only valid for `power' and `hitpoints' fields. +.lp "*" +absolute value sets the attribute when the field value +matches that number. If the number is prefixed with '<' +or '>', it also matches when value is below or above. +.lp "*" +text match sets the attribute when the field value +matches the text. Text matches can only be used for `alignment', +`carrying-capacity', and `dungeon-level'. +.ei +.lp "" Allowed colors are black, red, green, brown, blue, magenta, cyan, gray, orange, lightgreen, yellow, lightblue, lightmagenta, lightcyan, and white. .lp "" -Allowed attributes are bold, inverse, normal. +Allowed attributes are bold, inverse, underline, blink, dim, and normal. Note that the platform used may interpret the attributes any way it wants. .lp "" -Behaviours can occur based on percentage thresholds, updown, or absolute values. The in-game options menu can help you determine the correct syntax for a config file. .lp "" -The whole feature can be disabled by setting option statushilites off. +The whole feature can be disabled by setting option +statushilites to 0. +.lp "" +Example hilites: +.sd +.si +OPTION=hilite_status: gold/up/yellow/down/brown +OPTION=hilite_status: characteristics/up/green/down/red +OPTION=hilite_status: hitpoints/100%/gray&normal +OPTION=hilite_status: hitpoints/<100%/green&normal +OPTION=hilite_status: hitpoints/<66%/yellow&normal +OPTION=hilite_status: hitpoints/<50%/orange&normal +OPTION=hilite_status: hitpoints/<33%/red&bold +OPTION=hilite_status: hitpoints/<15%/red&inverse +OPTION=hilite_status: condition/major/orange&inverse +OPTION=hilite_status: condition/lev+fly/red&inverse +.ei +.ed .pg .hn 2 Modifying NetHack Symbols diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index edd7f9995..9119ac17d 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -3141,6 +3141,10 @@ on the top of the pile. Name your starting horse (ex.\ ``{\tt horsename:Trigger}''). Cannot be set with the `{\tt O}' command. %.lp +\item[\ib{hitpointbar}] +Show a hit point bar graph behind your name and title. +Only available for TTY and Windows GUI, and only when statushilites is on. +%.lp \item[\ib{ignintr}] Ignore interrupt signals, including breaks (default off). Persistent. %.lp @@ -3535,7 +3539,8 @@ attack to which it is resistant (default on). Persistent. Boldface monsters and ``{\tt --More--}'' (default off). Persistent. %.lp \item[\ib{statushilites}] -Enable coloring of status fields (default off). +Controls how many turns status hilite behaviors highlight +the field. If negated or set to zero, disables status hiliting. See ``{\it Configuring Status Hilites\/}'' for further information. %.lp \item[\ib{status\verb+_+updates}] @@ -4271,18 +4276,23 @@ The pattern should be a regular expression. Your copy of NetHack may have been compiled with support for {\it Status Hilites}. If so, you can customize your game display by setting thresholds to change the color or appearance of fields in the status display. - +%.pg +The format for defining status colors is: +\begin{verbatim} + OPTION=hilite_status: field-name/behavior/color&attributes +\end{verbatim} +%.pg For example, the following line in your config file will cause the hitpoints field to display in the color red if your hitpoints drop to or below a threshold of 30%: \begin{verbatim} - OPTION=hilite_status: hitpoints/30%/red/normal + OPTION=hilite_status: hitpoints/<30%/red/normal \end{verbatim} %.pg For another example, the following line in your config file will cause wisdom to be displayed red if it drops and green if it rises. \begin{verbatim} - OPTION=hilite_status: wisdom/updown/red/green + OPTION=hilite_status: wisdom/down/red/up/green \end{verbatim} You can adjust the display of the following status fields: %.sd @@ -4301,6 +4311,56 @@ experience & condition\\ \end{tabular} \end{center} %.ed +%.lp "" +The pseudo-field `characteristics' can be used to set all six +of Str, Dex, Con, Int, Wis, and Cha at once. `HD' is `hit dice', +an approximation of experience level displayed when polymorphed. +`experience', `time', and `score' are conditionally displayed +depending upon your other option settings. + +%.lp "" +Instead of a behavior, `condition' takes the following condition flags: +{\it stone}, {\it slime}, {\it strngl}, {\it foodpois}, {\it termill}, +{\it blind}, {\it deaf}, {\it stun}, {\it conf}, {\it hallu}, +{\it lev}, {\it fly}, and {\it ride}. You can use `major\_troubles' as an alias +for stone through termill, `minor\_troubles' for blind through hallu, +`movement' for lev, fly, and ride, and `all' for every condition. + +%.lp "" +Allowed behaviors are "always", "up", "down", "changed", a +percentage or absolute number threshold, or a text to match against. + +\blist{} +%.lp "*" +\item{\bb{}} +"always" will set the default attributes for that field. +%.lp "*" +\item{\bb{}} +"up" and "down" set the field attributes for when the field +value changes upwards or downwards. This attribute times out after +statushilites turns. +%.lp "*" +\item{\bb{}} +"changed" sets the field attribute for when the field value +changes. This attribute times out after statushilites turns. +%.lp "*" +\item{\bb{}} +percentage sets the field attribute when the field value +matches the percentage. If the percentage is prefixed with `{\tt <}' +or `{\tt >}', it also matches when value is below or above the percentage. +Only valid for `power' and `hitpoints' fields. +%.lp "*" +\item{\bb{}} +absolute value sets the attribute when the field value +matches that number. If the number is prefixed with `{\tt <}' +or `{\tt >}', it also matches when value is below or above. +%.lp "*" +\item{\bb{}} +text match sets the attribute when the field value +matches the text. Text matches can only be used for `alignment', +`carrying-capacity', and `dungeon-level'. +%.ei +\elist %.lp "" Allowed colors are {\it black}, {\it red}, {\it green}, {\it brown}, @@ -4309,12 +4369,32 @@ Allowed colors are {\it black}, {\it red}, {\it green}, {\it brown}, {\it lightcyan}, and {\it white}. %.lp "" -Behaviours can occur based on percentage thresholds, updown, or absolute values. +Allowed attributes are {\it bold}, {\it inverse}, {\it underline}, +{\it blink}, {\it dim}, and {\it normal}. +Note that the platform used may interpret the attributes any way it +wants. + +%.lp "" The in-game options menu can help you determine the correct syntax for a config file. %.lp "" -The whole feature can be disable by setting option {\it statushilites} off. +The whole feature can be disable by setting option {\it statushilites} to 0. + +%.lp "" +Example hilites: +\begin{verbatim} + OPTION=hilite_status: gold/up/yellow/down/brown + OPTION=hilite_status: characteristics/up/green/down/red + OPTION=hilite_status: hitpoints/100%/gray&normal + OPTION=hilite_status: hitpoints/<100%/green&normal + OPTION=hilite_status: hitpoints/<66%/yellow&normal + OPTION=hilite_status: hitpoints/<50%/orange&normal + OPTION=hilite_status: hitpoints/<33%/red&bold + OPTION=hilite_status: hitpoints/<15%/red&inverse + OPTION=hilite_status: condition/major/orange&inverse + OPTION=hilite_status: condition/lev+fly/red&inverse +\end{verbatim} %.lp %.hn 2 diff --git a/doc/fixes36.1 b/doc/fixes36.1 index b7b8ddc33..dcfa8eec0 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -443,9 +443,16 @@ fix buffer overflow in wizard mode for '#' command when 'extmenu' option is on "you suddenly vomit" was given two turns before actually vomiting, so you could get that message, move a bit, then get "you can move again" after the 2 turn freeze applied along with the actual vomit -fix mention_walls reporting secret doors as solid walls corpses and other flammable items not subject to direct burning or fire-based erosion which were thrown or dropped into lava remained intact +if a potion on the floor survived a land mine explosion and got propelled at + the hero, it didn't behave like a potion if it hit +adjust candelabrum's weight when candles are attached +when lit candelabrum burned out, persistent inventory window showed that it + was no longer lit but still showed phantom candles attached +improve hilite_status, allowing multiple stops per field, and temporarily or + permanently hilited fields +give feedback when released from a bear trap Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository @@ -502,6 +509,7 @@ hero poly'd into vampire could drain monster down to 0 HP without killing it, triggering impossible "dmonsfree: 1 removed doesn't match 0 pending" "you observe a fog cloud where a vampire/bat was" if an unseen vampire on the far side of a closed door shifted shape to pass under that door +fix mention_walls reporting secret doors as solid stone Platform- and/or Interface-Specific Fixes @@ -666,6 +674,7 @@ new paranoid_confirm settings: wand-break to require "yes" rather than 'y' than 'y' when hero inflicted with lycanthropy has polymorph control option force_invmenu to make commands asking for inventory items always use a menu instead of a text line query +option hitpointbar to show a bar graph of hit points behind title field Platform- and/or Interface-Specific New Features diff --git a/doc/window.doc b/doc/window.doc index 2ebf9f8b5..e648b23a4 100644 --- a/doc/window.doc +++ b/doc/window.doc @@ -401,7 +401,7 @@ status_enablefield(int fldindex, char fldname, char fieldfmt, boolean enable) BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX, BL_LEVELDESC, BL_EXP, BL_CONDITION -- There are MAXBLSTATS status fields (from botl.h) -status_update(int fldindex, genericptr_t ptr, int chg, int percentage) +status_update(int fldindex, genericptr_t ptr, int chg, int percentage, int color, long *colormasks) -- update the value of a status field. -- the fldindex identifies which field is changing and is an integer index value from botl.h @@ -417,58 +417,124 @@ status_update(int fldindex, genericptr_t ptr, int chg, int percentage) -- ptr is usually a "char *", unless fldindex is BL_CONDITION. If fldindex is BL_CONDITION, then ptr is a long value with any or none of the following bits set (from botl.h): - BL_MASK_BLIND 0x00000001L - BL_MASK_CONF 0x00000002L - BL_MASK_FOODPOIS 0x00000004L - BL_MASK_ILL 0x00000008L - BL_MASK_HALLU 0x00000010L - BL_MASK_STUNNED 0x00000020L - BL_MASK_SLIMED 0x00000040L + BL_MASK_STONE 0x00000001L + BL_MASK_SLIME 0x00000002L + BL_MASK_STRNGL 0x00000004L + BL_MASK_FOODPOIS 0x00000008L + BL_MASK_TERMILL 0x00000010L + BL_MASK_BLIND 0x00000020L + BL_MASK_DEAF 0x00000040L + BL_MASK_STUN 0x00000080L + BL_MASK_CONF 0x00000100L + BL_MASK_HALLU 0x00000200L + BL_MASK_LEV 0x00000400L + BL_MASK_FLY 0x00000800L + BL_MASK_RIDE 0x00001000L -- The value passed for BL_GOLD includes a leading symbol for GOLD "$:nnn". If the window port needs to use the textual gold amount without the leading "$:" the port will have to add 2 to the passed "ptr" for the BL_GOLD case. + -- color is an unsigned int. + int & 0x00FF = color CLR_* + int >> 8 = attribute (if any) + + This contains the color and attribute that the field should + be displayed in. + + This is relevant for everything except BL_CONDITION fldindex. + If fldindex is BL_CONDITION, this parameter should be ignored, + as condition hilighting is done via the next colormasks + parameter instead. + + -- colormasks - pointer to cond_hilites[] array of colormasks. + + Only relevant for BL_CONDITION fldindex. The window port + should ignore this parameter for other fldindex values. + + Each condition bit must only ever appear in one of the + CLR_ array members, but can appear in multiple HL_ATTCLR_ + offsets (because more than one attribute can co-exist). + + For the user's chosen set of BL_MASK_ condition bits, + They are stored internally in the cond_hilites[] array, + at the array offset aligned to the color those condtion + bits should display in. + + For example, if the user has chosen to display strngl + and stone and termill in red and inverse, + + BL_MASK_SLIME 0x00000002 + BL_MASK_STRNGL 0x00000004 + BL_MASK_TERMILL 0x00000010 + + The bitmask corresponding to those conditions is + 0x00000016 (or 00010110 in binary) and the color + is at offset 1 (CLR_RED). + + Here is how that is stored in the cond_hilites[] array: + + +------+----------------------+--------------------+ + |array | | | + |offset| macro for indexing | bitmask | + |------+----------------------+--------------------+ + | 0 | CLR_BLACK | | + +------+----------------------+--------------------+ + | 1 | CLR_RED | 00010110 | + +------+----------------------+--------------------+ + | 2 | CLR_GREEN | | + +------+----------------------+--------------------+ + | 3 | CLR_BROWN | | + +------+----------------------+--------------------+ + | 4 | CLR_BLUE | | + +------+----------------------+--------------------+ + | 5 | CLR_MAGENTA | | + +------+----------------------+--------------------+ + | 6 | CLR_CYAN | | + +------+----------------------+--------------------+ + | 7 | CLR_GRAY | | + +------+----------------------+--------------------+ + | 8 | NO_COLOR | | + +------+----------------------+--------------------+ + | 9 | CLR_ORANGE | | + +------+----------------------+--------------------+ + | 10 | CLR_BRIGHT_GREEN | | + +------+----------------------+--------------------+ + | 11 | CLR_BRIGHT_YELLOW | | + +------+----------------------+--------------------+ + | 12 | CLR_BRIGHT_BLUE | | + +------+----------------------+--------------------+ + | 13 | CLR_BRIGHT_MAGENTA | | + +------+----------------------+--------------------+ + | 14 | CLR_BRIGHT_CYAN | | + +------+----------------------+--------------------+ + | 15 | CLR_WHITE | | + +------+----------------------+--------------------+ + | 16 | HL_ATTCLR_DIM | | CLR_MAX + +------+----------------------+--------------------+ + | 17 | HL_ATTCLR_BLINK | | + +------+----------------------+--------------------+ + | 18 | HL_ATTCLR_ULINE | | + +------+----------------------+--------------------+ + | 19 | HL_ATTCLR_INVERSE | 00010110 | + +------+----------------------+--------------------+ + | 20 | HL_ATTCLR_BOLD | | + +------+----------------------+--------------------+ + | 21 | beyond array boundary | BL_ATTCLR_MAX + + The window port can AND (&) the bits passed in the + ptr argument to status_update() with any non-zero + entries in the cond_hilites[] array to determine + the color and attributes for displaying the + condition on the screen for the user. + + If the bit for a particular condition does not + appear in any of the cond_hilites[] array offsets, + that condition should be displayed in the default + color and attributes. status_finish() -- called when it is time for the window port to tear down the status display and free allocated memory, etc. -status_threshold(int fldidx, int threshholdtype, anything threshold, - int behavior, int under, int over) - -- called when a hiliting preference is added, changed, or - removed. - -- the fldindex identifies which field is having its hiliting - preference set. It is an integer index value from botl.h - -- fldindex could be any one of the following from botl.h: - BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, - BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX, - BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX, - BL_LEVELDESC, BL_EXP, BL_CONDITION - -- datatype is P_INT, P_UINT, P_LONG, or P_MASK. - -- threshold is an "anything" union which can contain the - datatype value. - -- behavior is used to define how threshold is used and can - be BL_TH_NONE, BL_TH_VAL_PERCENTAGE, BL_TH_VAL_ABSOLUTE, - or BL_TH_UPDOWN. BL_TH_NONE means don't do anything above - or below the threshold. BL_TH_VAL_PERCENTAGE treats the - threshold value as a precentage of the maximum possible - value. BL_TH_VAL_ABSOLUTE means that the threshold is an - actual value. BL_TH_UPDOWN means that threshold is not - used, and the two below/above hilite values indicate how - to display something going down (under) or rising (over). - -- under is the hilite attribute used if value is below the - threshold. The attribute can be BL_HILITE_NONE, - BL_HILITE_INVERSE, BL_HILITE_BOLD (-1, -2, or -3), or one - of the color indexes of CLR_BLACK, CLR_RED, CLR_GREEN, - CLR_BROWN, CLR_BLUE, CLR_MAGENTA, CLR_CYAN, CLR_GRAY, - CLR_ORANGE, CLR_BRIGHT_GREEN, CLR_YELLOW, CLR_BRIGHT_BLUE, - CLR_BRIGHT_MAGENTA, CLR_BRIGHT_CYAN, or CLR_WHITE (0 - 15). - -- over is the hilite attribute used if value is at or above - the threshold. The attribute can be BL_HILITE_NONE, - BL_HILITE_INVERSE, BL_HILITE_BOLD (-1, -2, or -3), or one - of the color indexes of CLR_BLACK, CLR_RED, CLR_GREEN, - CLR_BROWN, CLR_BLUE, CLR_MAGENTA, CLR_CYAN, CLR_GRAY, - CLR_ORANGE, CLR_BRIGHT_GREEN, CLR_YELLOW, CLR_BRIGHT_BLUE, - CLR_BRIGHT_MAGENTA, CLR_BRIGHT_CYAN, or CLR_WHITE (0 - 15). E. Misc. Routines @@ -703,6 +769,7 @@ to support: | softkeyboard | WC2_SOFTKEYBOARD | wc2_softkeyboard |boolean | | wraptext | WC2_WRAPTEXT | wc2_wraptext |boolean | | selectsaved | WC2_SELECTSAVED | wc2_selectsaved |boolean | + | hitpointbar | WC2_HITPOINTBAR | wc2_hitpointbar |boolean | +--------------------+--------------------+--------------------+--------+ align_message -- where to place message window (top, bottom, left, right) @@ -723,6 +790,7 @@ font_status -- port should use a font by this name for status display. font_text -- port should use a font by this name for text windows. fullscreen -- port should try to use the whole screen. hilite_pet -- port should mark pets in some special way on the map. +hitpointbar -- port should show a graphical bar representing hit points map_mode -- port should display the map in the manner specified. player_selection -- dialog or prompts for choosing character. diff --git a/include/botl.h b/include/botl.h index 061d6c716..ce2a94496 100644 --- a/include/botl.h +++ b/include/botl.h @@ -27,42 +27,18 @@ Astral Plane \GXXXXNNNN:123456 HP:1234(1234) Pw:1234(1234) AC:-127 #define MAXCO (COLNO + 40) #endif -#ifdef STATUS_VIA_WINDOWPORT -#if 0 -/* clang-format off */ -#define BL_FLUSH -1 -#define BL_TITLE 0 -#define BL_STR 1 -#define BL_DX 2 -#define BL_CO 3 -#define BL_IN 4 -#define BL_WI 5 -#define BL_CH 6 -#define BL_ALIGN 7 -#define BL_SCORE 8 -#define BL_CAP 9 -#define BL_GOLD 10 -#define BL_ENE 11 -#define BL_ENEMAX 12 -#define BL_XP 13 -#define BL_AC 14 -#define BL_HD 15 -#define BL_TIME 16 -#define BL_HUNGER 17 -#define BL_HP 18 -#define BL_HPMAX 19 -#define BL_LEVELDESC 20 -#define BL_EXP 21 -#define BL_CONDITION 22 -/* clang-format on */ +enum statusfields { + BL_CHARACTERISTICS = -2, /* alias for BL_STR..BL_CH */ + BL_FLUSH = -1, BL_TITLE = 0, + BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, /* 1..6 */ + BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX, /* 7..12 */ + BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX, BL_LEVELDESC, /* 13..20 */ + BL_EXP, BL_CONDITION +}; -#else -enum statusfields { BL_FLUSH = -1, BL_TITLE = 0, BL_STR, BL_DX, BL_CO, BL_IN, -BL_WI, BL_CH, BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX, -BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX, BL_LEVELDESC, -BL_EXP, BL_CONDITION }; -#endif -#define MAXBLSTATS BL_CONDITION+1 +enum relationships { LT_VALUE = -1, EQ_VALUE, GT_VALUE, TXT_VALUE }; + +#define MAXBLSTATS (BL_CONDITION + 1) #define BEFORE 0 #define NOW 1 @@ -87,7 +63,7 @@ BL_EXP, BL_CONDITION }; #define REASSESS_ONLY TRUE -#ifdef STATUS_HILITES +/* #ifdef STATUS_HILITES */ /* hilite status field behavior - coloridx values */ #define BL_HILITE_NONE -1 /* no hilite of this field */ #define BL_HILITE_INVERSE -2 /* inverse hilite */ @@ -98,9 +74,26 @@ BL_EXP, BL_CONDITION }; #define BL_TH_VAL_ABSOLUTE 101 /* threshold is particular value */ #define BL_TH_UPDOWN 102 /* threshold is up or down change */ #define BL_TH_CONDITION 103 /* threshold is bitmask of conditions */ -#endif +#define BL_TH_TEXTMATCH 104 /* threshold text value to match against */ +#define BL_TH_ALWAYS_HILITE 105 /* highlight regardless of value */ + + +#define HL_ATTCLR_DIM CLR_MAX + 0 +#define HL_ATTCLR_BLINK CLR_MAX + 1 +#define HL_ATTCLR_ULINE CLR_MAX + 2 +#define HL_ATTCLR_INVERSE CLR_MAX + 3 +#define HL_ATTCLR_BOLD CLR_MAX + 4 +#define BL_ATTCLR_MAX CLR_MAX + 5 + +enum hlattribs { HL_UNDEF = 0x00, + HL_NONE = 0x01, + HL_BOLD = 0x02, + HL_INVERSE = 0x04, + HL_ULINE = 0x08, + HL_BLINK = 0x10, + HL_DIM = 0x20 }; +/* #endif STATUS_HILITES */ extern const char *status_fieldnames[]; /* in botl.c */ -#endif #endif /* BOTL_H */ diff --git a/include/config.h b/include/config.h index 5f89126c3..6fcedc0fe 100644 --- a/include/config.h +++ b/include/config.h @@ -498,9 +498,7 @@ typedef unsigned char uchar; * Only available with POSIX_TYPES or GNU C */ /* #define MSGHANDLER */ -/* #define STATUS_VIA_WINDOWPORT */ /* re-work of the status line - updating process */ -/* #define STATUS_HILITES */ /* support hilites of status fields */ +#define STATUS_HILITES /* support hilites of status fields */ /* #define WINCHAIN */ /* stacked window systems */ diff --git a/include/decl.h b/include/decl.h index d161fae0f..b36c730f6 100644 --- a/include/decl.h +++ b/include/decl.h @@ -325,9 +325,7 @@ E NEARDATA char **viz_array; /* could see/in sight row pointers */ /* Window system stuff */ E NEARDATA winid WIN_MESSAGE; -#ifndef STATUS_VIA_WINDOWPORT E NEARDATA winid WIN_STATUS; -#endif E NEARDATA winid WIN_MAP, WIN_INVEN; /* pline (et al) for a single string argument (suppress compiler warning) */ diff --git a/include/extern.h b/include/extern.h index c35692979..3c1f79ed3 100644 --- a/include/extern.h +++ b/include/extern.h @@ -153,17 +153,17 @@ 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); -#ifdef STATUS_VIA_WINDOWPORT E void FDECL(status_initialize, (BOOLEAN_P)); E void NDECL(status_finish); E void FDECL(status_notify_windowport, (BOOLEAN_P)); +E void NDECL(status_eval_next_unhilite); #ifdef STATUS_HILITES -E boolean FDECL(set_status_hilites, (char *op, BOOLEAN_P)); -E void FDECL(clear_status_hilites, (BOOLEAN_P)); -E char *FDECL(get_status_hilites, (char *, int)); +E boolean FDECL(parse_status_hl1, (char *op, BOOLEAN_P)); +E void NDECL(clear_status_hilites); +E void NDECL(reset_status_hilites); +E int NDECL(count_status_hilites); E boolean NDECL(status_hilite_menu); #endif -#endif /* ### cmd.c ### */ @@ -869,6 +869,7 @@ E char *FDECL(upstart, (char *)); E char *FDECL(mungspaces, (char *)); E char *FDECL(trimspaces, (char *)); E char *FDECL(strip_newline, (char *)); +E char *FDECL(stripchars, (char *, const char *, const char *)); E char *FDECL(eos, (char *)); E boolean FDECL(str_end_is, (const char *, const char *)); E char *FDECL(strkitten, (char *, CHAR_P)); @@ -1536,7 +1537,7 @@ E void FDECL(Delay, (int)); /* ### mthrowu.c ### */ -E int FDECL(thitu, (int, int, struct obj *, const char *)); +E int FDECL(thitu, (int, int, struct obj **, const char *)); E int FDECL(ohitmon, (struct monst *, struct obj *, int, BOOLEAN_P)); E void FDECL(thrwmu, (struct monst *)); E int FDECL(spitmu, (struct monst *, struct attack *)); @@ -1714,8 +1715,11 @@ E boolean FDECL(parsesymbols, (char *)); E struct symparse *FDECL(match_sym, (char *)); E void NDECL(set_playmode); E int FDECL(sym_val, (const char *)); +E int FDECL(query_color, (const char *)); +E int FDECL(query_attr, (const char *)); E const char *FDECL(clr2colorname, (int)); E int FDECL(match_str2clr, (char *)); +E int FDECL(match_str2attr, (const char *, BOOLEAN_P)); E boolean FDECL(add_menu_coloring, (char *)); E boolean FDECL(get_menu_coloring, (char *, int *, int *)); E void NDECL(free_menu_coloring); @@ -1886,7 +1890,7 @@ E int FDECL(dopotion, (struct obj *)); E int FDECL(peffects, (struct obj *)); E void FDECL(healup, (int, int, BOOLEAN_P, BOOLEAN_P)); E void FDECL(strange_feeling, (struct obj *, const char *)); -E void FDECL(potionhit, (struct monst *, struct obj *, BOOLEAN_P)); +E void FDECL(potionhit, (struct monst *, struct obj *, int)); E void FDECL(potionbreathe, (struct obj *)); E int NDECL(dodip); E void FDECL(mongrantswish, (struct monst **)); @@ -2458,7 +2462,7 @@ E void FDECL(check_caitiff, (struct monst *)); E int FDECL(find_roll_to_hit, (struct monst *, UCHAR_P, struct obj *, int *, int *)); E boolean FDECL(attack, (struct monst *)); -E boolean FDECL(hmon, (struct monst *, struct obj *, int)); +E boolean FDECL(hmon, (struct monst *, struct obj *, int, int)); E int FDECL(damageum, (struct monst *, struct attack *)); E void FDECL(missum, (struct monst *, struct attack *, BOOLEAN_P)); E int FDECL(passive, (struct monst *, BOOLEAN_P, int, UCHAR_P, BOOLEAN_P)); @@ -2739,16 +2743,11 @@ E void FDECL(genl_putmsghistory, (const char *, BOOLEAN_P)); #ifdef HANGUPHANDLING E void NDECL(nhwindows_hangup); #endif -#ifdef STATUS_VIA_WINDOWPORT E void NDECL(genl_status_init); E void NDECL(genl_status_finish); E void FDECL(genl_status_enablefield, (int, const char *, const char *, BOOLEAN_P)); -E void FDECL(genl_status_update, (int, genericptr_t, int, int)); -#ifdef STATUS_HILITES -E void FDECL(genl_status_threshold, (int, int, anything, int, int, int)); -#endif -#endif +E void FDECL(genl_status_update, (int, genericptr_t, int, int, int, unsigned long *)); E void FDECL(dump_open_log, (time_t)); E void NDECL(dump_close_log); diff --git a/include/flag.h b/include/flag.h index 9cd6b3533..415688a92 100644 --- a/include/flag.h +++ b/include/flag.h @@ -251,7 +251,10 @@ struct instance_flags { boolean toptenwin; /* ending list in window instead of stdout */ boolean use_background_glyph; /* use background glyph when appropriate */ boolean use_menu_color; /* use color in menus; only if wc_color */ - boolean use_status_hilites; /* use color in status line */ +#ifdef STATUS_HILITES + long hilite_delta; /* number of moves to leave a temp hilite lit */ + long unhilite_deadline; /* time when oldest temp hilite should be unlit */ +#endif boolean zerocomp; /* write zero-compressed save files */ boolean rlecomp; /* alternative to zerocomp; run-length encoding * compression of levels when writing savefile */ @@ -360,6 +363,7 @@ struct instance_flags { boolean wc2_wraptext; /* wrap text */ boolean wc2_selectsaved; /* display a menu of user's saved games */ boolean wc2_darkgray; /* try to use dark-gray color for black glyphs */ + boolean wc2_hitpointbar; /* show graphical bar representing hit points */ boolean cmdassist; /* provide detailed assistance for some commands */ boolean clicklook; /* allow right-clicking for look */ boolean obsolete; /* obsolete options can point at this, it isn't used */ diff --git a/include/obj.h b/include/obj.h index 824ccca39..75b35779d 100644 --- a/include/obj.h +++ b/include/obj.h @@ -40,8 +40,9 @@ struct obj { unsigned owt; long quan; /* number of items */ - schar spe; /* quality of weapon, armor or ring (+ or -); + schar spe; /* quality of weapon, weptool, armor or ring (+ or -); number of charges for wand or charged tool ( >= -1 ); + number of candles attached to candelabrum; marks your eggs, tin variety and spinach tins; Schroedinger's Box (1) or royal coffers for a court (2); tells which fruit a fruit is; @@ -373,6 +374,12 @@ struct obj { #define ER_DAMAGED 2 /* object was damaged in some way */ #define ER_DESTROYED 3 /* object was destroyed */ +/* propeller method for potionhit() */ +#define POTHIT_HERO_BASH 0 /* wielded by hero */ +#define POTHIT_HERO_THROW 1 /* thrown by hero */ +#define POTHIT_MONST_THROW 2 /* thrown by a monster */ +#define POTHIT_OTHER_THROW 3 /* propelled by some other means [scatter()] */ + /* * Notes for adding new oextra structures: * diff --git a/include/winprocs.h b/include/winprocs.h index f3f4e870f..4d0d395a1 100644 --- a/include/winprocs.h +++ b/include/winprocs.h @@ -73,16 +73,11 @@ struct window_procs { void FDECL((*win_preference_update), (const char *)); char *FDECL((*win_getmsghistory), (BOOLEAN_P)); void FDECL((*win_putmsghistory), (const char *, BOOLEAN_P)); -#ifdef STATUS_VIA_WINDOWPORT void NDECL((*win_status_init)); void NDECL((*win_status_finish)); void FDECL((*win_status_enablefield), (int, const char *, const char *, BOOLEAN_P)); - void FDECL((*win_status_update), (int, genericptr_t, int, int)); -#ifdef STATUS_HILITES - void FDECL((*win_status_threshold), (int, int, anything, int, int, int)); -#endif -#endif + void FDECL((*win_status_update), (int, genericptr_t, int, int, int, unsigned long *)); boolean NDECL((*win_can_suspend)); }; @@ -160,16 +155,11 @@ extern #define preference_update (*windowprocs.win_preference_update) #define getmsghistory (*windowprocs.win_getmsghistory) #define putmsghistory (*windowprocs.win_putmsghistory) -#ifdef STATUS_VIA_WINDOWPORT /* there is a status_initialize() in botl.c, * which calls win_status_init() directly; same with status_finish. */ #define status_enablefield (*windowprocs.win_status_enablefield) #define status_update (*windowprocs.win_status_update) -#ifdef STATUS_HILITES -#define status_threshold (*windowprocs.win_status_threshold) -#endif -#endif /* * WINCAP @@ -221,7 +211,10 @@ extern #define WC2_HILITE_STATUS 0x0008L /* 04 hilite fields in status */ #define WC2_SELECTSAVED 0x0010L /* 05 saved game selection menu */ #define WC2_DARKGRAY 0x0020L /* 06 use bold black for black glyphs */ - /* 26 free bits */ +#define WC2_HITPOINTBAR 0x0040L /* 07 show bar representing hit points */ +#define WC2_FLUSH_STATUS 0x0080L /* 08 call status_update(BL_FLUSH) + after updating status window fields */ + /* 24 free bits */ #define ALIGN_LEFT 1 #define ALIGN_RIGHT 2 @@ -362,17 +355,11 @@ struct chain_procs { void FDECL((*win_preference_update), (CARGS, const char *)); char *FDECL((*win_getmsghistory), (CARGS, BOOLEAN_P)); void FDECL((*win_putmsghistory), (CARGS, const char *, BOOLEAN_P)); -#ifdef STATUS_VIA_WINDOWPORT void FDECL((*win_status_init), (CARGS)); void FDECL((*win_status_finish), (CARGS)); void FDECL((*win_status_enablefield), (CARGS, int, const char *, const char *, BOOLEAN_P)); - void FDECL((*win_status_update), (CARGS, int, genericptr_t, int, int)); -#ifdef STATUS_HILITES - void FDECL((*win_status_threshold), - (CARGS, int, int, anything, int, int, int)); -#endif -#endif + void FDECL((*win_status_update), (CARGS, int, genericptr_t, int, int, int, unsigned long)); boolean FDECL((*win_can_suspend), (CARGS)); }; #endif /* WINCHAIN */ diff --git a/include/wintty.h b/include/wintty.h index 4191cf349..1f270128f 100644 --- a/include/wintty.h +++ b/include/wintty.h @@ -215,13 +215,8 @@ E short FDECL(set_tty_font_name, (winid, char *)); #endif E char *NDECL(tty_get_color_string); #endif -#ifdef STATUS_VIA_WINDOWPORT E void NDECL(tty_status_init); -E void FDECL(tty_status_update, (int, genericptr_t, int, int)); -#ifdef STATUS_HILITES -E void FDECL(tty_status_threshold, (int, int, anything, int, int, int)); -#endif -#endif +E void FDECL(tty_status_update, (int, genericptr_t, int, int, int, unsigned long *)); /* other defs that really should go away (they're tty specific) */ E void NDECL(tty_start_screen); diff --git a/src/allmain.c b/src/allmain.c index b42a10604..0519cfab7 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -59,15 +59,12 @@ boolean resuming; context.rndencode = rnd(9000); set_wear((struct obj *) 0); /* for side-effects of starting gear */ (void) pickup(1); /* autopickup at initial location */ - } else { /* restore old game */ -#ifndef WIN32 - update_inventory(); /* for perm_invent */ -#endif + } + context.botlx = TRUE; /* for STATUS_HILITES */ + update_inventory(); /* for perm_invent */ + if (resuming) { /* restoring old game */ read_engr_at(u.ux, u.uy); /* subset of pickup() */ } -#ifdef WIN32 - update_inventory(); /* for perm_invent */ -#endif (void) encumber_msg(); /* in case they auto-picked up something */ if (defer_see_monsters) { @@ -325,6 +322,7 @@ boolean resuming; /* once-per-hero-took-time things go here */ /******************************************/ + status_eval_next_unhilite(); if (context.bypasses) clear_bypasses(); if ((u.uhave.amulet || Clairvoyant) && !In_endgame(&u.uz) @@ -537,7 +535,7 @@ void display_gamewindows() { WIN_MESSAGE = create_nhwindow(NHW_MESSAGE); -#ifdef STATUS_VIA_WINDOWPORT +#ifdef STATUS_HILITES status_initialize(0); #else WIN_STATUS = create_nhwindow(NHW_STATUS); @@ -561,7 +559,7 @@ display_gamewindows() * The mac port is not DEPENDENT on the order of these * displays, but it looks a lot better this way... */ -#ifndef STATUS_VIA_WINDOWPORT +#ifndef STATUS_HILITES display_nhwindow(WIN_STATUS, FALSE); #endif display_nhwindow(WIN_MESSAGE, FALSE); diff --git a/src/apply.c b/src/apply.c index dd50f32fa..e19e86b71 100644 --- a/src/apply.c +++ b/src/apply.c @@ -1224,6 +1224,9 @@ struct obj **optr; end_burn(obj, TRUE); /* candles are now gone */ useupall(obj); + /* candelabrum's weight is changing */ + otmp->owt = weight(otmp); + update_inventory(); } } @@ -2786,9 +2789,8 @@ struct obj *obj; int hitu, hitvalu; hitvalu = 8 + otmp->spe; - hitu = thitu(hitvalu, - dmgval(otmp, &youmonst), - otmp, (char *)0); + hitu = thitu(hitvalu, dmgval(otmp, &youmonst), + &otmp, (char *)0); if (hitu) { pline_The("%s hits you as you try to snatch it!", the(onambuf)); diff --git a/src/ball.c b/src/ball.c index 3b81a8f74..b9b63815d 100644 --- a/src/ball.c +++ b/src/ball.c @@ -610,11 +610,13 @@ drag: You("are jerked back by the iron ball!"); if ((victim = m_at(uchain->ox, uchain->oy)) != 0) { int tmp; + int dieroll = rnd(20); tmp = -2 + Luck + find_mac(victim); tmp += omon_adj(victim, uball, TRUE); - if (tmp >= rnd(20)) - (void) hmon(victim, uball, HMON_DRAGGED); + + if (tmp >= dieroll) + (void) hmon(victim, uball, HMON_DRAGGED, dieroll); else miss(xname(uball), victim); diff --git a/src/botl.c b/src/botl.c index d1d0a53e8..03f2aea11 100644 --- a/src/botl.c +++ b/src/botl.c @@ -12,6 +12,7 @@ const char *const enc_stat[] = { "", "Burdened", "Stressed", STATIC_OVL NEARDATA int mrank_sz = 0; /* loaded by max_rank_sz (from u_init) */ STATIC_DCL const char *NDECL(rank); +STATIC_DCL void NDECL(bot_via_windowport); static char * get_strength_str() @@ -32,8 +33,6 @@ get_strength_str() return buf; } -#if !defined(STATUS_VIA_WINDOWPORT) || defined(DUMPLOG) - char * do_statusline1() { @@ -228,21 +227,21 @@ do_statusline2() return newbot2; } -#ifndef STATUS_VIA_WINDOWPORT void bot() { if (youmonst.data && iflags.status_updates) { +#ifdef STATUS_HILITES + bot_via_windowport(); +#else curs(WIN_STATUS, 1, 0); putstr(WIN_STATUS, 0, do_statusline1()); curs(WIN_STATUS, 1, 1); putmixed(WIN_STATUS, 0, do_statusline2()); +#endif } context.botl = context.botlx = 0; } -#endif /* !STATUS_VIA_WINDOWPORT */ - -#endif /* !STATUS_VIA_WINDOWPORT || DUMPLOG */ /* convert experience level (1..30) to rank index (0..8) */ int @@ -387,18 +386,45 @@ char *buf; return ret; } -#ifdef STATUS_VIA_WINDOWPORT +/* =======================================================================*/ +/* statusnew routines */ /* =======================================================================*/ /* structure that tracks the status details in the core */ + +#ifdef STATUS_HILITES +struct hilite_s { + enum statusfields fld; + boolean set; + unsigned anytype; + anything value; + int behavior; + char textmatch[QBUFSZ]; + enum relationships rel; + int coloridx; + struct hilite_s *next; +}; + +struct condmap { + const char *id; + unsigned long bitmask; +}; +#endif /* STATUS_HILITES */ + struct istat_s { - long time; + const char *fldname; + const char *fldfmt; + long time; /* moves when this field hilite times out */ + boolean chg; /* need to recalc time? */ unsigned anytype; anything a; char *val; int valwidth; enum statusfields idxmax; enum statusfields fld; +#ifdef STATUS_HILITES + struct hilite_s *thresholds; +#endif }; @@ -406,63 +432,98 @@ 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)); + #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(assign_hilite, (char *, char *, char *, char *, - BOOLEAN_P)); -STATIC_DCL const char *FDECL(clridx_to_s, (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_condition, (char (*)[QBUFSZ], int)); +STATIC_DCL void FDECL(merge_bestcolor, (int *, int)); +STATIC_DCL void FDECL(get_hilite_color, (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 *)); +STATIC_DCL char *FDECL(hlattr2attrname, (int, char *, int)); +STATIC_DCL void FDECL(status_hilite_linestr_add, (int, struct hilite_s *, + unsigned long, const char *)); + +STATIC_DCL void NDECL(status_hilite_linestr_done); +STATIC_DCL int FDECL(status_hilite_linestr_countfield, (int)); +STATIC_DCL void NDECL(status_hilite_linestr_gather_conditions); +STATIC_DCL void NDECL(status_hilite_linestr_gather); +STATIC_DCL char *FDECL(status_hilite2str, (struct hilite_s *)); +STATIC_DCL boolean FDECL(status_hilite_menu_add, (int)); +#define has_hilite(i) (blstats[0][(i)].thresholds) #endif +#define INIT_BLSTAT(name, fmtstr, anytyp, wid, fld) \ + { name, fmtstr, 0L, FALSE, anytyp, { (genericptr_t) 0 }, (char *) 0, \ + wid, -1, fld } +#define INIT_BLSTATP(name, fmtstr, anytyp, wid, maxfld, fld) \ + { name, fmtstr, 0L, FALSE, anytyp, { (genericptr_t) 0 }, (char *) 0, \ + wid, maxfld, fld } + /* If entries are added to this, botl.h will require updating too */ STATIC_DCL struct istat_s initblstats[MAXBLSTATS] = { - { 0L, ANY_STR, { (genericptr_t) 0 }, (char *) 0, 80, 0, BL_TITLE}, - { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_STR}, - { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_DX}, - { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_CO}, - { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_IN}, - { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_WI}, - { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_CH}, - { 0L, ANY_STR, { (genericptr_t) 0 }, (char *) 0, 40, 0, BL_ALIGN}, - { 0L, ANY_LONG, { (genericptr_t) 0 }, (char *) 0, 20, 0, BL_SCORE}, - { 0L, ANY_LONG, { (genericptr_t) 0 }, (char *) 0, 20, 0, BL_CAP}, - { 0L, ANY_LONG, { (genericptr_t) 0 }, (char *) 0, 30, 0, BL_GOLD}, - { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, BL_ENEMAX, BL_ENE}, - { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_ENEMAX}, - { 0L, ANY_LONG, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_XP}, - { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_AC}, - { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_HD}, - { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 20, 0, BL_TIME}, - { 0L, ANY_UINT, { (genericptr_t) 0 }, (char *) 0, 40, 0, BL_HUNGER}, - { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, BL_HPMAX, BL_HP}, - { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_HPMAX}, - { 0L, ANY_STR, { (genericptr_t) 0 }, (char *) 0, 80, 0, BL_LEVELDESC}, - { 0L, ANY_LONG, { (genericptr_t) 0 }, (char *) 0, 20, 0, BL_EXP}, - { 0L, ANY_MASK32, - { (genericptr_t) 0 }, (char *) 0, 0, 0, BL_CONDITION} + INIT_BLSTAT("title", "%s", ANY_STR, 80, 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), + INIT_BLSTAT("intelligence", " In:%s", ANY_INT, 10, BL_IN), + INIT_BLSTAT("wisdom", " Wi:%s", ANY_INT, 10, BL_WI), + INIT_BLSTAT("charisma", " Ch:%s", ANY_INT, 10, BL_CH), + INIT_BLSTAT("alignment", " %s", ANY_STR, 40, BL_ALIGN), + INIT_BLSTAT("score", " S:%s", ANY_LONG, 20, BL_SCORE), + INIT_BLSTAT("carrying-capacity", " %s", ANY_LONG, 20, BL_CAP), + INIT_BLSTAT("gold", " %s", ANY_LONG, 30, BL_GOLD), + INIT_BLSTATP("power", " Pw:%s", ANY_INT, 10, BL_ENEMAX, BL_ENE), + INIT_BLSTAT("power-max", "(%s)", ANY_INT, 10, BL_ENEMAX), + INIT_BLSTAT("experience-level", " Xp:%s", ANY_LONG, 10, BL_XP), + INIT_BLSTAT("armor-class", " AC:%s", ANY_INT, 10, BL_AC), + INIT_BLSTAT("HD", " HD:%s", ANY_INT, 10, BL_HD), + INIT_BLSTAT("time", " T:%s", ANY_INT, 20, BL_TIME), + INIT_BLSTAT("hunger", " %s", ANY_UINT, 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("experience", "/%s", ANY_LONG, 20, BL_EXP), + INIT_BLSTAT("condition", "%s", ANY_MASK32, 0, BL_CONDITION) }; +#undef INIT_BLSTATP +#undef INIT_BLSTAT + struct istat_s blstats[2][MAXBLSTATS]; static boolean blinit = FALSE, update_all = FALSE; +static boolean valset[MAXBLSTATS]; +unsigned long blcolormasks[CLR_MAX]; +static long bl_hilite_moves = 0L; + +/* we don't put this next declaration in #ifdef STATUS_HILITES. + * In the absence of STATUS_HILITES, each array + * element will be 0 however, and quite meaningless, + * but we need to pass the first array element as + * the final argument of status_update, with or + * without STATUS_HILITES. + */ +unsigned long cond_hilites[BL_ATTCLR_MAX]; void -bot() +bot_via_windowport() { char buf[BUFSZ]; register char *nb; - static int idx = 0, idx_p, idxmax; - unsigned anytype; + static int i, idx = 0, idx_p, cap; long money; - int i, pc, chg, cap; - struct istat_s *curr, *prev; - boolean valset[MAXBLSTATS], chgval = FALSE, updated = FALSE; if (!blinit) panic("bot before init."); - if (!youmonst.data || !iflags.status_updates) { - context.botl = context.botlx = 0; - update_all = FALSE; - return; - } idx_p = idx; idx = 1 - idx; /* 0 -> 1, 1 -> 0 */ @@ -514,11 +575,9 @@ bot() /* Score */ blstats[idx][BL_SCORE].a.a_long = #ifdef SCORE_ON_BOTL - botl_score() -#else - 0L + flags.showscore ? botl_score() : #endif - ; + 0L; /* Hit points */ i = Upolyd ? u.mh : u.uhp; @@ -564,7 +623,7 @@ bot() blstats[idx][BL_AC].a.a_int = u.uac; /* Monster level (if Upolyd) */ - blstats[idx][BL_HD].a.a_int = Upolyd ? mons[u.umonnum].mlevel : 0; + blstats[idx][BL_HD].a.a_int = Upolyd ? (int) mons[u.umonnum].mlevel : 0; /* Experience */ blstats[idx][BL_XP].a.a_int = u.ulevel; @@ -618,6 +677,89 @@ bot() blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_FLY; if (u.usteed) blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_RIDE; + evaluate_and_notify_windowport(valset, idx, idx_p); +} + +STATIC_OVL boolean +evaluate_and_notify_windowport_field(fld, valsetlist, idx, idx_p) +int fld, idx, idx_p; +boolean *valsetlist; +{ + static int oldrndencode = 0; + int pc, chg, color = NO_COLOR; + unsigned anytype; + boolean updated = FALSE, reset; + struct istat_s *curr = NULL, *prev = NULL; + enum statusfields idxmax; + + /* + * Now pass the changed values to window port. + */ + anytype = blstats[idx][fld].anytype; + curr = &blstats[idx][fld]; + prev = &blstats[idx_p][fld]; + color = NO_COLOR; + + chg = update_all ? 0 : compare_blstats(prev, curr); + + /* Temporary? hack: moveloop()'s prolog for a new game sets + * context.rndencode after the status window has been init'd, + * so $:0 has already been encoded and cached by the window + * port. Without this hack, gold's \G sequence won't be + * recognized and ends up being displayed as-is for 'update_all'. + */ + if (context.rndencode != oldrndencode && fld == BL_GOLD) { + chg = 2; + oldrndencode = context.rndencode; + } + + reset = FALSE; +#ifdef STATUS_HILITES + if (!update_all && !chg) { + reset = hilite_reset_needed(prev, bl_hilite_moves); + if (reset) + curr->time = prev->time = 0L; + } +#endif + + if (update_all || chg || reset) { + idxmax = curr->idxmax; + pc = (idxmax > BL_FLUSH) ? percentage(curr, &blstats[idx][idxmax]) : 0; + + if (!valsetlist[fld]) + (void) anything_to_s(curr->val, &curr->a, anytype); + + 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 == 2) { + color = NO_COLOR; + chg = 0; + } + } +#endif /* STATUS_HILITES */ + status_update(fld, (genericptr_t) curr->val, + chg, pc, color, &cond_hilites[0]); + } else { + /* Color for conditions is done through cond_hilites[] */ + status_update(fld, (genericptr_t) &curr->a.a_ulong, chg, pc, + color, &cond_hilites[0]); + } + curr->chg = prev->chg = TRUE; + updated = TRUE; + } + return updated; +} + +static void +evaluate_and_notify_windowport(valsetlist, idx, idx_p) +int idx, idx_p; +boolean *valsetlist; +{ + int i; + boolean updated = FALSE; /* * Now pass the changed values to window port. @@ -629,28 +771,8 @@ bot() || ((i == BL_HD) && !Upolyd) || ((i == BL_XP || i == BL_EXP) && Upolyd)) continue; - anytype = blstats[idx][i].anytype; - curr = &blstats[idx][i]; - prev = &blstats[idx_p][i]; - chg = 0; - if (update_all - || ((chg = compare_blstats(prev, curr)) != 0) - || ((chgval = (valset[i] - && strcmp(blstats[idx][i].val, - blstats[idx_p][i].val))) != 0)) { - idxmax = blstats[idx][i].idxmax; - pc = (idxmax) ? percentage(curr, &blstats[idx][idxmax]) : 0; - if (!valset[i]) - (void) anything_to_s(curr->val, &curr->a, anytype); - if (anytype != ANY_MASK32) { - status_update(i, (genericptr_t) curr->val, - valset[i] ? chgval : chg, pc); - } else { - /* send pointer to mask */ - status_update(i, (genericptr_t) &curr->a.a_ulong, chg, 0); - } + if (evaluate_and_notify_windowport_field(i, valsetlist, idx, idx_p)) updated = TRUE; - } } /* * It is possible to get here, with nothing having been pushed @@ -664,13 +786,49 @@ bot() * index of BL_FLUSH (-1). */ if ((context.botlx && !updated) - || windowprocs.win_status_update == genl_status_update) - status_update(BL_FLUSH, (genericptr_t) 0, 0, 0); + || (windowprocs.wincap2 & WC2_FLUSH_STATUS) != 0L) + status_update(BL_FLUSH, (genericptr_t) 0, 0, 0, + NO_COLOR, &cond_hilites[0]); context.botl = context.botlx = 0; update_all = FALSE; } +void +status_eval_next_unhilite() +{ + int i; + struct istat_s *curr = NULL; + long next_unhilite, this_unhilite; + + bl_hilite_moves = moves; + /* figure out when the next unhilight needs to be performed */ + next_unhilite = 0L; + for (i = 0; i < MAXBLSTATS; ++i) { + curr = &blstats[0][i]; /* blstats[0][*].time == blstats[1][*].time */ + + if (curr->chg) { + struct istat_s *prev = &blstats[1][i]; + +#ifdef STATUS_HILITES + curr->time = prev->time = (bl_hilite_moves + iflags.hilite_delta); +#endif + curr->chg = prev->chg = FALSE; + } + + this_unhilite = curr->time; + if (this_unhilite > 0L + && (next_unhilite == 0L || this_unhilite < next_unhilite) +#ifdef STATUS_HILITES + && hilite_reset_needed(curr, this_unhilite + 1L) +#endif + ) + next_unhilite = this_unhilite; + } + if (next_unhilite > 0L && next_unhilite < bl_hilite_moves) + context.botl = TRUE; +} + void status_initialize(reassessment) boolean @@ -681,141 +839,24 @@ boolean const char *fieldname = (const char *) 0; if (!reassessment) { + if (blinit) + impossible("2nd status_initialize with full init."); init_blstats(); (*windowprocs.win_status_init)(); blinit = TRUE; -#ifdef STATUS_HILITES - status_notify_windowport(TRUE); -#endif } 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; - switch (fld) { - case BL_TITLE: - fieldfmt = "%s"; - fieldname = "title"; - status_enablefield(fld, fieldname, fieldfmt, TRUE); - break; - case BL_STR: - fieldfmt = " St:%s"; - fieldname = "strength"; - status_enablefield(fld, fieldname, fieldfmt, TRUE); - break; - case BL_DX: - fieldfmt = " Dx:%s"; - fieldname = "dexterity"; - status_enablefield(fld, fieldname, fieldfmt, TRUE); - break; - case BL_CO: - fieldfmt = " Co:%s"; - fieldname = "constitution"; - status_enablefield(fld, fieldname, fieldfmt, TRUE); - break; - case BL_IN: - fieldfmt = " In:%s"; - fieldname = "intelligence"; - status_enablefield(fld, fieldname, fieldfmt, TRUE); - break; - case BL_WI: - fieldfmt = " Wi:%s"; - fieldname = "wisdom"; - status_enablefield(fld, fieldname, fieldfmt, TRUE); - break; - case BL_CH: - fieldfmt = " Ch:%s"; - fieldname = "charisma"; - status_enablefield(fld, fieldname, fieldfmt, TRUE); - break; - case BL_ALIGN: - fieldfmt = " %s"; - fieldname = "alignment"; - status_enablefield(fld, fieldname, fieldfmt, TRUE); - break; - case BL_SCORE: - fieldfmt = " S:%s"; - fieldname = "score"; - status_enablefield(fld, fieldname, fieldfmt, - (!flags.showscore) ? FALSE : TRUE); - break; - case BL_CAP: - fieldfmt = " %s"; - fieldname = "carrying-capacity"; - status_enablefield(fld, fieldname, fieldfmt, TRUE); - break; - case BL_GOLD: - fieldfmt = " %s"; - fieldname = "gold"; - status_enablefield(fld, fieldname, fieldfmt, TRUE); - break; - case BL_ENE: - fieldfmt = " Pw:%s"; - fieldname = "power"; - status_enablefield(fld, fieldname, fieldfmt, TRUE); - break; - case BL_ENEMAX: - fieldfmt = "(%s)"; - fieldname = "power-max"; - status_enablefield(fld, fieldname, fieldfmt, TRUE); - break; - case BL_XP: - fieldfmt = " Xp:%s"; - fieldname = "experience-level"; - status_enablefield(fld, fieldname, fieldfmt, - (Upolyd) ? FALSE : TRUE); - break; - case BL_AC: - fieldfmt = " AC:%s"; - fieldname = "armor-class"; - status_enablefield(fld, fieldname, fieldfmt, TRUE); - break; - case BL_HD: - fieldfmt = " HD:%s"; - fieldname = "HD"; - status_enablefield(fld, fieldname, fieldfmt, - (!Upolyd) ? FALSE : TRUE); - break; - case BL_TIME: - fieldfmt = " T:%s"; - fieldname = "time"; - status_enablefield(fld, fieldname, fieldfmt, - (!flags.time) ? FALSE : TRUE); - break; - case BL_HUNGER: - fieldfmt = " %s"; - fieldname = "hunger"; - status_enablefield(fld, fieldname, fieldfmt, TRUE); - break; - case BL_HP: - fieldfmt = " HP:%s"; - fieldname = "hitpoints"; - status_enablefield(fld, fieldname, fieldfmt, TRUE); - break; - case BL_HPMAX: - fieldfmt = "(%s)"; - fieldname = "hitpoint-max"; - status_enablefield(fld, fieldname, fieldfmt, TRUE); - break; - case BL_LEVELDESC: - fieldfmt = "%s"; - fieldname = "dungeon-level"; - status_enablefield(fld, fieldname, fieldfmt, TRUE); - break; - case BL_EXP: - fieldfmt = "/%s"; - fieldname = "experience"; - status_enablefield(fld, fieldname, fieldfmt, - (!flags.showexp || Upolyd) ? FALSE : TRUE); - break; - case BL_CONDITION: - fieldfmt = "%s"; - fieldname = "condition"; - status_enablefield(fld, fieldname, fieldfmt, TRUE); - break; - case BL_FLUSH: - default: - break; - } + fieldname = initblstats[i].fldname; + fieldfmt = initblstats[i].fldfmt; + status_enablefield(fld, fieldname, fieldfmt, fldenabled); } update_all = TRUE; } @@ -831,9 +872,22 @@ status_finish() /* free memory that we alloc'd now */ for (i = 0; i < MAXBLSTATS; ++i) { if (blstats[0][i].val) - free((genericptr_t) blstats[0][i].val); + free((genericptr_t) blstats[0][i].val), blstats[0][i].val = 0; if (blstats[1][i].val) - free((genericptr_t) blstats[1][i].val); + free((genericptr_t) blstats[1][i].val), blstats[1][i].val = 0; +#ifdef STATUS_HILITES + if (blstats[0][i].thresholds) { + struct hilite_s *temp = blstats[0][i].thresholds, + *next = (struct hilite_s *)0; + while (temp) { + next = temp->next; + free(temp); + blstats[0][i].thresholds = (struct hilite_s *)0; + blstats[1][i].thresholds = blstats[0][i].thresholds; + temp = next; + } + } +#endif /* STATUS_HILITES */ } } @@ -851,6 +905,9 @@ init_blstats() initalready = TRUE; for (i = BEFORE; i <= NOW; ++i) { for (j = 0; j < MAXBLSTATS; ++j) { +#ifdef STATUS_HILITES + struct hilite_s *keep_hilite_chain = blstats[i][j].thresholds; +#endif blstats[i][j] = initblstats[j]; blstats[i][j].a = zeroany; if (blstats[i][j].valwidth) { @@ -858,108 +915,32 @@ init_blstats() blstats[i][j].val[0] = '\0'; } else blstats[i][j].val = (char *) 0; +#ifdef STATUS_HILITES + if (keep_hilite_chain) + blstats[i][j].thresholds = keep_hilite_chain; +#endif } } } -STATIC_OVL char * -anything_to_s(buf, a, anytype) -char *buf; -anything *a; -int anytype; -{ - if (!buf) - return (char *) 0; - - switch (anytype) { - case ANY_ULONG: - Sprintf(buf, "%lu", a->a_ulong); - break; - case ANY_MASK32: - Sprintf(buf, "%lx", a->a_ulong); - break; - case ANY_LONG: - Sprintf(buf, "%ld", a->a_long); - break; - case ANY_INT: - Sprintf(buf, "%d", a->a_int); - break; - case ANY_UINT: - Sprintf(buf, "%u", a->a_uint); - break; - case ANY_IPTR: - Sprintf(buf, "%d", *a->a_iptr); - break; - case ANY_LPTR: - Sprintf(buf, "%ld", *a->a_lptr); - break; - case ANY_ULPTR: - Sprintf(buf, "%lu", *a->a_ulptr); - break; - case ANY_UPTR: - Sprintf(buf, "%u", *a->a_uptr); - break; - case ANY_STR: /* do nothing */ - ; - break; - default: - buf[0] = '\0'; - } - return buf; -} - -#ifdef STATUS_HILITES - -STATIC_OVL void -s_to_anything(a, buf, anytype) -anything *a; -char *buf; -int anytype; -{ - if (!buf || !a) - return; - - switch (anytype) { - case ANY_LONG: - a->a_long = atol(buf); - break; - case ANY_INT: - a->a_int = atoi(buf); - break; - case ANY_UINT: - a->a_uint = (unsigned) atoi(buf); - break; - case ANY_ULONG: - a->a_ulong = (unsigned long) atol(buf); - break; - case ANY_IPTR: - if (a->a_iptr) - *a->a_iptr = atoi(buf); - break; - case ANY_UPTR: - if (a->a_uptr) - *a->a_uptr = (unsigned) atoi(buf); - break; - case ANY_LPTR: - if (a->a_lptr) - *a->a_lptr = atol(buf); - break; - case ANY_ULPTR: - if (a->a_ulptr) - *a->a_ulptr = (unsigned long) atol(buf); - break; - case ANY_MASK32: - a->a_ulong = (unsigned long) atol(buf); - break; - default: - a->a_void = 0; - break; - } - return; -} - -#endif - +/* + * This compares the previous stat with the current stat, + * and returns one of the following results based on that: + * + * if prev_value < new_value (stat went up, increased) + * return 1 + * + * if prev_value > new_value (stat went down, decreased) + * return -1 + * + * if prev_value == new_value (stat stayed the same) + * return 0 + * + * Special cases: + * - for bitmasks, 0 = stayed the same, 1 = changed + * - for strings, 0 = stayed the same, 1 = changed + * + */ STATIC_OVL int compare_blstats(bl1, bl2) struct istat_s *bl1, *bl2; @@ -1022,16 +1003,10 @@ struct istat_s *bl1, *bl2; : (*bl1->a.a_ulptr > *bl2->a.a_ulptr) ? -1 : 0; break; case ANY_STR: - if (strcmp(bl1->val, bl2->val) == 0) - result = 0; - else - result = 1; + result = sgn(strcmp(bl1->val, bl2->val)); break; case ANY_MASK32: - if (bl1->a.a_ulong == bl2->a.a_ulong) - result = 0; - else - result = 1; + result = (bl1->a.a_ulong != bl2->a.a_ulong); break; default: result = 1; @@ -1039,12 +1014,110 @@ struct istat_s *bl1, *bl2; return result; } +STATIC_OVL char * +anything_to_s(buf, a, anytype) +char *buf; +anything *a; +int anytype; +{ + if (!buf) + return (char *) 0; + + switch (anytype) { + case ANY_ULONG: + Sprintf(buf, "%lu", a->a_ulong); + break; + case ANY_MASK32: + Sprintf(buf, "%lx", a->a_ulong); + break; + case ANY_LONG: + Sprintf(buf, "%ld", a->a_long); + break; + case ANY_INT: + Sprintf(buf, "%d", a->a_int); + break; + case ANY_UINT: + Sprintf(buf, "%u", a->a_uint); + break; + case ANY_IPTR: + Sprintf(buf, "%d", *a->a_iptr); + break; + case ANY_LPTR: + Sprintf(buf, "%ld", *a->a_lptr); + break; + case ANY_ULPTR: + Sprintf(buf, "%lu", *a->a_ulptr); + break; + case ANY_UPTR: + Sprintf(buf, "%u", *a->a_uptr); + break; + case ANY_STR: /* do nothing */ + ; + break; + default: + buf[0] = '\0'; + } + return buf; +} + +STATIC_OVL void +s_to_anything(a, buf, anytype) +anything *a; +char *buf; +int anytype; +{ + if (!buf || !a) + return; + + switch (anytype) { + case ANY_LONG: + a->a_long = atol(buf); + break; + case ANY_INT: + a->a_int = atoi(buf); + break; + case ANY_UINT: + a->a_uint = (unsigned) atoi(buf); + break; + case ANY_ULONG: + a->a_ulong = (unsigned long) atol(buf); + break; + case ANY_IPTR: + if (a->a_iptr) + *a->a_iptr = atoi(buf); + break; + case ANY_UPTR: + if (a->a_uptr) + *a->a_uptr = (unsigned) atoi(buf); + break; + case ANY_LPTR: + if (a->a_lptr) + *a->a_lptr = atol(buf); + break; + case ANY_ULPTR: + if (a->a_ulptr) + *a->a_ulptr = (unsigned long) atol(buf); + break; + case ANY_MASK32: + a->a_ulong = (unsigned long) atol(buf); + break; + default: + a->a_void = 0; + break; + } + return; +} + STATIC_OVL int percentage(bl, maxbl) struct istat_s *bl, *maxbl; { int result = 0; int anytype; + int ival; + long lval; + unsigned uval; + unsigned long ulval; if (!bl || !maxbl) { impossible("percentage: bad istat pointer %s, %s", @@ -1052,35 +1125,51 @@ struct istat_s *bl, *maxbl; return 0; } + ival = 0, lval = 0L, uval = 0U, ulval = 0UL; anytype = bl->anytype; if (maxbl->a.a_void) { switch (anytype) { case ANY_INT: - result = ((100 * bl->a.a_int) / maxbl->a.a_int); + ival = bl->a.a_int; + result = ((100 * ival) / maxbl->a.a_int); break; case ANY_LONG: - result = (int) ((100L * bl->a.a_long) / maxbl->a.a_long); + lval = bl->a.a_long; + result = (int) ((100L * lval) / maxbl->a.a_long); break; case ANY_UINT: - result = (int) ((100U * bl->a.a_uint) / maxbl->a.a_uint); + uval = bl->a.a_uint; + result = (int) ((100U * uval) / maxbl->a.a_uint); break; case ANY_ULONG: - result = (int) ((100UL * bl->a.a_ulong) / maxbl->a.a_ulong); + ulval = bl->a.a_ulong; + result = (int) ((100UL * ulval) / maxbl->a.a_ulong); break; case ANY_IPTR: - result = ((100 * (*bl->a.a_iptr)) / (*maxbl->a.a_iptr)); + ival = *bl->a.a_iptr; + result = ((100 * ival) / (*maxbl->a.a_iptr)); break; case ANY_LPTR: - result = (int) ((100L * (*bl->a.a_lptr)) / (*maxbl->a.a_lptr)); + lval = *bl->a.a_lptr; + result = (int) ((100L * lval) / (*maxbl->a.a_lptr)); break; case ANY_UPTR: - result = (int) ((100U * (*bl->a.a_uptr)) / (*maxbl->a.a_uptr)); + uval = *bl->a.a_uptr; + result = (int) ((100U * uval) / (*maxbl->a.a_uptr)); break; case ANY_ULPTR: - result = (int) ((100UL * (*bl->a.a_ulptr)) / (*maxbl->a.a_ulptr)); + ulval = *bl->a.a_ulptr; + result = (int) ((100UL * ulval) / (*maxbl->a.a_ulptr)); break; } } + /* don't let truncation from integer division produce a zero result + from a non-zero input; note: if we ever change to something like + ((((1000 * val) / max) + 5) / 10) for a rounded result, we'll + also need to check for and convert false 100 to 99 */ + if (result == 0 && (ival != 0 || lval != 0L || uval != 0U || ulval != 0UL)) + result = 1; + return result; } @@ -1091,78 +1180,309 @@ struct istat_s *bl, *maxbl; /* Core status hiliting support */ /****************************************************************************/ +struct hilite_s status_hilites[MAXBLSTATS]; + static struct fieldid_t { const char *fieldname; enum statusfields fldid; -} fieldids[] = { - {"title", BL_TITLE}, - {"strength", BL_STR}, - {"dexterity", BL_DX}, - {"constitution", BL_CO}, - {"intelligence", BL_IN}, - {"wisdom", BL_WI}, - {"charisma", BL_CH}, - {"alignment", BL_ALIGN}, - {"score", BL_SCORE}, - {"carrying-capacity", BL_CAP}, - {"gold", BL_GOLD}, - {"power", BL_ENE}, - {"power-max", BL_ENEMAX}, - {"experience-level", BL_XP}, - {"armor-class", BL_AC}, - {"HD", BL_HD}, - {"time", BL_TIME}, - {"hunger", BL_HUNGER}, - {"hitpoints", BL_HP}, - {"hitpoints-max", BL_HPMAX}, - {"dungeon-level", BL_LEVELDESC}, - {"experience", BL_EXP}, - {"condition", BL_CONDITION}, +} fieldids_alias[] = { + {"characteristics", BL_CHARACTERISTICS}, + {"dx", BL_DX}, + {"co", BL_CO}, + {"con", BL_CO}, + {"points", BL_SCORE}, + {"cap", BL_CAP}, + {"pw", BL_ENE}, + {"pw-max", BL_ENEMAX}, + {"xl", BL_XP}, + {"xplvl", BL_XP}, + {"ac", BL_AC}, + {"hit-dice", BL_HD}, + {"turns", BL_TIME}, + {"hp", BL_HP}, + {"hp-max", BL_HPMAX}, + {"dgn", BL_LEVELDESC}, + {"xp", BL_EXP}, + {"exp", BL_EXP}, + {"flags", BL_CONDITION}, + {0, BL_FLUSH} }; -struct hilite_s { - boolean set; - unsigned anytype; - anything threshold; - int behavior; - int coloridx[2]; -}; +/* field name to bottom line index */ +STATIC_OVL enum statusfields +fldname_to_bl_indx(name) +const char *name; +{ + int i, nmatches = 0, fld = 0; + + if (name && *name) { + /* check matches to canonical names */ + for (i = 0; i < SIZE(initblstats); i++) + if (fuzzymatch(initblstats[i].fldname, name, " -_", TRUE)) { + fld = initblstats[i].fld; + nmatches++; + } + + if (!nmatches) { + /* check aliases */ + for (i = 0; fieldids_alias[i].fieldname; i++) + if (fuzzymatch(fieldids_alias[i].fieldname, name, + " -_", TRUE)) { + fld = fieldids_alias[i].fldid; + nmatches++; + } + } + + if (!nmatches) { + /* check partial matches to canonical names */ + int len = (int) strlen(name); + for (i = 0; i < SIZE(initblstats); i++) + if (!strncmpi(name, initblstats[i].fldname, len)) { + fld = initblstats[i].fld; + nmatches++; + } + } + + } + return (nmatches == 1) ? fld : BL_FLUSH; +} + +STATIC_OVL boolean +hilite_reset_needed(bl_p, augmented_time) +struct istat_s *bl_p; +long augmented_time; +{ + struct hilite_s *tl = bl_p->thresholds; + + /* + * This 'multi' handling may need some tuning... + */ + if (multi) + return FALSE; + + if (bl_p->time == 0 || bl_p->time >= augmented_time) + return FALSE; + + while (tl) { + /* only this style times out */ + if (tl->behavior == BL_TH_UPDOWN) + return TRUE; + tl = tl->next; + } + + return FALSE; +} + +/* called by options handling when 'statushilites' boolean is toggled */ +void +reset_status_hilites() +{ + if (iflags.hilite_delta) { + int i; + + for (i = 0; i < MAXBLSTATS; ++i) + blstats[0][i].time = blstats[1][i].time = 0L; + update_all = TRUE; + } + context.botlx = TRUE; +} + +STATIC_OVL void +merge_bestcolor(bestcolor, newcolor) +int *bestcolor; +int newcolor; +{ + int batr, bclr, natr, nclr; + + split_clridx(*bestcolor, &bclr, &batr); + 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 + * + * Figures out, based on the value and the + * direction it is moving, the color that the field + * should be displayed in. + * + * + * Provide get_hilite_color() 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 + * useful for BL_TH_UPDOWN or change detection + * percentage (current value percentage of max value) pc + * 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 + */ + +STATIC_OVL void +get_hilite_color(idx, fldidx, vp, chg, pc, colorptr) +int idx, fldidx, chg, pc; +genericptr_t vp; +int *colorptr; +{ + int bestcolor = NO_COLOR; + struct hilite_s *hl; + anything *value = (anything *)vp; + char *txtstr, *cmpstr; + + if (!colorptr || fldidx < 0 || fldidx >= MAXBLSTATS) + return; + + if (blstats[idx][fldidx].thresholds) { + /* there are hilites set here */ + int max_pc = 0, min_pc = 100; + int max_val = 0, min_val = LARGEST_INT; + boolean changed = FALSE; + boolean exactmatch = FALSE; + + hl = blstats[idx][fldidx].thresholds; + + while (hl) { + switch (hl->behavior) { + case BL_TH_VAL_PERCENTAGE: + if (hl->rel == EQ_VALUE && pc == hl->value.a_int) { + merge_bestcolor(&bestcolor, hl->coloridx); + min_pc = max_pc = hl->value.a_int; + exactmatch = TRUE; + } else if (hl->rel == LT_VALUE && !exactmatch + && (hl->value.a_int >= pc) + && (hl->value.a_int <= min_pc)) { + merge_bestcolor(&bestcolor, hl->coloridx); + min_pc = hl->value.a_int; + } else if (hl->rel == GT_VALUE && !exactmatch + && (hl->value.a_int <= pc) + && (hl->value.a_int >= max_pc)) { + merge_bestcolor(&bestcolor, hl->coloridx); + max_pc = hl->value.a_int; + } + break; + case BL_TH_UPDOWN: + if (chg < 0 && hl->rel == LT_VALUE) { + merge_bestcolor(&bestcolor, hl->coloridx); + changed = TRUE; + } else if (chg > 0 && hl->rel == GT_VALUE) { + merge_bestcolor(&bestcolor, hl->coloridx); + changed = TRUE; + } else if (hl->rel == EQ_VALUE && chg) { + merge_bestcolor(&bestcolor, hl->coloridx); + min_val = max_val = hl->value.a_int; + changed = TRUE; + } + break; + case BL_TH_VAL_ABSOLUTE: + if (hl->rel == EQ_VALUE && hl->value.a_int == value->a_int) { + merge_bestcolor(&bestcolor, hl->coloridx); + min_val = max_val = hl->value.a_int; + exactmatch = TRUE; + } else if (hl->rel == LT_VALUE && !exactmatch + && (hl->value.a_int >= value->a_int) + && (hl->value.a_int < min_val)) { + merge_bestcolor(&bestcolor, hl->coloridx); + min_val = hl->value.a_int; + } else if (hl->rel == GT_VALUE && !exactmatch + && (hl->value.a_int <= value->a_int) + && (hl->value.a_int > max_val)) { + merge_bestcolor(&bestcolor, hl->coloridx); + max_val = hl->value.a_int; + } + break; + case BL_TH_TEXTMATCH: + txtstr = dupstr(blstats[idx][fldidx].val); + cmpstr = txtstr; + if (fldidx == BL_TITLE) { + int len = (strlen(plname) + sizeof(" the")); + cmpstr += len; + } + (void) trimspaces(cmpstr); + if (hl->rel == TXT_VALUE && hl->textmatch[0] && + !strcmpi(hl->textmatch, cmpstr)) { + merge_bestcolor(&bestcolor, hl->coloridx); + } + free(txtstr); + break; + case BL_TH_ALWAYS_HILITE: + merge_bestcolor(&bestcolor, hl->coloridx); + break; + case BL_TH_NONE: + break; + default: + break; + } + hl = hl->next; + } + } + *colorptr = bestcolor; + return; +} + +STATIC_OVL void +split_clridx(idx, coloridx, attrib) +int idx; +int *coloridx, *attrib; +{ + if (idx && coloridx && attrib) { + *coloridx = idx & 0x00FF; + *attrib = (idx & 0xFF00) >> 8; + } +} -struct hilite_s status_hilites[MAXBLSTATS]; /* * This is the parser for the hilite options - * Example: - * OPTION=hilite_status: hitpoints/10%/red/normal * - * set_hilite_status() separates each hilite entry into its 4 component - * strings, then calls assign_hilite() to make the adjustments. + * parse_status_hl1() separates each hilite entry into + * a set of field threshold/action component strings, + * then calls parse_status_hl2() to parse further + * and configure the hilite. */ boolean -set_status_hilites(op, from_configfile) +parse_status_hl1(op, from_configfile) char *op; boolean from_configfile; { - char hsbuf[4][QBUFSZ]; +#define MAX_THRESH 21 + char hsbuf[MAX_THRESH][QBUFSZ]; boolean rslt, badopt = FALSE; - int fldnum, num = 0, ccount = 0; + int i, fldnum, ccount = 0; char c; - num = fldnum = 0; - hsbuf[0][0] = hsbuf[1][0] = hsbuf[2][0] = hsbuf[3][0] = '\0'; - while (*op && fldnum < 4 && ccount < (QBUFSZ - 2)) { + fldnum = 0; + for (i = 0; i < MAX_THRESH; ++i) { + hsbuf[i][0] = '\0'; + } + while (*op && fldnum < MAX_THRESH && ccount < (QBUFSZ - 2)) { c = lowc(*op); if (c == ' ') { - if (fldnum >= 2) { - rslt = assign_hilite(&hsbuf[0][0], &hsbuf[1][0], &hsbuf[2][0], - &hsbuf[3][0], from_configfile); + if (fldnum >= 1) { + rslt = parse_status_hl2(hsbuf, from_configfile); if (!rslt) { badopt = TRUE; break; } } - hsbuf[0][0] = hsbuf[1][0] = '\0'; - hsbuf[2][0] = hsbuf[3][0] = '\0'; + for (i = 0; i < MAX_THRESH; ++i) { + hsbuf[i][0] = '\0'; + } fldnum = 0; ccount = 0; } else if (c == '/') { @@ -1174,9 +1494,8 @@ boolean from_configfile; } op++; } - if (fldnum >= 2 && !badopt) { - rslt = assign_hilite(&hsbuf[0][0], &hsbuf[1][0], &hsbuf[2][0], - &hsbuf[3][0], from_configfile); + if (fldnum >= 1 && !badopt) { + rslt = parse_status_hl2(hsbuf, from_configfile); if (!rslt) badopt = TRUE; } @@ -1185,453 +1504,1737 @@ boolean from_configfile; return TRUE; } -void -clear_status_hilites(from_configfile) -boolean from_configfile; +/* is str in the format of "(<>)?[0-9]+%?" regex */ +STATIC_OVL boolean +is_ltgt_percentnumber(str) +const char *str; { - int i; - anything it; + const char *s = str; - it = zeroany; - for (i = 0; i < MAXBLSTATS; ++i) { - (void) memset((genericptr_t) &status_hilites[i], 0, - sizeof(struct hilite_s)); - /* notify window port */ - if (!from_configfile) - status_threshold(i, blstats[0][i].anytype, it, 0, 0, 0); - } + if (*s == '<' || *s == '>') s++; + while (digit(*s)) s++; + if (*s == '%') s++; + + return (*s == '\0'); } +/* does str only contain "<>0-9%" chars */ STATIC_OVL boolean -assign_hilite(sa, sb, sc, sd, from_configfile) -char *sa, *sb, *sc, *sd; +has_ltgt_percentnumber(str) +const char *str; +{ + const char *s = str; + + while (*s) { + if (!index("<>0123456789%", *s)) + return FALSE; + s++; + } + return TRUE; +} + +/* splitsubfields(): splits str in place into '+' or '&' separated strings. + * returns number of strings, or -1 if more than maxsf or MAX_SUBFIELDS + */ +#define MAX_SUBFIELDS 16 +STATIC_OVL int +splitsubfields(str, sfarr, maxsf) +char *str; +char ***sfarr; +int maxsf; +{ + static char *subfields[MAX_SUBFIELDS]; + char *st = (char *) 0; + int sf = 0; + + if (!str) + return 0; + for (sf = 0; sf < MAX_SUBFIELDS; ++sf) + subfields[sf] = (char *) 0; + + maxsf = (maxsf == 0) ? MAX_SUBFIELDS : min(maxsf, MAX_SUBFIELDS); + + if (index(str, '+') || index(str, '&')) { + char *c = str; + + sf = 0; + st = c; + while (*c && sf < maxsf) { + if (*c == '&' || *c == '+') { + *c = '\0'; + subfields[sf] = st; + st = c+1; + sf++; + } + c++; + } + if (sf >= maxsf - 1) + return -1; + if (!*c && c != st) + subfields[sf++] = st; + } else { + sf = 1; + subfields[0] = str; + } + *sfarr = subfields; + return sf; +} +#undef MAX_SUBFIELDS + +boolean +is_fld_arrayvalues(str, arr, arrmin, arrmax, retidx) +const char *str; +const char *const *arr; +int arrmin, arrmax; +int *retidx; +{ + int i; + + for (i = arrmin; i < arrmax; i++) + if (!strcmpi(str, arr[i])) { + *retidx = i; + return TRUE; + } + return FALSE; +} + +int +query_arrayvalue(querystr, arr, arrmin, arrmax) +const char *querystr; +const char *const *arr; +int arrmin, arrmax; +{ + int i, res, ret = arrmin - 1; + winid tmpwin; + anything any; + menu_item *picks = (menu_item *) 0; + int adj = (arrmin > 0) ? 1 : arrmax; + + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin); + + for (i = arrmin; i < arrmax; i++) { + any = zeroany; + any.a_int = i + adj; + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + arr[i], MENU_UNSELECTED); + } + + end_menu(tmpwin, querystr); + + res = select_menu(tmpwin, PICK_ONE, &picks); + destroy_nhwindow(tmpwin); + if (res > 0) { + ret = picks->item.a_int - adj; + free((genericptr_t) picks); + } + + return ret; +} + +void +status_hilite_add_threshold(fld, hilite) +int fld; +struct hilite_s *hilite; +{ + struct hilite_s *new_hilite; + + if (!hilite) + return; + + /* alloc and initialize a new hilite_s struct */ + new_hilite = (struct hilite_s *) alloc(sizeof(struct hilite_s)); + *new_hilite = *hilite; /* copy struct */ + + new_hilite->set = TRUE; + new_hilite->fld = fld; + new_hilite->next = (struct hilite_s *)0; + + /* Does that status field currently have any hilite thresholds? */ + if (!blstats[0][fld].thresholds) { + blstats[0][fld].thresholds = new_hilite; + } else { + struct hilite_s *temp_hilite = blstats[0][fld].thresholds; + new_hilite->next = temp_hilite; + blstats[0][fld].thresholds = new_hilite; + /* sort_hilites(fld) */ + } + /* current and prev must both point at the same hilites */ + blstats[1][fld].thresholds = blstats[0][fld].thresholds; +} + + +STATIC_OVL boolean +parse_status_hl2(s, from_configfile) +char (*s)[QBUFSZ]; boolean from_configfile; { char *tmp, *how; - int i = -1, dt = -1, idx = -1; - int coloridx[2] = { -1, -1 }; - boolean inverse[2] = { FALSE, FALSE }; - boolean bold[2] = { FALSE, FALSE }; - boolean normal[2] = { 0, 0 }; - boolean percent = FALSE, down_up = FALSE, changed = FALSE; - anything threshold; + int sidx = 0, i = -1, dt = -1; + int coloridx = -1, successes = 0; + int disp_attrib = 0; + boolean percent = FALSE, changed = FALSE, numeric = FALSE; + boolean down= FALSE, up = FALSE; + boolean gt = FALSE, lt = FALSE, eq = FALSE, neq = FALSE; + boolean txtval = FALSE; + boolean always = FALSE; + const char *txt; enum statusfields fld = BL_FLUSH; - threshold.a_void = 0; + struct hilite_s hilite; + char tmpbuf[BUFSZ]; + const char *aligntxt[] = {"chaotic", "neutral", "lawful"}; + /* hu_stat[] from eat.c has trailing spaces which foul up comparisons */ + const char *hutxt[] = {"Satiated", "", "Hungry", "Weak", + "Fainting", "Fainted", "Starved"}; - /* Example: - * hilite_status: hitpoints/10%/red/normal - */ + /* Examples: + 3.6.1: + OPTION=hilite_status: hitpoints/<10%/red + OPTION=hilite_status: hitpoints/<10%/red/<5%/purple/1/red+blink+inverse + OPTION=hilite_status: experience/down/red/up/green + OPTION=hilite_status: cap/strained/yellow/overtaxed/orange + OPTION=hilite_status: title/always/blue + OPTION=hilite_status: title/blue + */ /* field name to statusfield */ - for (i = 0; sa && i < SIZE(fieldids); ++i) { - if (strcmpi(sa, fieldids[i].fieldname) == 0) { - idx = i; - fld = fieldids[i].fldid; - break; - } - } - if (idx == -1) - return FALSE; - status_hilites[idx].set = FALSE; /* mark it "unset" */ + fld = fldname_to_bl_indx(s[sidx]); - /* threshold */ - if (!sb) - return FALSE; - if ((strcmpi(sb, "updown") == 0) || (strcmpi(sb, "downup") == 0) - || (strcmpi(sb, "up") == 0) || (strcmpi(sb, "down") == 0)) { - down_up = TRUE; - } else if ((strcmpi(sb, "changed") == 0) - && (fld == BL_TITLE || fld == BL_ALIGN || fld == BL_LEVELDESC - || fld == BL_CONDITION)) { - changed = TRUE; /* changed is only thing allowed */ - } else { - tmp = sb; - while (*tmp) { - if (*tmp == '%') { - *tmp = '\0'; - percent = TRUE; - break; - } else if (!index("0123456789", *tmp)) + if (fld == BL_CHARACTERISTICS) { + boolean res = FALSE; + + /* recursively set each of strength, dexterity, constitution, &c */ + for (fld = BL_STR; fld <= BL_CH; fld++) { + Strcpy(s[sidx], initblstats[fld].fldname); + res = parse_status_hl2(s, from_configfile); + if (!res) return FALSE; - tmp++; } - if (strlen(sb) > 0) { - dt = blstats[0][idx].anytype; - if (percent) - dt = ANY_INT; - (void) s_to_anything(&threshold, sb, dt); - } else - return FALSE; - if (percent && (threshold.a_int < 1 || threshold.a_int > 100)) - return FALSE; - if (!threshold.a_void && (strcmp(sb, "0") != 0)) - return FALSE; + return TRUE; } + if (fld == BL_FLUSH) { + config_error_add("Unknown status field '%s'", s[sidx]); + return FALSE; + } + if (fld == BL_CONDITION) + return parse_condition(s, sidx); - /* actions */ - for (i = 0; i < 2; ++i) { - how = !i ? sc : sd; - if (!how) { - if (!i) + ++sidx; + while(s[sidx]) { + char buf[BUFSZ], **subfields; + int sf = 0; /* subfield count */ + int kidx; + + txt = (const char *)0; + percent = changed = numeric = FALSE; + down = up = FALSE; + gt = eq = lt = neq = txtval = FALSE; + always = FALSE; + + /* threshold value */ + if (!s[sidx][0]) + return TRUE; + + memset((genericptr_t) &hilite, 0, sizeof(struct hilite_s)); + hilite.set = FALSE; /* mark it "unset" */ + hilite.fld = fld; + + if (*s[sidx+1] == '\0' || !strcmpi(s[sidx], "always")) { + /* "field/always/color" OR "field/color" */ + always = TRUE; + if (*s[sidx+1] == '\0') + sidx--; + goto do_rel; + } else if (!strcmpi(s[sidx], "up") || !strcmpi(s[sidx], "down")) { + if (!strcmpi(s[sidx], "down")) + down = TRUE; + else + up = TRUE; + changed = TRUE; + goto do_rel; + } else if (fld == BL_CAP + && is_fld_arrayvalues(s[sidx], enc_stat, + SLT_ENCUMBER, OVERLOADED+1, &kidx)) { + txt = enc_stat[kidx]; + txtval = TRUE; + goto do_rel; + } else if (fld == BL_ALIGN + && is_fld_arrayvalues(s[sidx], aligntxt, 0, 3, &kidx)) { + txt = aligntxt[kidx]; + txtval = TRUE; + goto do_rel; + } else if (fld == BL_HUNGER + && is_fld_arrayvalues(s[sidx], hutxt, + SATIATED, STARVED+1, &kidx)) { + txt = hu_stat[kidx]; /* store hu_stat[] val, not hutxt[] */ + txtval = TRUE; + goto do_rel; + } else if (!strcmpi(s[sidx], "changed")) { + changed = TRUE; + goto do_rel; + } else if (is_ltgt_percentnumber(s[sidx])) { + tmp = s[sidx]; + if (strchr(tmp, '%')) + percent = TRUE; + if (strchr(tmp, '<')) + lt = TRUE; + if (strchr(tmp, '>')) + gt = TRUE; + (void) stripchars(tmpbuf, "%<>", tmp); + tmp = tmpbuf; + while (*tmp) { + if (!index("0123456789", *tmp)) + return FALSE; + tmp++; + } + numeric = TRUE; + tmp = tmpbuf; + if (strlen(tmp) > 0) { + dt = blstats[0][fld].anytype; + if (percent) + dt = ANY_INT; + (void) s_to_anything(&hilite.value, tmp, dt); + } else return FALSE; - break; /* sc is mandatory; sd is not */ - } - - if (strcmpi(how, "bold") == 0) { - bold[i] = TRUE; - } else if (strcmpi(how, "inverse") == 0) { - inverse[i] = TRUE; - } else if (strcmpi(how, "normal") == 0) { - normal[i] = TRUE; + if (!hilite.value.a_void && (strcmp(tmp, "0") != 0)) + return FALSE; + } else if (initblstats[fld].anytype == ANY_STR) { + txt = s[sidx]; + txtval = TRUE; + goto do_rel; } else { - int k = match_str2clr(how); - - if (k >= CLR_MAX) - return FALSE; - coloridx[i] = k; + config_error_add(has_ltgt_percentnumber(s[sidx]) + ? "Wrong format '%s', expected a threshold number or percent" + : "Unknown behavior '%s'", s[sidx]); + return FALSE; } - } - - /* Assign the values */ - - for (i = 0; i < 2; ++i) { - if (inverse[i]) - status_hilites[idx].coloridx[i] = BL_HILITE_INVERSE; - else if (bold[i]) - status_hilites[idx].coloridx[i] = BL_HILITE_BOLD; - else if (coloridx[i]) - status_hilites[idx].coloridx[i] = coloridx[i]; +do_rel: + /* relationships { LT_VALUE, GT_VALUE, EQ_VALUE} */ + if (gt) + hilite.rel = GT_VALUE; + else if (eq) + hilite.rel = EQ_VALUE; + else if (lt) + hilite.rel = LT_VALUE; + else if (percent) + hilite.rel = EQ_VALUE; + else if (numeric) + hilite.rel = EQ_VALUE; + else if (down) + hilite.rel = LT_VALUE; + else if (up) + hilite.rel = GT_VALUE; + else if (changed) + hilite.rel = EQ_VALUE; + else if (txtval) + hilite.rel = TXT_VALUE; else - status_hilites[idx].coloridx[i] = BL_HILITE_NONE; + hilite.rel = LT_VALUE; + + if (initblstats[fld].anytype == ANY_STR + && (percent || numeric)) { + config_error_add("Field '%s' does not support numeric values", + initblstats[fld].fldname); + return FALSE; + } + + if (percent) { + if (initblstats[fld].idxmax <= BL_FLUSH) { + config_error_add("Cannot use percent with '%s'", + initblstats[fld].fldname); + return FALSE; + } else if ((hilite.value.a_int < 0) + || (hilite.value.a_int == 0 + && hilite.rel == LT_VALUE) + || (hilite.value.a_int > 100) + || (hilite.value.a_int == 100 + && hilite.rel == GT_VALUE)) { + config_error_add("Illegal percentage value"); + return FALSE; + } + } + + /* actions */ + sidx++; + how = s[sidx]; + if (!how) { + if (!successes) + return FALSE; + } + coloridx = -1; + Strcpy(buf, how); + sf = splitsubfields(buf, &subfields, 0); + + if (sf < 1) + return FALSE; + + disp_attrib = HL_UNDEF; + + for (i = 0; i < sf; ++i) { + int a = match_str2attr(subfields[i], FALSE); + if (a == ATR_DIM) + disp_attrib |= HL_DIM; + else if (a == ATR_BLINK) + disp_attrib |= HL_BLINK; + else if (a == ATR_ULINE) + disp_attrib |= HL_ULINE; + else if (a == ATR_INVERSE) + disp_attrib |= HL_INVERSE; + else if (a == ATR_BOLD) + disp_attrib |= HL_BOLD; + else if (a == ATR_NONE) + disp_attrib = HL_NONE; + else { + int c = match_str2clr(subfields[i]); + + if (c >= CLR_MAX || coloridx != -1) + return FALSE; + coloridx = c; + } + } + if (coloridx == -1) + coloridx = NO_COLOR; + + /* Assign the values */ + hilite.coloridx = coloridx | (disp_attrib << 8); + + if (always) + hilite.behavior = BL_TH_ALWAYS_HILITE; + else if (percent) + hilite.behavior = BL_TH_VAL_PERCENTAGE; + else if (changed) + hilite.behavior = BL_TH_UPDOWN; + else if (numeric) + hilite.behavior = BL_TH_VAL_ABSOLUTE; + else if (txtval) + hilite.behavior = BL_TH_TEXTMATCH; + else if (hilite.value.a_void) + hilite.behavior = BL_TH_VAL_ABSOLUTE; + else + hilite.behavior = BL_TH_NONE; + + hilite.anytype = dt; + + if (hilite.behavior == BL_TH_TEXTMATCH && txt + && strlen(txt) < QBUFSZ-1) { + Strcpy(hilite.textmatch, txt); + (void) trimspaces(hilite.textmatch); + } + + status_hilite_add_threshold(fld, &hilite); + + successes++; + sidx++; } - if (percent) - status_hilites[idx].behavior = BL_TH_VAL_PERCENTAGE; - else if (down_up) - status_hilites[idx].behavior = BL_TH_UPDOWN; - else if (threshold.a_void) - status_hilites[idx].behavior = BL_TH_VAL_ABSOLUTE; - else - status_hilites[idx].behavior = BL_TH_NONE; - - if (status_hilites[idx].behavior != BL_TH_NONE) { - status_hilites[idx].threshold = threshold; - status_hilites[idx].set = TRUE; - } - status_hilites[idx].anytype = dt; - - /* Now finally, we notify the window port */ - if (!from_configfile) - status_threshold(idx, status_hilites[idx].anytype, - status_hilites[idx].threshold, - status_hilites[idx].behavior, - status_hilites[idx].coloridx[0], - status_hilites[idx].coloridx[1]); - return TRUE; } -/*ARGUSED*/ -void -status_notify_windowport(all) -boolean all UNUSED; + + +const struct condmap valid_conditions[] = { + {"stone", BL_MASK_STONE}, + {"slime", BL_MASK_SLIME}, + {"strngl", BL_MASK_STRNGL}, + {"foodPois", BL_MASK_FOODPOIS}, + {"termIll", BL_MASK_TERMILL}, + {"blind", BL_MASK_BLIND}, + {"deaf", BL_MASK_DEAF}, + {"stun", BL_MASK_STUN}, + {"conf", BL_MASK_CONF}, + {"hallu", BL_MASK_HALLU}, + {"lev", BL_MASK_LEV}, + {"fly", BL_MASK_FLY}, + {"ride", BL_MASK_RIDE}, +}; + +const struct condmap condition_aliases[] = { + {"strangled", BL_MASK_STRNGL}, + {"all", BL_MASK_STONE | BL_MASK_SLIME | BL_MASK_STRNGL | + BL_MASK_FOODPOIS | BL_MASK_TERMILL | + BL_MASK_BLIND | BL_MASK_DEAF | BL_MASK_STUN | + BL_MASK_CONF | BL_MASK_HALLU | + BL_MASK_LEV | BL_MASK_FLY | BL_MASK_RIDE }, + {"major_troubles", BL_MASK_STONE | BL_MASK_SLIME | BL_MASK_STRNGL | + BL_MASK_FOODPOIS | BL_MASK_TERMILL}, + {"minor_troubles", BL_MASK_BLIND | BL_MASK_DEAF | BL_MASK_STUN | + BL_MASK_CONF | BL_MASK_HALLU}, + {"movement", BL_MASK_LEV | BL_MASK_FLY | BL_MASK_RIDE} +}; + +unsigned long +query_conditions() { - int idx; - anything it; + int i,res; + unsigned long ret = 0UL; + winid tmpwin; + anything any; + menu_item *picks = (menu_item *) 0; - it = zeroany; - for (idx = 0; idx < MAXBLSTATS; ++idx) { - if (status_hilites[idx].set) - status_threshold(idx, status_hilites[idx].anytype, - status_hilites[idx].threshold, - status_hilites[idx].behavior, - status_hilites[idx].coloridx[0], - status_hilites[idx].coloridx[1]); - else - status_threshold(idx, blstats[0][idx].anytype, it, 0, 0, 0); + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin); + for (i = 0; i < SIZE(valid_conditions); i++) { + any = zeroany; + any.a_ulong = valid_conditions[i].bitmask; + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + valid_conditions[i].id, MENU_UNSELECTED); + } + + end_menu(tmpwin, "Choose status conditions"); + + res = select_menu(tmpwin, PICK_ANY, &picks); + destroy_nhwindow(tmpwin); + if (res > 0) { + for (i = 0; i < res; i++) + ret |= picks[i].item.a_ulong; + free((genericptr_t) picks); + } + return ret; +} + +STATIC_OVL char * +conditionbitmask2str(ul) +unsigned long ul; +{ + static char buf[BUFSZ]; + int i; + boolean first = TRUE; + const char *alias = (char *) 0; + + + buf[0] = '\0'; + if (!ul) + return buf; + + for (i = 1; i < SIZE(condition_aliases); i++) + if (condition_aliases[i].bitmask == ul) + alias = condition_aliases[i].id; + + for (i = 0; i < SIZE(valid_conditions); i++) + if ((valid_conditions[i].bitmask & ul) != 0UL) { + Sprintf(eos(buf), "%s%s", (first) ? "" : "+", + valid_conditions[i].id); + first = FALSE; + } + + if (!first && alias) + Sprintf(buf, "%s", alias); + + return buf; +} + +STATIC_OVL unsigned long +match_str2conditionbitmask(str) +const char *str; +{ + int i, nmatches = 0; + unsigned long mask = 0UL; + + if (str && *str) { + /* check matches to canonical names */ + for (i = 0; i < SIZE(valid_conditions); i++) + if (fuzzymatch(valid_conditions[i].id, str, " -_", TRUE)) { + mask |= valid_conditions[i].bitmask; + nmatches++; + } + + if (!nmatches) { + /* check aliases */ + for (i = 0; i < SIZE(condition_aliases); i++) + if (fuzzymatch(condition_aliases[i].id, str, " -_", TRUE)) { + mask |= condition_aliases[i].bitmask; + nmatches++; + } + } + + if (!nmatches) { + /* check partial matches to aliases */ + int len = (int) strlen(str); + for (i = 0; i < SIZE(condition_aliases); i++) + if (!strncmpi(str, condition_aliases[i].id, len)) { + mask |= condition_aliases[i].bitmask; + nmatches++; + } + } + } + + return mask; +} + +STATIC_OVL unsigned long +str2conditionbitmask(str) +char *str; +{ + unsigned long conditions_bitmask = 0UL; + char **subfields; + int i, sf; + + sf = splitsubfields(str, &subfields, SIZE(valid_conditions)); + + if (sf < 1) + return 0UL; + + for (i = 0; i < sf; ++i) { + unsigned long bm = match_str2conditionbitmask(subfields[i]); + + if (!bm) { + config_error_add("Unknown condition '%s'", subfields[i]); + return 0UL; + } + conditions_bitmask |= bm; + } + return conditions_bitmask; +} + +STATIC_OVL boolean +parse_condition(s, sidx) +char (*s)[QBUFSZ]; +int sidx; +{ + int i; + int coloridx = NO_COLOR; + char *tmp, *how; + unsigned long conditions_bitmask = 0UL; + boolean success = FALSE; + + if (!s) + return FALSE; + + /*3.6.1: + OPTION=hilite_status: condition/stone+slime+foodPois/red&inverse */ + + sidx++; + while(s[sidx]) { + int sf = 0; /* subfield count */ + char buf[BUFSZ], **subfields; + + tmp = s[sidx]; + if (!*tmp) { + if (!success) + config_error_add("Missing condition(s)"); + return success; + } + + Strcpy(buf, tmp); + conditions_bitmask = str2conditionbitmask(buf); + + if (!conditions_bitmask) + return FALSE; + + /* + * We have the conditions_bitmask with bits set for + * each ailment we want in a particular color and/or + * attribute, but we need to assign it to an arry of + * bitmasks indexed by the color chosen + * (0 to (CLR_MAX - 1)) + * and/or attributes chosen + * (HL_ATTCLR_DIM to (BL_ATTCLR_MAX - 1)) + * We still have to parse the colors and attributes out. + */ + + /* actions */ + sidx++; + how = s[sidx]; + if (!how || !*how) { + config_error_add("Missing color+attribute"); + return FALSE; + } + + Strcpy(buf, how); + sf = splitsubfields(buf, &subfields, 0); + + /* + * conditions_bitmask now has bits set representing + * the conditions that player wants represented, but + * now we parse out *how* they will be represented. + * + * Only 1 colour is allowed, but potentially multiple + * attributes are allowed. + * + * We have the following additional array offsets to + * use for storing the attributes beyond the end of + * the color indexes, all of which are less than CLR_MAX. + * HL_ATTCLR_DIM = CLR_MAX + * HL_ATTCLR_BLINK = CLR_MAX + 1 + * HL_ATTCLR_ULINE = CLR_MAX + 2 + * HL_ATTCLR_INVERSE = CLR_MAX + 3 + * HL_ATTCLR_BOLD = CLR_MAX + 4 + * HL_ATTCLR_MAX = CLR_MAX + 5 (this is past array boundary) + * + */ + + for (i = 0; i < sf; ++i) { + int a = match_str2attr(subfields[i], FALSE); + if (a == ATR_DIM) + cond_hilites[HL_ATTCLR_DIM] |= conditions_bitmask; + else if (a == ATR_BLINK) + cond_hilites[HL_ATTCLR_BLINK] |= conditions_bitmask; + else if (a == ATR_ULINE) + cond_hilites[HL_ATTCLR_ULINE] |= conditions_bitmask; + else if (a == ATR_INVERSE) + cond_hilites[HL_ATTCLR_INVERSE] |= conditions_bitmask; + else if (a == ATR_BOLD) + cond_hilites[HL_ATTCLR_BOLD] |= conditions_bitmask; + else if (a == ATR_NONE) { + cond_hilites[HL_ATTCLR_DIM] = 0UL; + cond_hilites[HL_ATTCLR_BLINK] = 0UL; + cond_hilites[HL_ATTCLR_ULINE] = 0UL; + cond_hilites[HL_ATTCLR_INVERSE] = 0UL; + cond_hilites[HL_ATTCLR_BOLD] = 0UL; + } else { + int k = match_str2clr(subfields[i]); + + if (k >= CLR_MAX) + return FALSE; + coloridx = k; + } + } + /* set the bits in the appropriate member of the + condition array according to color chosen as index */ + + cond_hilites[coloridx] |= conditions_bitmask; + success = TRUE; + sidx++; + } + return TRUE; +} + +void +clear_status_hilites() +{ + int i; + + for (i = 0; i < MAXBLSTATS; ++i) { + if (blstats[0][i].thresholds) { + struct hilite_s *temp = blstats[0][i].thresholds, + *next = (struct hilite_s *)0; + while (temp) { + next = temp->next; + free(temp); + blstats[0][i].thresholds = (struct hilite_s *)0; + blstats[1][i].thresholds = blstats[0][i].thresholds; + temp = next; + } + } } } -/* - * get_status_hilites - * - * Returns a string containing all the status hilites in the - * same format that is used to specify a status hilite preference - * in the config file. - */ -char * -get_status_hilites(buf, bufsiz) +STATIC_OVL char * +hlattr2attrname(attrib, buf, bufsz) +int attrib, bufsz; char *buf; -int bufsiz; { - int i, j, k, coloridx; - const char *text = (char *) 0; - char tmp[BUFSZ], colorname[BUFSZ]; - boolean val_percentage, val_absolute, up_down; - boolean added_one = FALSE; + if (attrib && buf) { + char attbuf[BUFSZ]; + int k, first = 0; - if (!buf) - return (char *) 0; - *buf = '\0'; + attbuf[0] = '\0'; + if (attrib & HL_NONE) { + Strcpy(buf, "normal"); + return buf; + } - bufsiz--; /* required trailing null */ - for (i = 0; i < MAXBLSTATS; ++i) { - val_percentage = val_absolute = up_down = FALSE; - if (status_hilites[i].set) { - if (!added_one) - added_one = TRUE; - else { - Strcat(buf, " "); - bufsiz--; - } - k = strlen(fieldids[i].fieldname); - if (k < bufsiz) { - Strcat(buf, fieldids[i].fieldname); - bufsiz -= k; - } - if (bufsiz > 1) { - Strcat(buf, "/"); - bufsiz--; - } - if (status_hilites[i].behavior == BL_TH_VAL_PERCENTAGE) { - val_percentage = TRUE; - } else if (status_hilites[i].behavior == BL_TH_VAL_ABSOLUTE) { - val_absolute = TRUE; - } else if (status_hilites[i].behavior == BL_TH_UPDOWN) { - up_down = TRUE; - text = "updown"; - } + if (attrib & HL_BOLD) + Strcat(attbuf, first++ ? "+bold" : "bold"); + if (attrib & HL_INVERSE) + Strcat(attbuf, first++ ? "+inverse" : "inverse"); + if (attrib & HL_ULINE) + Strcat(attbuf, first++ ? "+underline" : "underline"); + if (attrib & HL_BLINK) + Strcat(attbuf, first++ ? "+blink" : "blink"); + if (attrib & HL_DIM) + Strcat(attbuf, first++ ? "+dim" : "dim"); - if (status_hilites[i].behavior != BL_TH_UPDOWN) { - anything_to_s(tmp, &status_hilites[i].threshold, - blstats[0][i].anytype); - text = tmp; - } - k = strlen(text); - if (k < (bufsiz - 1)) { - Strcat(buf, text); - if (val_percentage) - Strcat(buf, "%"), k++; - bufsiz -= k; - } - for (j = 0; j < 2; ++j) { - if (bufsiz > 1) { - Strcat(buf, "/"); - bufsiz--; - } - coloridx = status_hilites[i].coloridx[j]; - if (coloridx < 0) { - if (coloridx == BL_HILITE_BOLD) - text = "bold"; - else if (coloridx == BL_HILITE_INVERSE) - text = "inverse"; - else - text = "normal"; - } else { - char *blank; - - (void) strcpy(colorname, c_obj_colors[coloridx]); - for (blank = index(colorname, ' '); blank; - blank = index(colorname, ' ')) - *blank = '-'; - text = colorname; - } - k = strlen(text); - if (k < bufsiz) { - Strcat(buf, text); - bufsiz -= k; + k = strlen(attbuf); + if (k < (bufsz - 1)) + Strcpy(buf, attbuf); + return buf; + } + return (char *) 0; +} + + +struct _status_hilite_line_str { + int id; + int fld; + struct hilite_s *hl; + unsigned long mask; + char str[BUFSZ]; + struct _status_hilite_line_str *next; +}; + +struct _status_hilite_line_str *status_hilite_str = + (struct _status_hilite_line_str *) 0; +static int status_hilite_str_id = 0; + +STATIC_OVL void +status_hilite_linestr_add(fld, hl, mask, str) +int fld; +struct hilite_s *hl; +unsigned long mask; +const char *str; +{ + struct _status_hilite_line_str *tmp = (struct _status_hilite_line_str *) + alloc(sizeof(struct _status_hilite_line_str)); + struct _status_hilite_line_str *nxt = status_hilite_str; + + (void) memset(tmp, 0, sizeof(struct _status_hilite_line_str)); + + ++status_hilite_str_id; + tmp->fld = fld; + tmp->hl = hl; + tmp->mask = mask; + (void) stripchars(tmp->str, " ", str); + + tmp->id = status_hilite_str_id; + + if (nxt) { + while (nxt && nxt->next) + nxt = nxt->next; + nxt->next = tmp; + } else { + tmp->next = (struct _status_hilite_line_str *) 0; + status_hilite_str = tmp; + } +} + +STATIC_OVL void +status_hilite_linestr_done() +{ + struct _status_hilite_line_str *tmp = status_hilite_str; + struct _status_hilite_line_str *nxt; + + while (tmp) { + nxt = tmp->next; + free(tmp); + tmp = nxt; + } + status_hilite_str = (struct _status_hilite_line_str *) 0; + status_hilite_str_id = 0; +} + +STATIC_OVL int +status_hilite_linestr_countfield(fld) +int fld; +{ + struct _status_hilite_line_str *tmp = status_hilite_str; + int count = 0; + + while (tmp) { + if (tmp->fld == fld || fld == BL_FLUSH) + count++; + tmp = tmp->next; + } + return count; +} + +int +count_status_hilites() +{ + int count; + status_hilite_linestr_gather(); + count = status_hilite_linestr_countfield(BL_FLUSH); + status_hilite_linestr_done(); + return count; +} + +STATIC_OVL void +status_hilite_linestr_gather_conditions() +{ + int i; + struct _cond_map { + unsigned long bm; + unsigned long clratr; + } cond_maps[SIZE(valid_conditions)]; + + (void)memset(cond_maps, 0, + sizeof(struct _cond_map) * SIZE(valid_conditions)); + + for (i = 0; i < SIZE(valid_conditions); i++) { + int clr = NO_COLOR; + int atr = HL_NONE; + int j; + for (j = 0; j < CLR_MAX; j++) + if (cond_hilites[j] & valid_conditions[i].bitmask) + clr = j; + if (cond_hilites[HL_ATTCLR_DIM] & valid_conditions[i].bitmask) + atr |= HL_DIM; + if (cond_hilites[HL_ATTCLR_BOLD] & valid_conditions[i].bitmask) + atr |= HL_BOLD; + if (cond_hilites[HL_ATTCLR_BLINK] & valid_conditions[i].bitmask) + atr |= HL_BLINK; + if (cond_hilites[HL_ATTCLR_ULINE] & valid_conditions[i].bitmask) + atr |= HL_ULINE; + if (cond_hilites[HL_ATTCLR_INVERSE] & valid_conditions[i].bitmask) + atr |= HL_INVERSE; + + if (clr != NO_COLOR || atr != HL_NONE) { + unsigned long ca = clr | (atr << 8); + boolean added_condmap = FALSE; + for (j = 0; j < SIZE(valid_conditions); j++) + if (cond_maps[j].clratr == ca) { + cond_maps[j].bm |= valid_conditions[i].bitmask; + added_condmap = TRUE; + break; } + if (!added_condmap) { + for (j = 0; j < SIZE(valid_conditions); j++) + if (!cond_maps[j].bm) { + cond_maps[j].bm = valid_conditions[i].bitmask; + cond_maps[j].clratr = ca; + break; + } } } } + + for (i = 0; i < SIZE(valid_conditions); i++) + if (cond_maps[i].bm) { + int clr = NO_COLOR, atr = HL_NONE; + split_clridx(cond_maps[i].clratr, &clr, &atr); + if (clr != NO_COLOR || atr != HL_NONE) { + char clrbuf[BUFSZ]; + char attrbuf[BUFSZ]; + char condbuf[BUFSZ]; + char *tmpattr; + (void) stripchars(clrbuf, " ", clr2colorname(clr)); + tmpattr = hlattr2attrname(atr, attrbuf, BUFSZ); + if (tmpattr) + Sprintf(eos(clrbuf), "&%s", tmpattr); + Sprintf(condbuf, "condition/%s/%s", + conditionbitmask2str(cond_maps[i].bm), clrbuf); + status_hilite_linestr_add(BL_CONDITION, 0, + cond_maps[i].bm, condbuf); + } + } +} + +STATIC_OVL void +status_hilite_linestr_gather() +{ + int i; + struct hilite_s *hl; + + status_hilite_linestr_done(); + + for (i = 0; i < MAXBLSTATS; i++) { + hl = blstats[0][i].thresholds; + while (hl) { + status_hilite_linestr_add(i, hl, 0UL, status_hilite2str(hl)); + hl = hl->next; + } + } + + status_hilite_linestr_gather_conditions(); +} + + +char * +status_hilite2str(hl) +struct hilite_s *hl; +{ + static char buf[BUFSZ]; + int clr = 0, attr = 0; + char behavebuf[BUFSZ]; + char clrbuf[BUFSZ]; + char attrbuf[BUFSZ]; + char *tmpattr; + + if (!hl) + return (char *) 0; + + behavebuf[0] = '\0'; + clrbuf[0] = '\0'; + + switch (hl->behavior) { + case BL_TH_VAL_PERCENTAGE: + if (hl->rel == LT_VALUE) + Sprintf(behavebuf, "<%i%%", hl->value.a_int); + else if (hl->rel == GT_VALUE) + Sprintf(behavebuf, ">%i%%", hl->value.a_int); + else if (hl->rel == EQ_VALUE) + Sprintf(behavebuf, "%i%%", hl->value.a_int); + else + impossible("hl->behavior=percentage, rel error"); + break; + case BL_TH_UPDOWN: + if (hl->rel == LT_VALUE) + Sprintf(behavebuf, "down"); + else if (hl->rel == GT_VALUE) + Sprintf(behavebuf, "up"); + else if (hl->rel == EQ_VALUE) + Sprintf(behavebuf, "changed"); + else + impossible("hl->behavior=updown, rel error"); + break; + case BL_TH_VAL_ABSOLUTE: + if (hl->rel == LT_VALUE) + Sprintf(behavebuf, "<%i", hl->value.a_int); + else if (hl->rel == GT_VALUE) + Sprintf(behavebuf, ">%i", hl->value.a_int); + else if (hl->rel == EQ_VALUE) + Sprintf(behavebuf, "%i", hl->value.a_int); + else + impossible("hl->behavior=absolute, rel error"); + break; + case BL_TH_TEXTMATCH: + if (hl->rel == TXT_VALUE && hl->textmatch[0]) + Sprintf(behavebuf, "%s", hl->textmatch); + else + impossible("hl->behavior=textmatch, rel or textmatch error"); + break; + case BL_TH_CONDITION: + if (hl->rel == EQ_VALUE) + Sprintf(behavebuf, "%s", conditionbitmask2str(hl->value.a_ulong)); + else + impossible("hl->behavior=condition, rel error"); + break; + case BL_TH_ALWAYS_HILITE: + Sprintf(behavebuf, "always"); + break; + case BL_TH_NONE: + break; + default: + break; + } + + split_clridx(hl->coloridx, &clr, &attr); + if (clr != NO_COLOR) + (void) stripchars(clrbuf, " ", clr2colorname(clr)); + if (attr != HL_UNDEF) { + tmpattr = hlattr2attrname(attr, attrbuf, BUFSZ); + if (tmpattr) + Sprintf(eos(clrbuf), "%s%s", + (clr != NO_COLOR) ? "&" : "", + tmpattr); + } + Sprintf(buf, "%s/%s/%s", initblstats[hl->fld].fldname, behavebuf, clrbuf); + return buf; } -STATIC_OVL const char * -clridx_to_s(buf, idx) -char *buf; -int idx; +int +status_hilite_menu_choose_field() { - static const char *a[] = { "bold", "inverse", "normal" }; - char* p = 0; + winid tmpwin; + int i, res, fld = BL_FLUSH; + anything any; + menu_item *picks = (menu_item *) 0; - if (buf) { - buf[0] = '\0'; - if (idx < 0 && idx >= BL_HILITE_BOLD) - Strcpy(buf, a[idx + 3]); - else if (idx >= 0 && idx < CLR_MAX) - Strcpy(buf, c_obj_colors[idx]); - /* replace spaces with - */ - for(p = buf; *p; p++) - if(*p == ' ') *p = '-'; + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin); + + for (i = 0; i < MAXBLSTATS; i++) { + any = zeroany; + any.a_int = (i+1); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + initblstats[i].fldname, MENU_UNSELECTED); } - return buf; + + end_menu(tmpwin, "Select a hilite field:"); + + res = select_menu(tmpwin, PICK_ONE, &picks); + destroy_nhwindow(tmpwin); + if (res > 0) { + fld = picks->item.a_int - 1; + free((genericptr_t) picks); + } + return fld; +} + +int +status_hilite_menu_choose_behavior(fld) +int fld; +{ + winid tmpwin; + int res = 0, beh = BL_TH_NONE-1; + anything any; + menu_item *picks = (menu_item *) 0; + char buf[BUFSZ]; + int at; + int onlybeh = BL_TH_NONE, nopts = 0; + + if (fld <= BL_FLUSH || fld >= MAXBLSTATS) + return BL_TH_NONE; + + at = initblstats[fld].anytype; + + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin); + + if (fld != BL_CONDITION) { + any = zeroany; + any.a_int = onlybeh = BL_TH_ALWAYS_HILITE; + Sprintf(buf, "Always highlight %s", initblstats[fld].fldname); + add_menu(tmpwin, NO_GLYPH, &any, 'a', 0, ATR_NONE, + buf, MENU_UNSELECTED); + nopts++; + } + + if (fld == BL_CONDITION) { + any = zeroany; + any.a_int = onlybeh = BL_TH_CONDITION; + add_menu(tmpwin, NO_GLYPH, &any, 'b', 0, ATR_NONE, + "Bitmask of conditions", MENU_UNSELECTED); + nopts++; + } + + if (fld != BL_CONDITION) { + any = zeroany; + any.a_int = onlybeh = BL_TH_UPDOWN; + Sprintf(buf, "%s value changes", initblstats[fld].fldname); + add_menu(tmpwin, NO_GLYPH, &any, 'c', 0, ATR_NONE, + buf, MENU_UNSELECTED); + nopts++; + } + + if (fld != BL_CAP && (at == ANY_INT || at == ANY_LONG || at == ANY_UINT)) { + any = zeroany; + any.a_int = onlybeh = BL_TH_VAL_ABSOLUTE; + add_menu(tmpwin, NO_GLYPH, &any, 'n', 0, ATR_NONE, + "Number threshold", MENU_UNSELECTED); + nopts++; + } + + if (initblstats[fld].idxmax > BL_FLUSH) { + any = zeroany; + any.a_int = onlybeh = BL_TH_VAL_PERCENTAGE; + add_menu(tmpwin, NO_GLYPH, &any, 'p', 0, ATR_NONE, + "Percentage threshold", MENU_UNSELECTED); + nopts++; + } + + if (initblstats[fld].anytype == ANY_STR || fld == BL_CAP) { + any = zeroany; + any.a_int = onlybeh = BL_TH_TEXTMATCH; + Sprintf(buf, "%s text match", initblstats[fld].fldname); + add_menu(tmpwin, NO_GLYPH, &any, 't', 0, ATR_NONE, + buf, MENU_UNSELECTED); + nopts++; + } + + Sprintf(buf, "Select %s field hilite behavior:", initblstats[fld].fldname); + end_menu(tmpwin, buf); + + if (nopts > 1) { + res = select_menu(tmpwin, PICK_ONE, &picks); + if (res == 0) /* none chosen*/ + beh = BL_TH_NONE; + else if (res == -1) /* menu cancelled */ + beh = (BL_TH_NONE - 1); + } else if (onlybeh != BL_TH_NONE) + beh = onlybeh; + destroy_nhwindow(tmpwin); + if (res > 0) { + beh = picks->item.a_int; + free((genericptr_t) picks); + } + return beh; +} + +int +status_hilite_menu_choose_updownboth(fld, str) +int fld; +const char *str; +{ + int res, ret = -2; + winid tmpwin; + char buf[BUFSZ]; + anything any; + menu_item *picks = (menu_item *) 0; + + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin); + + if (str) + Sprintf(buf, "%s or less", str); + else + Sprintf(buf, "Value goes down"); + any = zeroany; + any.a_int = 10 + LT_VALUE; + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + buf, MENU_UNSELECTED); + + if (str) + Sprintf(buf, "Exactly %s", str); + else + Sprintf(buf, "Value changes"); + any = zeroany; + any.a_int = 10 + EQ_VALUE; + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + buf, MENU_UNSELECTED); + + if (str) + Sprintf(buf, "%s or more", str); + else + Sprintf(buf, "Value goes up"); + any = zeroany; + any.a_int = 10 + GT_VALUE; + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + buf, MENU_UNSELECTED); + + Sprintf(buf, "Select field %s value:", initblstats[fld].fldname); + end_menu(tmpwin, buf); + + res = select_menu(tmpwin, PICK_ONE, &picks); + destroy_nhwindow(tmpwin); + if (res > 0) { + ret = picks->item.a_int - 10; + free((genericptr_t) picks); + } + + return ret; +} + +STATIC_OVL boolean +status_hilite_menu_add(origfld) +int origfld; +{ + int fld; + int behavior; + int lt_gt_eq = 0; + int clr = NO_COLOR, atr = HL_UNDEF; + struct hilite_s hilite; + unsigned long cond = 0UL; + char colorqry[BUFSZ]; + char attrqry[BUFSZ]; + +choose_field: + fld = origfld; + if (fld == BL_FLUSH) { + fld = status_hilite_menu_choose_field(); + if (fld == BL_FLUSH) + return FALSE; + } + + if (fld == BL_FLUSH) + return FALSE; + + colorqry[0] = '\0'; + attrqry[0] = '\0'; + + memset((genericptr_t) &hilite, 0, sizeof(struct hilite_s)); + hilite.set = FALSE; /* mark it "unset" */ + hilite.fld = fld; + +choose_behavior: + + behavior = status_hilite_menu_choose_behavior(fld); + + if (behavior == (BL_TH_NONE-1)) { + return FALSE; + } else if (behavior == BL_TH_NONE) { + if (origfld == BL_FLUSH) + goto choose_field; + else + return FALSE; + } + + hilite.behavior = behavior; + +choose_value: + + if (behavior == BL_TH_VAL_PERCENTAGE + || behavior == BL_TH_VAL_ABSOLUTE) { + char inbuf[BUFSZ], buf[BUFSZ]; + int val; + boolean skipltgt = FALSE; + boolean gotnum = FALSE; + char *inp = inbuf; + char *numstart = inbuf; + + inbuf[0] = '\0'; + Sprintf(buf, "Enter %svalue for %s threshold:", + (behavior == BL_TH_VAL_PERCENTAGE) ? "percentage " : "", + initblstats[fld].fldname); + getlin(buf, inbuf); + if (inbuf[0] == '\0' || inbuf[0] == '\033') + goto choose_behavior; + + inp = trimspaces(inbuf); + if (!*inp) + goto choose_behavior; + + /* allow user to enter "<50%" or ">50" or just "50" */ + if (*inp == '>' || *inp == '<' || *inp == '=') { + lt_gt_eq = (*inp == '>') ? GT_VALUE + : (*inp == '<') ? LT_VALUE : EQ_VALUE; + skipltgt = TRUE; + *inp = ' '; + inp++; + numstart++; + } + while (digit(*inp)) { + inp++; + gotnum = TRUE; + } + if (*inp == '%') { + behavior = BL_TH_VAL_PERCENTAGE; + *inp = '\0'; + } else if (!*inp) { + behavior = BL_TH_VAL_ABSOLUTE; + } else { + /* some random characters */ + pline("\"%s\" is not a recognized number.", inp); + goto choose_value; + } + if (!gotnum) { + pline("Is that an invisible number?"); + goto choose_value; + } + + val = atoi(numstart); + if (behavior == BL_TH_VAL_PERCENTAGE) { + if (initblstats[fld].idxmax == -1) { + pline("Field '%s' does not support percentage values.", + initblstats[fld].fldname); + behavior = BL_TH_VAL_ABSOLUTE; + goto choose_value; + } + if (val < 0 || val > 100) { + pline("Not a valid percent value."); + goto choose_value; + } + } + + if (!skipltgt) { + lt_gt_eq = status_hilite_menu_choose_updownboth(fld, inbuf); + if (lt_gt_eq == -2) + goto choose_value; + } + + Sprintf(colorqry, "Choose a color for when %s is %s%s:", + initblstats[fld].fldname, + numstart, + (lt_gt_eq == EQ_VALUE) ? "" + : (lt_gt_eq == LT_VALUE) ? " or less" + : " or more"); + + Sprintf(attrqry, "Choose attribute for when %s is %s%s:", + initblstats[fld].fldname, + inbuf, + (lt_gt_eq == EQ_VALUE) ? "" + : (lt_gt_eq == LT_VALUE) ? " or less" + : " or more"); + + hilite.rel = lt_gt_eq; + hilite.value.a_int = val; + } else if (behavior == BL_TH_UPDOWN) { + lt_gt_eq = status_hilite_menu_choose_updownboth(fld, (char *)0); + if (lt_gt_eq == -2) + goto choose_behavior; + Sprintf(colorqry, "Choose a color for when %s %s:", + initblstats[fld].fldname, + (lt_gt_eq == EQ_VALUE) ? "changes" + : (lt_gt_eq == LT_VALUE) ? "decreases" + : "increases"); + Sprintf(attrqry, "Choose attribute for when %s %s:", + initblstats[fld].fldname, + (lt_gt_eq == EQ_VALUE) ? "changes" + : (lt_gt_eq == LT_VALUE) ? "decreases" + : "increases"); + hilite.rel = lt_gt_eq; + } else if (behavior == BL_TH_CONDITION) { + cond = query_conditions(); + if (!cond) { + if (origfld == BL_FLUSH) + goto choose_field; + else + return FALSE; + } + Sprintf(colorqry, "Choose a color for conditions %s:", + conditionbitmask2str(cond)); + Sprintf(attrqry, "Choose attribute for conditions %s:", + conditionbitmask2str(cond)); + } else if (behavior == BL_TH_TEXTMATCH) { + char qry_buf[BUFSZ]; + Sprintf(qry_buf, "%s %s text value to match:", + (fld == BL_CAP + || fld == BL_ALIGN + || fld == BL_HUNGER + || fld == BL_TITLE) ? "Choose" : "Enter", + initblstats[fld].fldname); + if (fld == BL_CAP) { + int rv = query_arrayvalue(qry_buf, + enc_stat, + SLT_ENCUMBER, OVERLOADED+1); + if (rv < SLT_ENCUMBER) + goto choose_behavior; + + hilite.rel = TXT_VALUE; + Strcpy(hilite.textmatch, enc_stat[rv]); + } else if (fld == BL_ALIGN) { + const char *aligntxt[] = {"chaotic", "neutral", "lawful"}; + int rv = query_arrayvalue(qry_buf, + aligntxt, 0, 3); + if (rv < 0) + goto choose_behavior; + + hilite.rel = TXT_VALUE; + Strcpy(hilite.textmatch, aligntxt[rv]); + } else if (fld == BL_HUNGER) { + const char *hutxt[] = {"Satiated", "", "Hungry", "Weak", + "Fainting", "Fainted", "Starved"}; + int rv = query_arrayvalue(qry_buf, + hutxt, + SATIATED, STARVED+1); + if (rv < SATIATED) + goto choose_behavior; + + hilite.rel = TXT_VALUE; + Strcpy(hilite.textmatch, hutxt[rv]); + } else if (fld == BL_TITLE) { + const char *rolelist[9]; + int i, rv; + + for (i = 0; i < 9; i++) + rolelist[i] = (flags.female && urole.rank[i].f) + ? urole.rank[i].f : urole.rank[i].m; + + rv = query_arrayvalue(qry_buf, rolelist, 0, 9); + if (rv < 0) + goto choose_behavior; + + hilite.rel = TXT_VALUE; + Strcpy(hilite.textmatch, rolelist[rv]); + } else { + char inbuf[BUFSZ]; + + inbuf[0] = '\0'; + getlin(qry_buf, inbuf); + if (inbuf[0] == '\0' || inbuf[0] == '\033') + goto choose_behavior; + + hilite.rel = TXT_VALUE; + if (strlen(inbuf) < QBUFSZ-1) + Strcpy(hilite.textmatch, inbuf); + else + return FALSE; + } + Sprintf(colorqry, "Choose a color for when %s is '%s':", + initblstats[fld].fldname, hilite.textmatch); + Sprintf(colorqry, "Choose attribute for when %s is '%s':", + initblstats[fld].fldname, hilite.textmatch); + } else if (behavior == BL_TH_ALWAYS_HILITE) { + Sprintf(colorqry, "Choose a color to always hilite %s:", + initblstats[fld].fldname); + Sprintf(attrqry, "Choose attribute to always hilite %s:", + initblstats[fld].fldname); + } + +choose_color: + + clr = query_color(colorqry); + if (clr == -1) { + if (behavior != BL_TH_ALWAYS_HILITE) + goto choose_value; + else + goto choose_behavior; + } + + atr = query_attr(attrqry); /* FIXME: pick multiple attrs */ + if (atr == -1) + goto choose_color; + if (atr == ATR_DIM) + atr = HL_DIM; + else if (atr == ATR_BLINK) + atr = HL_BLINK; + else if (atr == ATR_ULINE) + atr = HL_ULINE; + else if (atr == ATR_INVERSE) + atr = HL_INVERSE; + else if (atr == ATR_BOLD) + atr = HL_BOLD; + else if (atr == ATR_NONE) + atr = HL_NONE; + else + atr = HL_UNDEF; + + if (clr == -1) + clr = NO_COLOR; + + if (behavior == BL_TH_CONDITION) { + char clrbuf[BUFSZ]; + char attrbuf[BUFSZ]; + char *tmpattr; + if (atr == HL_DIM) + cond_hilites[HL_ATTCLR_DIM] |= cond; + else if (atr == HL_BLINK) + cond_hilites[HL_ATTCLR_BLINK] |= cond; + else if (atr == HL_ULINE) + cond_hilites[HL_ATTCLR_ULINE] |= cond; + else if (atr == HL_INVERSE) + cond_hilites[HL_ATTCLR_INVERSE] |= cond; + else if (atr == HL_BOLD) + cond_hilites[HL_ATTCLR_BOLD] |= cond; + else if (atr == HL_NONE) { + cond_hilites[HL_ATTCLR_DIM] = 0UL; + cond_hilites[HL_ATTCLR_BLINK] = 0UL; + cond_hilites[HL_ATTCLR_ULINE] = 0UL; + cond_hilites[HL_ATTCLR_INVERSE] = 0UL; + cond_hilites[HL_ATTCLR_BOLD] = 0UL; + } + cond_hilites[clr] |= cond; + (void) stripchars(clrbuf, " ", clr2colorname(clr)); + tmpattr = hlattr2attrname(atr, attrbuf, BUFSZ); + if (tmpattr) + Sprintf(eos(clrbuf), "&%s", tmpattr); + pline("Added hilite condition/%s/%s", + conditionbitmask2str(cond), clrbuf); + } else { + hilite.coloridx = clr | (atr << 8); + hilite.anytype = initblstats[fld].anytype; + + status_hilite_add_threshold(fld, &hilite); + pline("Added hilite %s", status_hilite2str(&hilite)); + } + reset_status_hilites(); + return TRUE; +} + +boolean +status_hilite_remove(id) +int id; +{ + struct _status_hilite_line_str *hlstr = status_hilite_str; + + while (hlstr && hlstr->id != id) { + hlstr = hlstr->next; + } + + if (!hlstr) + return FALSE; + + if (hlstr->fld == BL_CONDITION) { + int i; + + for (i = 0; i < CLR_MAX; i++) + cond_hilites[i] &= ~hlstr->mask; + cond_hilites[HL_ATTCLR_DIM] &= ~hlstr->mask; + cond_hilites[HL_ATTCLR_BOLD] &= ~hlstr->mask; + cond_hilites[HL_ATTCLR_BLINK] &= ~hlstr->mask; + cond_hilites[HL_ATTCLR_ULINE] &= ~hlstr->mask; + cond_hilites[HL_ATTCLR_INVERSE] &= ~hlstr->mask; + return TRUE; + } else { + int fld = hlstr->fld; + struct hilite_s *hl = blstats[0][fld].thresholds; + struct hilite_s *hlprev = (struct hilite_s *) 0; + + if (hl) { + while (hl) { + if (hlstr->hl == hl) { + if (hlprev) + hlprev->next = hl->next; + else { + blstats[0][fld].thresholds = hl->next; + blstats[1][fld].thresholds = + blstats[0][fld].thresholds; + } + free(hl); + return TRUE; + } + hlprev = hl; + hl = hl->next; + } + } + } + return FALSE; +} + +boolean +status_hilite_menu_fld(fld) +int fld; +{ + winid tmpwin; + int i, res; + menu_item *picks = (menu_item *) 0; + anything any; + int count = status_hilite_linestr_countfield(fld); + struct _status_hilite_line_str *hlstr; + char buf[BUFSZ]; + boolean acted = FALSE; + + if (!count) { + if (status_hilite_menu_add(fld)) { + status_hilite_linestr_done(); + status_hilite_linestr_gather(); + count = status_hilite_linestr_countfield(fld); + } else + return FALSE; + } + + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin); + + if (count) { + hlstr = status_hilite_str; + while (hlstr) { + if (hlstr->fld == fld) { + any = zeroany; + any.a_int = hlstr->id; + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + hlstr->str, MENU_UNSELECTED); + } + hlstr = hlstr->next; + } + } else { + any = zeroany; + Sprintf(buf, "No current hilites for %s", initblstats[fld].fldname); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED); + } + + /* separator line */ + any = zeroany; + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED); + + if (count) { + any = zeroany; + any.a_int = -1; + add_menu(tmpwin, NO_GLYPH, &any, 'X', 0, ATR_NONE, + "Remove selected hilites", MENU_UNSELECTED); + } + + any = zeroany; + any.a_int = -2; + add_menu(tmpwin, NO_GLYPH, &any, 'Z', 0, ATR_NONE, + "Add a new hilite", MENU_UNSELECTED); + + + Sprintf(buf, "Current %s hilites:", initblstats[fld].fldname); + end_menu(tmpwin, buf); + + if ((res = select_menu(tmpwin, PICK_ANY, &picks)) > 0) { + int mode = 0; + for (i = 0; i < res; i++) { + int idx = picks[i].item.a_int; + if (idx == -1) { + /* delete selected hilites */ + if (mode) + goto shlmenu_free; + mode = -1; + break; + } else if (idx == -2) { + /* create a new hilite */ + if (mode) + goto shlmenu_free; + mode = -2; + break; + } + } + + if (mode == -1) { + /* delete selected hilites */ + for (i = 0; i < res; i++) { + int idx = picks[i].item.a_int; + if (idx > 0) + (void) status_hilite_remove(idx); + } + reset_status_hilites(); + acted = TRUE; + } else if (mode == -2) { + /* create a new hilite */ + if (status_hilite_menu_add(fld)) + acted = TRUE; + } + + free((genericptr_t) picks); + } + +shlmenu_free: + + picks = (menu_item *) 0; + destroy_nhwindow(tmpwin); + return acted; +} + +void +status_hilites_viewall() +{ + winid datawin; + struct _status_hilite_line_str *hlstr = status_hilite_str; + char buf[BUFSZ]; + + datawin = create_nhwindow(NHW_TEXT); + + while (hlstr) { + Sprintf(buf, "OPTIONS=hilite_status: %s", hlstr->str); + putstr(datawin, 0, buf); + hlstr = hlstr->next; + } + + display_nhwindow(datawin, FALSE); + destroy_nhwindow(datawin); } boolean status_hilite_menu() { - int i, j, k, pick_cnt, pick_idx, opt_idx; - menu_item *statfield_picks = (menu_item *) 0; - const char *fieldname; - int field_picks[MAXBLSTATS], res; - struct hilite_s hltemp[MAXBLSTATS]; - char buf[BUFSZ], thresholdbuf[BUFSZ], below[BUFSZ], above[BUFSZ]; winid tmpwin; + int i, res; + menu_item *picks = (menu_item *) 0; anything any; + boolean redo; + int countall; + +shlmenu_redo: + redo = FALSE; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin); + + status_hilite_linestr_gather(); + + countall = status_hilite_linestr_countfield(BL_FLUSH); + + if (countall) { + any = zeroany; + any.a_int = -1; + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + "View all hilites in config format", MENU_UNSELECTED); + + any = zeroany; + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED); + } + for (i = 0; i < MAXBLSTATS; i++) { - (void) memset(&hltemp[i], 0, sizeof(struct hilite_s)); - fieldname = fieldids[i].fieldname; - any.a_int = i + 1; - add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, fieldname, - MENU_UNSELECTED); - field_picks[i] = 0; + int count = status_hilite_linestr_countfield(i); + char buf[BUFSZ]; + + any = zeroany; + any.a_int = (i+1); + if (count) + Sprintf(buf, "%-18s (%i defined)", + initblstats[i].fldname, count); + else + Sprintf(buf, "%-18s", initblstats[i].fldname); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + buf, MENU_UNSELECTED); } - end_menu(tmpwin, "Change hilite on which status field(s):"); - if ((pick_cnt = select_menu(tmpwin, PICK_ANY, &statfield_picks)) > 0) { - for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) { - opt_idx = statfield_picks[pick_idx].item.a_int - 1; - field_picks[opt_idx] = 1; - } - free((genericptr_t) statfield_picks); - statfield_picks = (menu_item *) 0; + + + end_menu(tmpwin, "Status hilites:"); + if ((res = select_menu(tmpwin, PICK_ONE, &picks)) > 0) { + i = picks->item.a_int - 1; + if (i < 0) + status_hilites_viewall(); + else + (void) status_hilite_menu_fld(i); + free((genericptr_t) picks); + redo = TRUE; } + + picks = (menu_item *) 0; destroy_nhwindow(tmpwin); - if (pick_cnt < 0) - return FALSE; + status_hilite_linestr_done(); - for (i = 0; i < MAXBLSTATS; i++) { - if (field_picks[i]) { - menu_item *pick = (menu_item *) 0; + if (redo) + goto shlmenu_redo; - Sprintf(buf, "Threshold behavior options for %s:", - fieldids[i].fieldname); - tmpwin = create_nhwindow(NHW_MENU); - start_menu(tmpwin); - if (i == BL_CONDITION) { - any = zeroany; - any.a_int = BL_TH_CONDITION + 1; - add_menu(tmpwin, NO_GLYPH, &any, 'c', 0, ATR_NONE, - "Condition bitmask threshold.", MENU_UNSELECTED); - } - any = zeroany; - any.a_int = BL_TH_NONE + 1; - add_menu(tmpwin, NO_GLYPH, &any, 'n', 0, ATR_NONE, "None", - MENU_UNSELECTED); - if (i != BL_CONDITION) { - if (blstats[0][i].idxmax > 0) { - any = zeroany; - any.a_int = BL_TH_VAL_PERCENTAGE + 1; - add_menu(tmpwin, NO_GLYPH, &any, 'p', 0, ATR_NONE, - "Percentage threshold.", MENU_UNSELECTED); - } - any = zeroany; - any.a_int = BL_TH_UPDOWN + 1; - add_menu(tmpwin, NO_GLYPH, &any, 'u', 0, ATR_NONE, - "UpDown threshold.", MENU_UNSELECTED); - any = zeroany; - any.a_int = BL_TH_VAL_ABSOLUTE + 1; - add_menu(tmpwin, NO_GLYPH, &any, 'v', 0, ATR_NONE, - "Value threshold.", MENU_UNSELECTED); - } - end_menu(tmpwin, buf); - if ((res = select_menu(tmpwin, PICK_ONE, &pick)) > 0) { - hltemp[i].behavior = pick->item.a_int - 1; - free((genericptr_t) pick); - } - destroy_nhwindow(tmpwin); - if (res < 0) - return FALSE; - - if (hltemp[i].behavior == BL_TH_UPDOWN) { - Sprintf(below, "%s decreases", fieldids[i].fieldname); - Sprintf(above, "%s increases", fieldids[i].fieldname); - } else if (hltemp[i].behavior) { - /* Have them enter the threshold*/ - Sprintf( - buf, "Set %s threshold to what%s?", fieldids[i].fieldname, - (hltemp[i].behavior == BL_TH_VAL_PERCENTAGE) - ? " percentage" - : (hltemp[i].behavior == BL_TH_CONDITION) ? " mask" - : ""); - getlin(buf, thresholdbuf); - if (thresholdbuf[0] == '\033') - return FALSE; - (void) s_to_anything(&hltemp[i].threshold, thresholdbuf, - blstats[0][i].anytype); - if (!hltemp[i].threshold.a_void) - return FALSE; - - Sprintf(below, "%s falls below %s%s", fieldids[i].fieldname, - thresholdbuf, - (hltemp[i].behavior == BL_TH_VAL_PERCENTAGE) ? "%" - : ""); - Sprintf(above, "%s rises above %s%s", fieldids[i].fieldname, - thresholdbuf, - (hltemp[i].behavior == BL_TH_VAL_PERCENTAGE) ? "%" - : ""); - } - for (j = 0; j < 2 && (hltemp[i].behavior != BL_TH_NONE); ++j) { - char prompt[QBUFSZ]; - /* j == 0 below, j == 1 above */ - menu_item *pick2 = (menu_item *) 0; - - Sprintf(prompt, "Display how when %s?", j ? above : below); - tmpwin = create_nhwindow(NHW_MENU); - start_menu(tmpwin); - for (k = -3; k < CLR_MAX; ++k) { - /* if (k == -1) continue; */ - any = zeroany; - any.a_int = (k >= 0) ? k + 1 : k; - if (k > 0) - add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, - c_obj_colors[k], MENU_UNSELECTED); - else if (k == -1) - add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, - "normal", MENU_UNSELECTED); - else if (k == -2) - add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, - "inverse", MENU_UNSELECTED); - else if (k == -3) - add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, - "bold", MENU_UNSELECTED); - } - end_menu(tmpwin, prompt); - if ((res = select_menu(tmpwin, PICK_ONE, &pick2)) > 0) { - hltemp[i].coloridx[j] = (pick2->item.a_char > 0) - ? pick2->item.a_int - 1 - : pick2->item.a_int; - free((genericptr_t) pick2); - } - destroy_nhwindow(tmpwin); - if (res < 0) - return FALSE; - } - } - } - buf[0] = '\0'; - for (i = 0; i < MAXBLSTATS; i++) { - if (field_picks[i]) { - Sprintf(eos(buf), "%s/%s%s/", fieldids[i].fieldname, - (hltemp[i].behavior == BL_TH_UPDOWN) - ? "updown" - : anything_to_s(thresholdbuf, &hltemp[i].threshold, - blstats[0][i].anytype), - (hltemp[i].behavior == BL_TH_VAL_PERCENTAGE) ? "%" : ""); - /* borrow thresholdbuf for use with these last two */ - Sprintf(eos(buf), "%s/", - clridx_to_s(thresholdbuf, hltemp[i].coloridx[0])); - Sprintf(eos(buf), "%s ", - clridx_to_s(thresholdbuf, hltemp[i].coloridx[1])); - } - } - return set_status_hilites(buf, FALSE); + return TRUE; } -#endif /*STATUS_HILITES*/ -#endif /*STATUS_VIA_WINDOWPORT*/ + +#endif /* STATUS_HILITES */ /*botl.c*/ diff --git a/src/decl.c b/src/decl.c index 97020d5ae..fc9dc96e6 100644 --- a/src/decl.c +++ b/src/decl.c @@ -264,9 +264,7 @@ NEARDATA char **viz_array = 0; /* used in cansee() and couldsee() macros */ /* Global windowing data, defined here for multi-window-system support */ NEARDATA winid WIN_MESSAGE = WIN_ERR; -#ifndef STATUS_VIA_WINDOWPORT NEARDATA winid WIN_STATUS = WIN_ERR; -#endif NEARDATA winid WIN_MAP = WIN_ERR, WIN_INVEN = WIN_ERR; char toplines[TBUFSZ]; /* Windowing stuff that's really tty oriented, but present for all ports */ diff --git a/src/do.c b/src/do.c index d88dda4cf..d93daa6cc 100644 --- a/src/do.c +++ b/src/do.c @@ -158,7 +158,10 @@ const char *verb; (mtmp) ? "" : " with you"); if (mtmp) { if (!passes_walls(mtmp->data) && !throws_rocks(mtmp->data)) { - if (hmon(mtmp, obj, TRUE) && !is_whirly(mtmp->data)) + int dieroll = rnd(20); + + if (hmon(mtmp, obj, HMON_THROWN, dieroll) + && !is_whirly(mtmp->data)) return FALSE; /* still alive */ } mtmp->mtrapped = 0; diff --git a/src/dothrow.c b/src/dothrow.c index a6825fcc9..6c9aeb315 100644 --- a/src/dothrow.c +++ b/src/dothrow.c @@ -884,7 +884,7 @@ boolean hitsroof; /* object now hits you */ if (obj->oclass == POTION_CLASS) { - potionhit(&youmonst, obj, TRUE); + potionhit(&youmonst, obj, POTHIT_HERO_THROW); } else if (breaktest(obj)) { int otyp = obj->otyp; int blindinc; @@ -1086,7 +1086,7 @@ boolean && rn2(6)) { /* alternative to prayer or wand of opening/spell of knock for dealing with cursed saddle: throw holy water > */ - potionhit(u.usteed, obj, TRUE); + potionhit(u.usteed, obj, POTHIT_HERO_THROW); } else { hitfloor(obj); } @@ -1386,6 +1386,7 @@ register struct obj *obj; /* thrownobj or kickedobj or uwep */ register int disttmp; /* distance modifier */ int otyp = obj->otyp, hmode; boolean guaranteed_hit = (u.uswallow && mon == u.ustuck); + int dieroll; hmode = (obj == uwep) ? HMON_APPLIED : (obj == kickedobj) ? HMON_KICKED @@ -1493,6 +1494,8 @@ register struct obj *obj; /* thrownobj or kickedobj or uwep */ return 0; } + dieroll = rnd(20); + if (obj->oclass == WEAPON_CLASS || is_weptool(obj) || obj->oclass == GEM_CLASS) { if (hmode == HMON_KICKED) { @@ -1533,13 +1536,13 @@ register struct obj *obj; /* thrownobj or kickedobj or uwep */ tmp += weapon_hit_bonus(obj); } - if (tmp >= rnd(20)) { + if (tmp >= dieroll) { boolean wasthrown = (thrownobj != 0); /* attack hits mon */ if (hmode == HMON_APPLIED) u.uconduct.weaphit++; - if (hmon(mon, obj, hmode)) { /* mon still alive */ + if (hmon(mon, obj, hmode, dieroll)) { /* mon still alive */ cutworm(mon, bhitpos.x, bhitpos.y, obj); } exercise(A_DEX, TRUE); @@ -1585,11 +1588,11 @@ register struct obj *obj; /* thrownobj or kickedobj or uwep */ } else if (otyp == HEAVY_IRON_BALL) { exercise(A_STR, TRUE); - if (tmp >= rnd(20)) { + if (tmp >= dieroll) { int was_swallowed = guaranteed_hit; exercise(A_DEX, TRUE); - if (!hmon(mon, obj, hmode)) { /* mon killed */ + if (!hmon(mon, obj, hmode, dieroll)) { /* mon killed */ if (was_swallowed && !u.uswallow && obj == uball) return 1; /* already did placebc() */ } @@ -1599,9 +1602,9 @@ register struct obj *obj; /* thrownobj or kickedobj or uwep */ } else if (otyp == BOULDER) { exercise(A_STR, TRUE); - if (tmp >= rnd(20)) { + if (tmp >= dieroll) { exercise(A_DEX, TRUE); - (void) hmon(mon, obj, hmode); + (void) hmon(mon, obj, hmode, dieroll); } else { tmiss(obj, mon, TRUE); } @@ -1609,12 +1612,12 @@ register struct obj *obj; /* thrownobj or kickedobj or uwep */ } else if ((otyp == EGG || otyp == CREAM_PIE || otyp == BLINDING_VENOM || otyp == ACID_VENOM) && (guaranteed_hit || ACURR(A_DEX) > rnd(25))) { - (void) hmon(mon, obj, hmode); + (void) hmon(mon, obj, hmode, dieroll); return 1; /* hmon used it up */ } else if (obj->oclass == POTION_CLASS && (guaranteed_hit || ACURR(A_DEX) > rnd(25))) { - potionhit(mon, obj, TRUE); + potionhit(mon, obj, POTHIT_HERO_THROW); return 1; } else if (befriend_with_obj(mon->data, obj) diff --git a/src/end.c b/src/end.c index 3344f5771..31914dbb5 100644 --- a/src/end.c +++ b/src/end.c @@ -1277,7 +1277,7 @@ int how; destroy_nhwindow(WIN_INVEN), WIN_INVEN = WIN_ERR; display_nhwindow(WIN_MESSAGE, TRUE); destroy_nhwindow(WIN_MAP), WIN_MAP = WIN_ERR; -#ifndef STATUS_VIA_WINDOWPORT +#ifndef STATUS_HILITES destroy_nhwindow(WIN_STATUS), WIN_STATUS = WIN_ERR; #endif destroy_nhwindow(WIN_MESSAGE), WIN_MESSAGE = WIN_ERR; diff --git a/src/explode.c b/src/explode.c index 50674f087..643805dc4 100644 --- a/src/explode.c +++ b/src/explode.c @@ -570,7 +570,7 @@ struct obj *obj; /* only scatter this obj */ struct scatter_chain *schain = (struct scatter_chain *) 0; long total = 0L; - while ((otmp = individual_object ? obj : level.objects[sx][sy]) != 0) { + while ((otmp = (individual_object ? obj : level.objects[sx][sy])) != 0) { if (otmp->quan > 1L) { qtmp = otmp->quan - 1L; if (qtmp > LARGEST_INT) @@ -584,8 +584,8 @@ struct obj *obj; /* only scatter this obj */ used_up = FALSE; /* 9 in 10 chance of fracturing boulders or statues */ - if ((scflags & MAY_FRACTURE) - && ((otmp->otyp == BOULDER) || (otmp->otyp == STATUE)) + if ((scflags & MAY_FRACTURE) != 0 + && (otmp->otyp == BOULDER || otmp->otyp == STATUE) && rn2(10)) { if (otmp->otyp == BOULDER) { if (cansee(sx, sy)) @@ -614,7 +614,7 @@ struct obj *obj; /* only scatter this obj */ used_up = TRUE; /* 1 in 10 chance of destruction of obj; glass, egg destruction */ - } else if ((scflags & MAY_DESTROY) + } else if ((scflags & MAY_DESTROY) != 0 && (!rn2(10) || (objects[otmp->otyp].oc_material == GLASS || otmp->otyp == EGG))) { if (breaks(otmp, (xchar) sx, (xchar) sy)) @@ -622,8 +622,7 @@ struct obj *obj; /* only scatter this obj */ } if (!used_up) { - stmp = (struct scatter_chain *) - alloc(sizeof (struct scatter_chain)); + stmp = (struct scatter_chain *) alloc(sizeof *stmp); stmp->next = (struct scatter_chain *) 0; stmp->obj = otmp; stmp->ox = sx; @@ -679,7 +678,9 @@ struct obj *obj; /* only scatter this obj */ if (bigmonst(youmonst.data)) hitvalu++; hitu = thitu(hitvalu, dmgval(stmp->obj, &youmonst), - stmp->obj, (char *) 0); + &stmp->obj, (char *) 0); + if (!stmp->obj) + stmp->stopped = TRUE; if (hitu) { stmp->range -= 3; stop_occupation(); diff --git a/src/files.c b/src/files.c index 8ca16bab2..8abacce66 100644 --- a/src/files.c +++ b/src/files.c @@ -2540,6 +2540,11 @@ char *origbuf; } else if (match_varname(buf, "MENUCOLOR", 9)) { if (!add_menu_coloring(bufp)) retval = FALSE; + } else if (match_varname(buf, "HILITE_STATUS", 6)) { +#ifdef STATUS_HILITES + if (!parse_status_hl1(bufp, TRUE)) + retval = FALSE; +#endif } else if (match_varname(buf, "WARNINGS", 5)) { (void) get_uchars(bufp, translate, FALSE, WARNCOUNT, "WARNINGS"); diff --git a/src/hack.c b/src/hack.c index 11ff4275f..14e7258b4 100644 --- a/src/hack.c +++ b/src/hack.c @@ -1161,7 +1161,7 @@ trapmove(x, y, desttrap) int x, y; /* targetted destination, */ struct trap *desttrap; /* nonnull if another trap at */ { - boolean anchored; + boolean anchored = FALSE; const char *predicament, *culprit; char *steedname = !u.usteed ? (char *) 0 : y_monnam(u.usteed); @@ -1180,6 +1180,8 @@ struct trap *desttrap; /* nonnull if another trap at */ /* [why does diagonal movement give quickest escape?] */ if ((u.dx && u.dy) || !rn2(5)) u.utrap--; + if (!u.utrap) + goto wriggle_free; break; case TT_PIT: if (desttrap && desttrap->tseen @@ -1271,6 +1273,7 @@ struct trap *desttrap; /* nonnull if another trap at */ Norep("You are %s %s.", predicament, culprit); } } else { +wriggle_free: if (u.usteed) pline("%s finally %s free.", upstart(steedname), !anchored ? "lurches" : "wrenches the ball"); diff --git a/src/hacklib.c b/src/hacklib.c index 1c4fe63d6..5d18fcbf7 100644 --- a/src/hacklib.c +++ b/src/hacklib.c @@ -20,6 +20,7 @@ char * mungspaces (char *) char * trimspaces (char *) char * strip_newline (char *) + char * stripchars (char *, const char *, const char *) char * eos (char *) boolean str_end_is (const char *, const char *) char * strkitten (char *,char) @@ -433,6 +434,31 @@ char c; return ccc; } +/* strip all the chars in stuff_to_strip from orig */ +/* caller is responsible for ensuring that bp is a + valid pointer to a BUFSZ buffer */ +char * +stripchars(bp, stuff_to_strip, orig) +char *bp; +const char *stuff_to_strip, *orig; +{ + int i = 0; + char *s = bp; + + if (s) { + while (s && *orig && i < (BUFSZ - 1)) { + if (!index(stuff_to_strip, *orig)) { + *s++ = *orig; + i++; + } + orig++; + } + *s = '\0'; + } else + impossible("no output buf in stripchars"); + return bp; +} + /* substitute a word or phrase in a string (in place) */ /* caller is responsible for ensuring that bp points to big enough buffer */ char * diff --git a/src/mkobj.c b/src/mkobj.c index 78bb826f1..ab59719b7 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -1339,7 +1339,7 @@ int weight(obj) register struct obj *obj; { - int wt = objects[obj->otyp].oc_weight; + int wt = (int) objects[obj->otyp].oc_weight; /* glob absorpsion means that merging globs accumulates weight while quantity stays 1, so update 'wt' to reflect that, unless owt is 0, @@ -1390,6 +1390,8 @@ register struct obj *obj; return (int) ((obj->quan + 50L) / 100L); } else if (obj->otyp == HEAVY_IRON_BALL && obj->owt != 0) { return (int) obj->owt; /* kludge for "very" heavy iron ball */ + } else if (obj->otyp == CANDELABRUM_OF_INVOCATION && obj->spe) { + return wt + obj->spe * (int) objects[TALLOW_CANDLE].oc_weight; } return (wt ? wt * (int) obj->quan : ((int) obj->quan + 1) >> 1); } diff --git a/src/mthrowu.c b/src/mthrowu.c index c36c0fe75..3d2830b96 100644 --- a/src/mthrowu.c +++ b/src/mthrowu.c @@ -29,11 +29,12 @@ extern boolean notonhead; /* for long worms */ /* hero is hit by something other than a monster */ int -thitu(tlev, dam, obj, name) +thitu(tlev, dam, objp, name) int tlev, dam; -struct obj *obj; -const char *name; /* if null, then format `obj' */ +struct obj **objp; +const char *name; /* if null, then format `*objp' */ { + struct obj *obj = objp ? *objp : 0; const char *onm, *knm; boolean is_acid; int kprefix = KILLED_BY_AN; @@ -42,8 +43,8 @@ const char *name; /* if null, then format `obj' */ if (!name) { if (!obj) panic("thitu: name & obj both null?"); - name = - strcpy(onmbuf, (obj->quan > 1L) ? doname(obj) : mshot_xname(obj)); + name = strcpy(onmbuf, + (obj->quan > 1L) ? doname(obj) : mshot_xname(obj)); knm = strcpy(knmbuf, killer_xname(obj)); kprefix = KILLED_BY; /* killer_name supplies "an" if warranted */ } else { @@ -70,14 +71,20 @@ const char *name; /* if null, then format `obj' */ else You("are hit by %s%s", onm, exclam(dam)); - if (obj && objects[obj->otyp].oc_material == SILVER && Hate_silver) { - /* extra damage already applied by dmgval() */ - pline_The("silver sears your flesh!"); - exercise(A_CON, FALSE); - } if (is_acid && Acid_resistance) { pline("It doesn't seem to hurt you."); + } else if (obj && obj->oclass == POTION_CLASS) { + /* an explosion which scatters objects might hit hero with one + (potions deliberately thrown at hero are handled by m_throw) */ + potionhit(&youmonst, obj, POTHIT_OTHER_THROW); + *objp = obj = 0; /* potionhit() uses up the potion */ } else { + if (obj && objects[obj->otyp].oc_material == SILVER + && Hate_silver) { + /* extra damage already applied by dmgval() */ + pline_The("silver sears your flesh!"); + exercise(A_CON, FALSE); + } if (is_acid) pline("It burns!"); losehp(dam, knm, kprefix); /* acid damage */ @@ -110,9 +117,9 @@ int x, y; else create = 1; - if (create - && !((mtmp = m_at(x, y)) && (mtmp->mtrapped) && (t = t_at(x, y)) - && ((t->ttyp == PIT) || (t->ttyp == SPIKED_PIT)))) { + if (create && !((mtmp = m_at(x, y)) != 0 && mtmp->mtrapped + && (t = t_at(x, y)) != 0 + && (t->ttyp == PIT || t->ttyp == SPIKED_PIT))) { int objgone = 0; if (down_gate(x, y) != -1) @@ -329,7 +336,9 @@ boolean verbose; /* give message(s) even when you can't see what happened */ mtmp->msleeping = 0; if (vis) otmp->dknown = 1; - potionhit(mtmp, otmp, FALSE); + /* probably thrown by a monster rather than 'other', but the + distinction only matters when hitting the hero */ + potionhit(mtmp, otmp, POTHIT_OTHER_THROW); return 1; } else { damage = dmgval(otmp, mtmp); @@ -549,7 +558,7 @@ struct obj *obj; /* missile (or stack providing it) */ if (singleobj->oclass == POTION_CLASS) { if (!Blind) singleobj->dknown = 1; - potionhit(&youmonst, singleobj, FALSE); + potionhit(&youmonst, singleobj, POTHIT_MONST_THROW); break; } oldumort = u.umortality; @@ -565,7 +574,7 @@ struct obj *obj; /* missile (or stack providing it) */ /* fall through */ case CREAM_PIE: case BLINDING_VENOM: - hitu = thitu(8, 0, singleobj, (char *) 0); + hitu = thitu(8, 0, &singleobj, (char *) 0); break; default: dam = dmgval(singleobj, &youmonst); @@ -585,7 +594,7 @@ struct obj *obj; /* missile (or stack providing it) */ hitv += 8 + singleobj->spe; if (dam < 1) dam = 1; - hitu = thitu(hitv, dam, singleobj, (char *) 0); + hitu = thitu(hitv, dam, &singleobj, (char *) 0); } if (hitu && singleobj->opoisoned && is_poisonable(singleobj)) { char onmbuf[BUFSZ], knmbuf[BUFSZ]; @@ -888,7 +897,7 @@ struct monst *mtmp; if (dam < 1) dam = 1; - (void) thitu(hitv, dam, otmp, (char *) 0); + (void) thitu(hitv, dam, &otmp, (char *) 0); stop_occupation(); return; } diff --git a/src/objnam.c b/src/objnam.c index ec7c0e70e..5daead647 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -1106,8 +1106,11 @@ unsigned doname_flags; "corpse" is already in the buffer returned by xname() */ unsigned cxarg = (((obj->quan != 1L) ? 0 : CXN_ARTICLE) | CXN_NOCORPSE); + char *cxstr = corpse_xname(obj, prefix, cxarg); - Sprintf(prefix, "%s ", corpse_xname(obj, prefix, cxarg)); + Sprintf(prefix, "%s ", cxstr); + /* avoid having doname(corpse) consume an extra obuf */ + releaseobuf(cxstr); } else if (obj->otyp == EGG) { #if 0 /* corpses don't tell if they're stale either */ if (known && stale_egg(obj)) diff --git a/src/options.c b/src/options.c index 467a669ff..d5842bb76 100644 --- a/src/options.c +++ b/src/options.c @@ -131,6 +131,9 @@ static struct Bool_Opt { { "help", &flags.help, TRUE, SET_IN_GAME }, { "hilite_pet", &iflags.wc_hilite_pet, FALSE, SET_IN_GAME }, /*WC*/ { "hilite_pile", &iflags.hilite_pile, FALSE, SET_IN_GAME }, +#ifdef STATUS_HILITES + { "hitpointbar", &iflags.wc2_hitpointbar, FALSE, SET_IN_GAME }, /*WC2*/ +#endif #ifndef MAC { "ignintr", &flags.ignintr, FALSE, SET_IN_GAME }, #else @@ -205,11 +208,6 @@ static struct Bool_Opt { { "sparkle", &flags.sparkle, TRUE, SET_IN_GAME }, { "splash_screen", &iflags.wc_splash_screen, TRUE, DISP_IN_GAME }, /*WC*/ { "standout", &flags.standout, FALSE, SET_IN_GAME }, -#if defined(STATUS_VIA_WINDOWPORT) && defined(STATUS_HILITES) - { "statushilites", &iflags.use_status_hilites, TRUE, SET_IN_GAME }, -#else - { "statushilites", &iflags.use_status_hilites, FALSE, DISP_IN_GAME }, -#endif { "status_updates", &iflags.status_updates, TRUE, DISP_IN_GAME }, { "tiled_map", &iflags.wc_tiled_map, PREFER_TILED, DISP_IN_GAME }, /*WC*/ { "time", &flags.time, FALSE, SET_IN_GAME }, @@ -375,6 +373,13 @@ static struct Comp_Opt { SET_IN_GAME }, #ifdef MSDOS { "soundcard", "type of sound card to use", 20, SET_IN_FILE }, +#endif +#ifdef STATUS_HILITES + { "statushilites", + "0=no status highlighting, N=show highlights for N turns", + 20, SET_IN_GAME }, +#else + { "statushilites", "highlight control", 20, SET_IN_FILE }, #endif { "symset", "load a set of display symbols from the symbols file", 70, SET_IN_GAME }, @@ -516,10 +521,7 @@ STATIC_DCL int FDECL(feature_alert_opts, (char *, const char *)); STATIC_DCL boolean FDECL(duplicate_opt_detection, (const char *, int)); STATIC_DCL void FDECL(complain_about_duplicate, (const char *, int)); -STATIC_DCL int FDECL(match_str2attr, (const char *)); STATIC_DCL const char *FDECL(attr2attrname, (int)); -STATIC_DCL int NDECL(query_color); -STATIC_DCL int FDECL(query_attr, (const char *)); STATIC_DCL const char * FDECL(msgtype2name, (int)); STATIC_DCL int NDECL(query_msgtype); STATIC_DCL boolean FDECL(msgtype_add, (int, char *)); @@ -1279,9 +1281,9 @@ static const struct { { "light magenta", CLR_BRIGHT_MAGENTA }, { "light cyan", CLR_BRIGHT_CYAN }, { "white", CLR_WHITE }, + { "no color", NO_COLOR }, { NULL, CLR_BLACK }, /* everything after this is an alias */ { "transparent", NO_COLOR }, - { "nocolor", NO_COLOR }, { "purple", CLR_MAGENTA }, { "light purple", CLR_BRIGHT_MAGENTA }, { "bright purple", CLR_BRIGHT_MAGENTA }, @@ -1302,7 +1304,10 @@ static const struct { { "dim", ATR_DIM }, { "underline", ATR_ULINE }, { "blink", ATR_BLINK }, - { "inverse", ATR_INVERSE } + { "inverse", ATR_INVERSE }, + { NULL, ATR_NONE }, /* everything after this is an alias */ + { "normal", ATR_NONE }, + { "uline", ATR_ULINE } }; const char * @@ -1321,7 +1326,7 @@ int match_str2clr(str) char *str; { - int i, c = NO_COLOR; + int i, c = CLR_MAX; /* allow "lightblue", "light blue", and "light-blue" to match "light blue" (also junk like "_l i-gh_t---b l u e" but we won't worry about that); @@ -1353,9 +1358,10 @@ int attr; return (char *) 0; } -STATIC_OVL int -match_str2attr(str) +int +match_str2attr(str, complain) const char *str; +boolean complain; { int i, a = -1; @@ -1366,14 +1372,15 @@ const char *str; break; } - if (a == -1) + if (a == -1 && complain) config_error_add("Unknown text attribute '%s'", str); return a; } -STATIC_OVL int -query_color() +int +query_color(prompt) +const char *prompt; { winid tmpwin; anything any; @@ -1390,7 +1397,7 @@ query_color() add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, colornames[i].name, MENU_UNSELECTED); } - end_menu(tmpwin, "Pick a color"); + end_menu(tmpwin, (prompt && *prompt) ? prompt : "Pick a color"); pick_cnt = select_menu(tmpwin, PICK_ONE, &picks); destroy_nhwindow(tmpwin); if (pick_cnt > 0) { @@ -1401,7 +1408,7 @@ query_color() return -1; } -STATIC_OVL int +int query_attr(prompt) const char *prompt; { @@ -1420,7 +1427,7 @@ const char *prompt; add_menu(tmpwin, NO_GLYPH, &any, 0, 0, attrnames[i].attr, attrnames[i].name, MENU_UNSELECTED); } - end_menu(tmpwin, prompt ? prompt : "Pick an attribute"); + end_menu(tmpwin, (prompt && *prompt) ? prompt : "Pick an attribute"); pick_cnt = select_menu(tmpwin, PICK_ONE, &picks); destroy_nhwindow(tmpwin); if (pick_cnt > 0) { @@ -1706,7 +1713,7 @@ char *tmpstr; if (amp) { tmps = amp + 1; /* advance past '&' */ - a = match_str2attr(tmps); + a = match_str2attr(tmps, TRUE); if (a == -1) return FALSE; } @@ -3427,7 +3434,7 @@ boolean tinitial, tfrom_file; } else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) { return FALSE; } - tmpattr = match_str2attr(opts); + tmpattr = match_str2attr(opts, TRUE); if (tmpattr == -1) return FALSE; else @@ -3458,23 +3465,39 @@ boolean tinitial, tfrom_file; return retval; } } -#if defined(STATUS_VIA_WINDOWPORT) && defined(STATUS_HILITES) +#ifdef STATUS_HILITES /* hilite fields in status prompt */ if (match_optname(opts, "hilite_status", 13, TRUE)) { if (duplicate) complain_about_duplicate(opts, 1); op = string_for_opt(opts, TRUE); if (op && negated) { - clear_status_hilites(tfrom_file); + clear_status_hilites(); return retval; } else if (!op) { config_error_add("Value is mandatory for hilite_status"); return FALSE; } - if (!set_status_hilites(op, tfrom_file)) /* TODO: error msg? */ + if (!parse_status_hl1(op, tfrom_file)) return FALSE; return retval; } + + /* control over whether highlights should be displayed, and for how long */ + fullname = "statushilites"; + if (match_optname(opts, fullname, 9, TRUE)) { + if (negated) { + iflags.hilite_delta = 0L; + } else { + op = string_for_opt(opts, TRUE); + iflags.hilite_delta = (!op || !*op) ? 3L : atol(op); + if (iflags.hilite_delta < 0L) + iflags.hilite_delta = 1L; + } + if (!tfrom_file) + reset_status_hilites(); + return retval; + } #endif #if defined(BACKWARD_COMPAT) @@ -3624,7 +3647,7 @@ boolean tinitial, tfrom_file; || boolopt[i].addr == &flags.showscore #endif || boolopt[i].addr == &flags.showexp) { -#ifdef STATUS_VIA_WINDOWPORT +#ifdef STATUS_HILITES status_initialize(REASSESS_ONLY); #endif context.botl = TRUE; @@ -3650,6 +3673,9 @@ boolean tinitial, tfrom_file; || boolopt[i].addr == &iflags.hilite_pile || boolopt[i].addr == &iflags.hilite_pet) { need_redraw = TRUE; + } else if ((boolopt[i].addr) == &iflags.wc2_hitpointbar) { + status_initialize(REASSESS_ONLY); + need_redraw = TRUE; #ifdef TEXTCOLOR } else if (boolopt[i].addr == &iflags.use_color) { need_redraw = TRUE; @@ -3937,6 +3963,12 @@ int nset; add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED); } +int +count_apes() +{ + return count_ape_maps((int *) 0, (int *) 0); +} + enum opt_other_enums { OPT_OTHER_MSGTYPE = -4, OPT_OTHER_MENUCOLOR = -3, @@ -3945,19 +3977,18 @@ enum opt_other_enums { /* these must be < 0 */ }; -/* presently only used when determining longest option name */ static struct other_opts { const char *name; int optflags; enum opt_other_enums code; + int NDECL((*othr_count_func)); } othropt[] = { - { "autopickup exceptions", SET_IN_GAME, OPT_OTHER_APEXC }, - { "menucolors", SET_IN_GAME, OPT_OTHER_MENUCOLOR }, - { "message types", SET_IN_GAME, OPT_OTHER_MSGTYPE }, -#ifdef STATUS_VIA_WINDOWPORT + { "autopickup exceptions", SET_IN_GAME, OPT_OTHER_APEXC, count_apes }, + { "menucolors", SET_IN_GAME, OPT_OTHER_MENUCOLOR, count_menucolors }, + { "message types", SET_IN_GAME, OPT_OTHER_MSGTYPE, msgtype_count }, #ifdef STATUS_HILITES - { "status_hilites", SET_IN_GAME, OPT_OTHER_STATHILITE }, -#endif + { "status hilite rules", SET_IN_GAME, OPT_OTHER_STATHILITE, + count_status_hilites }, #endif { (char *) 0, 0, (enum opt_other_enums) 0 }, }; @@ -4086,20 +4117,10 @@ doset() /* changing options via menu by Per Liboriussen */ "Other settings:", MENU_UNSELECTED); - opts_add_others(tmpwin, "autopickup exceptions", OPT_OTHER_APEXC, - NULL, count_ape_maps((int *) 0, (int *) 0)); - opts_add_others(tmpwin, "menucolors", OPT_OTHER_MENUCOLOR, - NULL, count_menucolors()); - opts_add_others(tmpwin, "message types", OPT_OTHER_MSGTYPE, - NULL, msgtype_count()); -#ifdef STATUS_VIA_WINDOWPORT -#ifdef STATUS_HILITES - get_status_hilites(buf2, 60); - if (!*buf2) - Sprintf(buf2, "%s", "(none)"); - opts_add_others(tmpwin, "status_hilites", OPT_OTHER_STATHILITE, buf2, 0); -#endif -#endif + for (i = 0; othropt[i].name; i++) + opts_add_others(tmpwin, othropt[i].name, othropt[i].code, + NULL, othropt[i].othr_count_func()); + #ifdef PREFIXES_IN_USE any = zeroany; add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED); @@ -4123,16 +4144,14 @@ doset() /* changing options via menu by Per Liboriussen */ if (opt_indx == OPT_OTHER_APEXC) { (void) special_handling("autopickup_exception", setinitial, fromfile); -#ifdef STATUS_VIA_WINDOWPORT #ifdef STATUS_HILITES } else if (opt_indx == OPT_OTHER_STATHILITE) { if (!status_hilite_menu()) { pline("Bad status hilite(s) specified."); } else { - if (wc2_supported("status_hilites")) - preference_update("status_hilites"); + if (wc2_supported("hilite_status")) + preference_update("hilite_status"); } -#endif #endif } else if (opt_indx == OPT_OTHER_MENUCOLOR) { (void) special_handling("menucolors", setinitial, @@ -4728,7 +4747,7 @@ boolean setinitial, setfromfile; return TRUE; if (*mcbuf && test_regex_pattern(mcbuf, (const char *)0) - && (mcclr = query_color()) != -1 + && (mcclr = query_color((char *) 0)) != -1 && (mcattr = query_attr((char *) 0)) != -1 && !add_menu_coloring_parsed(mcbuf, mcclr, mcattr)) { pline("Error adding the menu color."); @@ -5305,6 +5324,14 @@ char *buf; #ifdef MSDOS } else if (!strcmp(optname, "soundcard")) { Sprintf(buf, "%s", to_be_done); +#endif +#ifdef STATUS_HILITES + } else if (!strcmp("statushilites", optname)) { + if (!iflags.hilite_delta) + Strcpy(buf, "0 (off: don't highlight status fields)"); + else + Sprintf(buf, "%ld (on: highlight status for %ld turns)", + iflags.hilite_delta, iflags.hilite_delta); #endif } else if (!strcmp(optname, "suppress_alert")) { if (flags.suppress_alert == 0L) @@ -6010,7 +6037,8 @@ struct wc_Opt wc2_options[] = { { "fullscreen", WC2_FULLSCREEN }, { "softkeyboard", WC2_SOFTKEYBOARD }, { "wraptext", WC2_WRAPTEXT }, { "use_darkgray", WC2_DARKGRAY }, -#ifdef STATUS_VIA_WINDOWPORT +#ifdef STATUS_HILITES + { "hitpointbar", WC2_HITPOINTBAR }, { "hilite_status", WC2_HILITE_STATUS }, #endif { (char *) 0, 0L } }; diff --git a/src/polyself.c b/src/polyself.c index 9b15b32c0..c1dc74ff6 100644 --- a/src/polyself.c +++ b/src/polyself.c @@ -107,9 +107,11 @@ set_uasmon() youmonst.movement = new_speed * youmonst.movement / old_speed; } -#ifdef STATUS_VIA_WINDOWPORT +#ifdef STATUS_HILITES status_initialize(REASSESS_ONLY); #endif + + polysense(); } /* Levitation overrides Flying; set or clear BFlying|I_SPECIAL */ diff --git a/src/potion.c b/src/potion.c index 1a7e08ecc..aeed5891e 100644 --- a/src/potion.c +++ b/src/potion.c @@ -1243,24 +1243,28 @@ const char *objphrase; /* "Your widget glows" or "Steed's saddle glows" */ return res; } +/* potion obj hits monster mon, which might be youmonst; obj always use up */ void -potionhit(mon, obj, your_fault) -register struct monst *mon; -register struct obj *obj; -boolean your_fault; +potionhit(mon, obj, how) +struct monst *mon; +struct obj *obj; +int how; { const char *botlnam = bottlename(); boolean isyou = (mon == &youmonst); int distance, tx, ty; struct obj *saddle = (struct obj *) 0; - boolean hit_saddle = FALSE; + boolean hit_saddle = FALSE, your_fault = (how <= POTHIT_HERO_THROW); if (isyou) { tx = u.ux, ty = u.uy; distance = 0; pline_The("%s crashes on your %s and breaks into shards.", botlnam, body_part(HEAD)); - losehp(Maybe_Half_Phys(rnd(2)), "thrown potion", KILLED_BY_AN); + losehp(Maybe_Half_Phys(rnd(2)), + (how == POTHIT_OTHER_THROW) ? "propelled potion" /* scatter */ + : "thrown potion", + KILLED_BY_AN); } else { tx = mon->mx, ty = mon->my; /* sometimes it hits the saddle */ @@ -1314,6 +1318,7 @@ boolean your_fault; case POT_ACID: if (!Acid_resistance) { int dmg; + pline("This burns%s!", obj->blessed ? " a little" : obj->cursed ? " a lot" : ""); diff --git a/src/save.c b/src/save.c index cdd5f02d7..5f6674b40 100644 --- a/src/save.c +++ b/src/save.c @@ -1413,7 +1413,7 @@ freedynamicdata() /* free_pickinv_cache(); -- now done from really_done()... */ free_symsets(); #endif /* FREE_ALL_MEMORY */ -#ifdef STATUS_VIA_WINDOWPORT +#ifdef STATUS_HILITES status_finish(); #endif #ifdef DUMPLOG diff --git a/src/timeout.c b/src/timeout.c index 7e391cb13..a85baf552 100644 --- a/src/timeout.c +++ b/src/timeout.c @@ -203,7 +203,7 @@ vomiting_dialogue() countdown, and it was still possible to move around between that message and "You can move again." (from vomit()'s nomul(-2)) with no intervening message; give one here to - have a more specific at which hero became unable to move + have more specific point at which hero became unable to move [vomit() issues its own message for the cantvomit() case] */ You("%s!", !Hallucination ? "vomit" : "hurl chunks"); } @@ -966,7 +966,7 @@ anything *arg; long timeout; { struct obj *obj = arg->a_obj; - boolean canseeit, many, menorah, need_newsym; + boolean canseeit, many, menorah, need_newsym, need_invupdate; xchar x, y; char whose[BUFSZ]; @@ -983,6 +983,7 @@ long timeout; if (menorah) { obj->spe = 0; /* no more candles */ + obj->owt = weight(obj); } else if (Is_candle(obj) || obj->otyp == POT_OIL) { /* get rid of candles and burning oil potions; we know this object isn't carried by hero, @@ -1007,7 +1008,7 @@ long timeout; } else { canseeit = FALSE; } - need_newsym = FALSE; + need_newsym = need_invupdate = FALSE; /* obj->age is the age remaining at this point. */ switch (obj->otyp) { @@ -1016,6 +1017,8 @@ long timeout; if (canseeit) { switch (obj->where) { case OBJ_INVENT: + need_invupdate = TRUE; + /*FALLTHRU*/ case OBJ_MINVENT: pline("%spotion of oil has burnt away.", whose); break; @@ -1077,6 +1080,8 @@ long timeout; if (canseeit || obj->where == OBJ_INVENT) { switch (obj->where) { case OBJ_INVENT: + need_invupdate = TRUE; + /*FALLTHRU*/ case OBJ_MINVENT: if (obj->otyp == BRASS_LANTERN) pline("%slantern has run out of power.", whose); @@ -1154,6 +1159,8 @@ long timeout; if (menorah) { switch (obj->where) { case OBJ_INVENT: + need_invupdate = TRUE; + /*FALLTHRU*/ case OBJ_MINVENT: pline("%scandelabrum's flame%s.", whose, many ? "s die" : " dies"); @@ -1166,15 +1173,18 @@ long timeout; } else { switch (obj->where) { case OBJ_INVENT: + /* no need_invupdate for update_inventory() necessary; + useupall() -> freeinv() handles it */ + /*FALLTHRU*/ case OBJ_MINVENT: pline("%s %s consumed!", Yname2(obj), many ? "are" : "is"); break; case OBJ_FLOOR: /* - You see some wax candles consumed! - You see a wax candle consumed! - */ + You see some wax candles consumed! + You see a wax candle consumed! + */ You_see("%s%s consumed!", many ? "some " : "", many ? xname(obj) : an(xname(obj))); need_newsym = TRUE; @@ -1192,6 +1202,7 @@ long timeout; if (menorah) { obj->spe = 0; + obj->owt = weight(obj); } else { if (carried(obj)) { useupall(obj); @@ -1205,7 +1216,7 @@ long timeout; } obj = (struct obj *) 0; } - break; + break; /* case [age ==] 0 */ default: /* @@ -1218,8 +1229,7 @@ long timeout; if (obj && obj->age) begin_burn(obj, TRUE); - - break; + break; /* case [otyp ==] candelabrum|tallow_candle|wax_candle */ default: impossible("burn_object: unexpeced obj %s", xname(obj)); @@ -1227,6 +1237,8 @@ long timeout; } if (need_newsym) newsym(x, y); + if (need_invupdate) + update_inventory(); } /* diff --git a/src/trap.c b/src/trap.c index d6b3f8de9..91584da94 100644 --- a/src/trap.c +++ b/src/trap.c @@ -844,7 +844,7 @@ register struct trap *trap; unsigned trflags; { register int ttype = trap->ttyp; - register struct obj *otmp; + struct obj *otmp; boolean already_seen = trap->tseen, forcetrap = (trflags & FORCETRAP) != 0, webmsgok = (trflags & NOWEBMSG) == 0, @@ -915,8 +915,9 @@ unsigned trflags; otmp->opoisoned = 0; if (u.usteed && !rn2(2) && steedintrap(trap, otmp)) { /* nothing */ ; - } else if (thitu(8, dmgval(otmp, &youmonst), otmp, "arrow")) { - obfree(otmp, (struct obj *) 0); + } else if (thitu(8, dmgval(otmp, &youmonst), &otmp, "arrow")) { + if (otmp) + obfree(otmp, (struct obj *) 0); } else { place_object(otmp, u.ux, u.uy); if (!Blind) @@ -944,13 +945,15 @@ unsigned trflags; oldumort = u.umortality; if (u.usteed && !rn2(2) && steedintrap(trap, otmp)) { /* nothing */ ; - } else if (thitu(7, dmgval(otmp, &youmonst), otmp, "little dart")) { - if (otmp->opoisoned) - poisoned("dart", A_CON, "little dart", - /* if damage triggered life-saving, - poison is limited to attrib loss */ - (u.umortality > oldumort) ? 0 : 10, TRUE); - obfree(otmp, (struct obj *) 0); + } else if (thitu(7, dmgval(otmp, &youmonst), &otmp, "little dart")) { + if (otmp) { + if (otmp->opoisoned) + poisoned("dart", A_CON, "little dart", + /* if damage triggered life-saving, + poison is limited to attrib loss */ + (u.umortality > oldumort) ? 0 : 10, TRUE); + obfree(otmp, (struct obj *) 0); + } } else { place_object(otmp, u.ux, u.uy); if (!Blind) @@ -1802,7 +1805,7 @@ int style; if (multi) nomul(0); if (thitu(9 + singleobj->spe, dmgval(singleobj, &youmonst), - singleobj, (char *) 0)) + &singleobj, (char *) 0)) stop_occupation(); } if (style == ROLL) { diff --git a/src/uhitm.c b/src/uhitm.c index 81a71a406..0fcf8445c 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -5,11 +5,12 @@ #include "hack.h" STATIC_DCL boolean FDECL(known_hitum, (struct monst *, struct obj *, int *, - int, int, struct attack *)); + int, int, struct attack *, int)); STATIC_DCL boolean FDECL(theft_petrifies, (struct obj *)); STATIC_DCL void FDECL(steal_it, (struct monst *, struct attack *)); STATIC_DCL boolean FDECL(hitum, (struct monst *, struct attack *)); -STATIC_DCL boolean FDECL(hmon_hitmon, (struct monst *, struct obj *, int)); +STATIC_DCL boolean FDECL(hmon_hitmon, (struct monst *, struct obj *, int, + int)); STATIC_DCL int FDECL(joust, (struct monst *, struct obj *)); STATIC_DCL void NDECL(demonpet); STATIC_DCL boolean FDECL(m_slips_free, (struct monst * mtmp, @@ -23,8 +24,7 @@ STATIC_DCL void FDECL(nohandglow, (struct monst *)); STATIC_DCL boolean FDECL(shade_aware, (struct obj *)); extern boolean notonhead; /* for long worms */ -/* The below might become a parameter instead if we use it a lot */ -static int dieroll; + /* Used to flag attacks caused by Stormbringer's maliciousness. */ static boolean override_confirmation = FALSE; @@ -434,12 +434,13 @@ atk_done: /* really hit target monster; returns TRUE if it still lives */ STATIC_OVL boolean -known_hitum(mon, weapon, mhit, rollneeded, armorpenalty, uattk) +known_hitum(mon, weapon, mhit, rollneeded, armorpenalty, uattk, dieroll) register struct monst *mon; struct obj *weapon; int *mhit; int rollneeded, armorpenalty; /* for monks */ struct attack *uattk; +int dieroll; { register boolean malive = TRUE; @@ -463,7 +464,7 @@ struct attack *uattk; /* we hit the monster; be careful: it might die or be knocked into a different location */ notonhead = (mon->mx != x || mon->my != y); - malive = hmon(mon, weapon, HMON_MELEE); + malive = hmon(mon, weapon, HMON_MELEE, dieroll); if (malive) { /* monster still alive */ if (!rn2(25) && mon->mhp < mon->mhpmax / 2 @@ -499,19 +500,22 @@ struct attack *uattk; int armorpenalty, attknum = 0, x = u.ux + u.dx, y = u.uy + u.dy, tmp = find_roll_to_hit(mon, uattk->aatyp, uwep, &attknum, &armorpenalty); - int mhit = (tmp > (dieroll = rnd(20)) || u.uswallow); + int dieroll = rnd(20); + int mhit = (tmp > dieroll || u.uswallow); if (tmp > dieroll) exercise(A_DEX, TRUE); - malive = known_hitum(mon, uwep, &mhit, tmp, armorpenalty, uattk); + malive = known_hitum(mon, uwep, &mhit, tmp, armorpenalty, uattk, dieroll); /* second attack for two-weapon combat; won't occur if Stormbringer overrode confirmation (assumes Stormbringer is primary weapon) or if the monster was killed or knocked to different location */ if (u.twoweap && !override_confirmation && malive && m_at(x, y) == mon) { tmp = find_roll_to_hit(mon, uattk->aatyp, uswapwep, &attknum, &armorpenalty); - mhit = (tmp > (dieroll = rnd(20)) || u.uswallow); - malive = known_hitum(mon, uswapwep, &mhit, tmp, armorpenalty, uattk); + dieroll = rnd(20); + mhit = (tmp > dieroll || u.uswallow); + malive = known_hitum(mon, uswapwep, &mhit, tmp, armorpenalty, uattk, + dieroll); } if (wepbefore && !uwep) wep_was_destroyed = TRUE; @@ -521,16 +525,17 @@ struct attack *uattk; /* general "damage monster" routine; return True if mon still alive */ boolean -hmon(mon, obj, thrown) +hmon(mon, obj, thrown, dieroll) struct monst *mon; struct obj *obj; int thrown; /* HMON_xxx (0 => hand-to-hand, other => ranged) */ +int dieroll; { boolean result, anger_guards; anger_guards = (mon->mpeaceful && (mon->ispriest || mon->isshk || is_watch(mon->data))); - result = hmon_hitmon(mon, obj, thrown); + result = hmon_hitmon(mon, obj, thrown, dieroll); if (mon->ispriest && !rn2(2)) ghod_hitsu(mon); if (anger_guards) @@ -540,10 +545,11 @@ int thrown; /* HMON_xxx (0 => hand-to-hand, other => ranged) */ /* guts of hmon() */ STATIC_OVL boolean -hmon_hitmon(mon, obj, thrown) +hmon_hitmon(mon, obj, thrown, dieroll) struct monst *mon; struct obj *obj; int thrown; /* HMON_xxx (0 => hand-to-hand, other => ranged) */ +int dieroll; { int tmp; struct permonst *mdat = mon->data; @@ -731,7 +737,8 @@ int thrown; /* HMON_xxx (0 => hand-to-hand, other => ranged) */ else setuwep((struct obj *) 0); freeinv(obj); - potionhit(mon, obj, TRUE); + potionhit(mon, obj, + hand_to_hand ? POTHIT_HERO_BASH : POTHIT_HERO_THROW); if (mon->mhp <= 0) return FALSE; /* killed */ hittxt = TRUE; @@ -2114,6 +2121,7 @@ register struct monst *mon; struct obj *weapon; boolean altwep = FALSE, weapon_used = FALSE; int i, tmp, armorpenalty, sum[NATTK], nsum = 0, dhit = 0, attknum = 0; + int dieroll; for (i = 0; i < NATTK; i++) { sum[i] = 0; @@ -2137,9 +2145,11 @@ register struct monst *mon; altwep = !altwep; /* toggle for next attack */ tmp = find_roll_to_hit(mon, AT_WEAP, weapon, &attknum, &armorpenalty); - dhit = (tmp > (dieroll = rnd(20)) || u.uswallow); + dieroll = rnd(20); + dhit = (tmp > dieroll || u.uswallow); /* Enemy dead, before any special abilities used */ - if (!known_hitum(mon, weapon, &dhit, tmp, armorpenalty, mattk)) { + if (!known_hitum(mon, weapon, &dhit, tmp, armorpenalty, mattk, + dieroll)) { sum[i] = 2; break; } else @@ -2168,7 +2178,8 @@ register struct monst *mon; case AT_TENT: tmp = find_roll_to_hit(mon, mattk->aatyp, (struct obj *) 0, &attknum, &armorpenalty); - dhit = (tmp > (dieroll = rnd(20)) || u.uswallow); + dieroll = rnd(20); + dhit = (tmp > dieroll || u.uswallow); if (dhit) { int compat; diff --git a/src/windows.c b/src/windows.c index a23ec95c7..7a62582e6 100644 --- a/src/windows.c +++ b/src/windows.c @@ -474,9 +474,7 @@ static short FDECL(hup_set_font_name, (winid, char *)); #endif static char *NDECL(hup_get_color_string); #endif /* CHANGE_COLOR */ -#ifdef STATUS_VIA_WINDOWPORT -static void FDECL(hup_status_update, (int, genericptr_t, int, int)); -#endif +static void FDECL(hup_status_update, (int, genericptr_t, int, int, int, unsigned long *)); static int NDECL(hup_int_ndecl); static void NDECL(hup_void_ndecl); @@ -526,14 +524,9 @@ static struct window_procs hup_procs = { hup_void_ndecl, /* end_screen */ hup_outrip, genl_preference_update, genl_getmsghistory, genl_putmsghistory, -#ifdef STATUS_VIA_WINDOWPORT hup_void_ndecl, /* status_init */ hup_void_ndecl, /* status_finish */ genl_status_enablefield, hup_status_update, -#ifdef STATUS_HILITES - genl_status_threshold, -#endif -#endif /* STATUS_VIA_WINDOWPORT */ genl_can_suspend_no, }; @@ -762,17 +755,17 @@ hup_get_color_string(VOID_ARGS) } #endif /* CHANGE_COLOR */ -#ifdef STATUS_VIA_WINDOWPORT /*ARGSUSED*/ static void -hup_status_update(idx, ptr, chg, percent) +hup_status_update(idx, ptr, chg, pc, color, colormasks) int idx UNUSED; genericptr_t ptr UNUSED; -int chg UNUSED, percent UNUSED; +int chg UNUSED, pc UNUSED, color UNUSED; +unsigned long *colormasks UNUSED; + { return; } -#endif /* STATUS_VIA_WINDOWPORT */ /* * Non-specific stubs. @@ -816,7 +809,6 @@ const char *string UNUSED; #endif /* HANGUPHANDLING */ -#ifdef STATUS_VIA_WINDOWPORT /****************************************************************************/ /* genl backward compat stuff */ @@ -871,10 +863,11 @@ boolean enable; /* call once for each field, then call with BL_FLUSH to output the result */ void -genl_status_update(idx, ptr, chg, percent) +genl_status_update(idx, ptr, chg, percent, color, colormasks) int idx; genericptr_t ptr; -int chg UNUSED, percent UNUSED; +int chg UNUSED, percent UNUSED, color UNUSED; +unsigned long *colormasks UNUSED; { char newbot1[MAXCO], newbot2[MAXCO]; long cond, *condptr = (long *) ptr; @@ -912,12 +905,17 @@ int chg UNUSED, percent UNUSED; BL_LEVELDESC, BL_GOLD, BL_XP, BL_EXP, BL_HD, BL_TIME, BL_FLUSH }, }; + /* in case interface is using genl_status_update() but has not + specified WC2_FLUSH_STATUS (status_update() for field values + is buffered so final BL_FLUSH is needed to produce output) */ + windowprocs.wincap2 |= WC2_FLUSH_STATUS; + if (idx != BL_FLUSH) { if (!status_activefields[idx]) return; switch (idx) { case BL_CONDITION: - cond = *condptr; + cond = condptr ? *condptr : 0L; nb = status_vals[idx]; *nb = '\0'; if (cond & BL_MASK_STONE) @@ -949,7 +947,8 @@ int chg UNUSED, percent UNUSED; break; default: Sprintf(status_vals[idx], - status_fieldfmt[idx] ? status_fieldfmt[idx] : "%s", text); + status_fieldfmt[idx] ? status_fieldfmt[idx] : "%s", + text ? text : ""); break; } return; /* processed one field other than BL_FLUSH */ @@ -1035,18 +1034,6 @@ int chg UNUSED, percent UNUSED; putmixed(WIN_STATUS, 0, newbot2); /* putmixed() due to GOLD glyph */ } -#ifdef STATUS_HILITES -void -genl_status_threshold(fldidx, thresholdtype, threshold, behavior, under, over) -int fldidx UNUSED, thresholdtype UNUSED; -anything threshold UNUSED; -int behavior UNUSED, under UNUSED, over UNUSED; -{ - return; -} -#endif /* STATUS_HILITES */ -#endif /* STATUS_VIA_WINDOWPORT */ - STATIC_VAR struct window_procs dumplog_windowprocs_backup; STATIC_VAR FILE *dumplog_file; diff --git a/src/zap.c b/src/zap.c index add78a0de..6d82ab48e 100644 --- a/src/zap.c +++ b/src/zap.c @@ -3429,7 +3429,7 @@ int dx, dy; if (bhitpos.x == u.ux && bhitpos.y == u.uy) { /* ct == 9 */ if (Fumbling || rn2(20) >= ACURR(A_DEX)) { /* we hit ourselves */ - (void) thitu(10 + obj->spe, dmgval(obj, &youmonst), obj, + (void) thitu(10 + obj->spe, dmgval(obj, &youmonst), &obj, "boomerang"); endmultishot(TRUE); break; diff --git a/sys/amiga/winami.c b/sys/amiga/winami.c index 58d4165da..1819ccde3 100644 --- a/sys/amiga/winami.c +++ b/sys/amiga/winami.c @@ -51,13 +51,8 @@ struct window_procs amii_procs = { /* other defs that really should go away (they're tty specific) */ amii_delay_output, amii_delay_output, amii_outrip, genl_preference_update, genl_getmsghistory, genl_putmsghistory, -#ifdef STATUS_VIA_WINDOWPORT genl_status_init, genl_status_finish, genl_status_enablefield, genl_status_update, -#ifdef STATUS_HILITES - genl_status_threshold, -#endif -#endif genl_can_suspend_yes, }; @@ -88,13 +83,8 @@ struct window_procs amiv_procs = { /* other defs that really should go away (they're tty specific) */ amii_delay_output, amii_delay_output, amii_outrip, genl_preference_update, genl_getmsghistory, genl_putmsghistory, -#ifdef STATUS_VIA_WINDOWPORT genl_status_init, genl_status_finish, genl_status_enablefield, genl_status_update, -#ifdef STATUS_HILITES - genl_status_threshold, -#endif -#endif genl_can_suspend_yes, }; diff --git a/sys/wince/mswproc.c b/sys/wince/mswproc.c index 500306875..7137a062a 100644 --- a/sys/wince/mswproc.c +++ b/sys/wince/mswproc.c @@ -73,13 +73,8 @@ struct window_procs mswin_procs = { /* other defs that really should go away (they're tty specific) */ mswin_start_screen, mswin_end_screen, mswin_outrip, mswin_preference_update, genl_getmsghistory, genl_putmsghistory, -#ifdef STATUS_VIA_WINDOWPORT genl_status_init, genl_status_finish, genl_status_enablefield, genl_status_update, -#ifdef STATUS_HILITES - genl_status_threshold, -#endif -#endif genl_can_suspend_no, }; diff --git a/sys/winnt/defaults.nh b/sys/winnt/defaults.nh index 7dfef2713..ee1b479f9 100644 --- a/sys/winnt/defaults.nh +++ b/sys/winnt/defaults.nh @@ -66,9 +66,6 @@ MENUCOLOR=" cursed .* (being worn)" = orange&underline -# Turn off all status hilites. -#OPTIONS=!statushilites -# # General options. You might also set "silent" so as not to attract # the boss's attention. # @@ -185,3 +182,45 @@ OPTIONS=vary_msgcount:1 #OPTIONS=palette:bright cyan-0-255-255 #OPTIONS=palette:white-255-255-255 + + +# Status hilites +OPTIONS=statushilites +# HP +OPTIONS=hitpointbar +OPTIONS=hilite_status:hitpoints/100%/gray&normal +OPTIONS=hilite_status:hitpoints/<100%/green&normal +OPTIONS=hilite_status:hitpoints/<66%/yellow&normal +OPTIONS=hilite_status:hitpoints/<50%/orange&normal +OPTIONS=hilite_status:hitpoints/<33%/red&bold +OPTIONS=hilite_status:hitpoints/<15%/red&inverse + +## Pw +OPTIONS=hilite_status:power/100%/gray&normal +OPTIONS=hilite_status:power/<100%/green&normal +OPTIONS=hilite_status:power/<66%/yellow&normal +OPTIONS=hilite_status:power/<50%/orange&normal +OPTIONS=hilite_status:power/<33%/red&bold + +## Carry +OPTIONS=hilite_status:cap/burdened/yellow/stressed/orange/strained/red&bold/overtaxed/red&inverse/overloaded/red&inverse&blink + +## Hunger +OPTIONS=hilite_status:hunger/satiated/yellow/hungry/orange/weak/red&bold/fainting/red&inverse/fainted/red&inverse&blink + +## Mental +OPTIONS=hilite_status:condition/hallu/yellow +OPTIONS=hilite_status:condition/conf/orange +OPTIONS=hilite_status:condition/stun/red&bold + +## Health +OPTIONS=hilite_status:condition/termill/red&inverse +OPTIONS=hilite_status:condition/foodpois/red&inverse +OPTIONS=hilite_status:condition/slime/red&inverse + +# gold +OPTIONS=hilite_status:gold/up/yellow/down/brown + +# St, Dx, Co, In, Wi, Ch +OPTIONS=hilite_status:characteristics/up/green/down/red + diff --git a/sys/winnt/nttty.c b/sys/winnt/nttty.c index 7a77e4612..64bea743c 100644 --- a/sys/winnt/nttty.c +++ b/sys/winnt/nttty.c @@ -42,9 +42,7 @@ void FDECL(cmov, (int, int)); void FDECL(nocmov, (int, int)); int FDECL(process_keystroke, (INPUT_RECORD *, boolean *, BOOLEAN_P numberpad, int portdebug)); -#ifdef TEXTCOLOR static void NDECL(init_ttycolor); -#endif static void NDECL(really_move_cursor); /* Win32 Console handles for input and output */ @@ -111,8 +109,10 @@ static DWORD ccount, acount; #ifndef CLR_MAX #define CLR_MAX 16 #endif + int ttycolors[CLR_MAX]; int ttycolors_inv[CLR_MAX]; + #define MAX_OVERRIDES 256 unsigned char key_overrides[MAX_OVERRIDES]; static char nullstr[] = ""; @@ -158,7 +158,7 @@ gettty() init_ttycolor(); #else for (k = 0; k < CLR_MAX; ++k) - ttycolors[k] = 7; + ttycolors[k] = NO_COLOR; #endif } @@ -692,6 +692,7 @@ tty_delay_output() static void init_ttycolor() { +#ifdef TEXTCOLOR ttycolors[CLR_BLACK] = FOREGROUND_INTENSITY; /* fix by Quietust */ ttycolors[CLR_RED] = FOREGROUND_RED; ttycolors[CLR_GREEN] = FOREGROUND_GREEN; @@ -728,6 +729,15 @@ init_ttycolor() ttycolors_inv[CLR_BRIGHT_CYAN] = BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY; ttycolors_inv[CLR_WHITE] = BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED | BACKGROUND_INTENSITY; +#else + int k; + ttycolors[0] = FOREGROUND_INTENSITY; + ttycolors_inv[0] = BACKGROUND_INTENSITY; + for (k = 1; k < SIZE(ttycolors); ++k) { + ttycolors[k] = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED; + ttycolors_inv[k] = BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED; + } +#endif init_ttycolor_completed = TRUE; } #endif /* TEXTCOLOR */ @@ -739,7 +749,7 @@ has_color(int color) if ((color >= 0) && (color < CLR_MAX)) return 1; #else - if ((color == CLR_BLACK) || (color == CLR_WHITE)) + if ((color == CLR_BLACK) || (color == CLR_WHITE) || (color == NO_COLOR)) return 1; #endif else @@ -794,7 +804,7 @@ term_start_color(int color) console.current_nhcolor = color; } else #endif - console.current_nhcolor = color; + console.current_nhcolor = NO_COLOR; } void diff --git a/util/makedefs.c b/util/makedefs.c index c2a046a94..97d7f10a9 100644 --- a/util/makedefs.c +++ b/util/makedefs.c @@ -1515,14 +1515,11 @@ static const char *build_opts[] = { #ifdef SHELL "shell command", #endif -#ifdef STATUS_VIA_WINDOWPORT -# ifdef STATUS_HILITES - "status via windowport with highlighting", -# else - "status via windowport without highlighting", -# endif -#else "traditional status display", +#ifdef STATUS_HILITES + "status via windowport with highlighting", +#else + "status via windowport without highlighting", #endif #ifdef SUSPEND "suspend command", diff --git a/win/Qt/qt_win.cpp b/win/Qt/qt_win.cpp index 52c925ae6..84ac6a82c 100644 --- a/win/Qt/qt_win.cpp +++ b/win/Qt/qt_win.cpp @@ -5286,15 +5286,11 @@ struct window_procs Qt_procs = { genl_preference_update, genl_getmsghistory, genl_putmsghistory, -#ifdef STATUS_VIA_WINDOWPORT genl_status_init, genl_status_finish, genl_status_enablefield, genl_status_update, -# ifdef STATUS_HILITES - genl_status_threshold, -# endif -#endif + genl_can_suspend_yes, }; extern "C" void play_usersound(const char* filename, int volume) diff --git a/win/X11/winX.c b/win/X11/winX.c index 25229541a..78e304b1f 100644 --- a/win/X11/winX.c +++ b/win/X11/winX.c @@ -125,13 +125,8 @@ struct window_procs X11_procs = { genl_outrip, #endif X11_preference_update, genl_getmsghistory, genl_putmsghistory, -#ifdef STATUS_VIA_WINDOWPORT genl_status_init, genl_status_finish, genl_status_enablefield, genl_status_update, -#ifdef STATUS_HILITES - genl_status_threshold, -#endif -#endif genl_can_suspend_no, /* XXX may not always be correct */ }; diff --git a/win/chain/wc_chainin.c b/win/chain/wc_chainin.c index 66362bd0b..bd0a11827 100644 --- a/win/chain/wc_chainin.c +++ b/win/chain/wc_chainin.c @@ -462,7 +462,6 @@ boolean is_restoring; (*cibase->nprocs->win_putmsghistory)(cibase->ndata, msg, is_restoring); } -#ifdef STATUS_VIA_WINDOWPORT void chainin_status_init() { @@ -487,29 +486,15 @@ boolean enable; } void -chainin_status_update(idx, ptr, chg, percent) -int idx, chg, percent; +chainin_status_update(idx, ptr, chg, percent, color, colormasks) +int idx, chg, percent, color; genericptr_t ptr; +unsigned long *colormasks; { (*cibase->nprocs->win_status_update)(cibase->ndata, idx, ptr, chg, - percent); + percent, color, colormasks); } -#ifdef STATUS_HILITES -void -chainin_status_threshold(fldidx, thresholdtype, threshold, behavior, under, - over) -int fldidx, thresholdtype; -int behavior, under, over; -anything threshold; -{ - (*cibase->nprocs->win_status_threshold)(cibase->ndata, fldidx, - thresholdtype, threshold, - behavior, under, over); -} -#endif -#endif - boolean chainin_can_suspend() { @@ -561,12 +546,7 @@ struct window_procs chainin_procs = { chainin_outrip, chainin_preference_update, chainin_getmsghistory, chainin_putmsghistory, -#ifdef STATUS_VIA_WINDOWPORT chainin_status_init, chainin_status_finish, chainin_status_enablefield, chainin_status_update, -#ifdef STATUS_HILITES - chainin_status_threshold, -#endif -#endif chainin_can_suspend, }; diff --git a/win/chain/wc_chainout.c b/win/chain/wc_chainout.c index 85b265117..593bd6cd8 100644 --- a/win/chain/wc_chainout.c +++ b/win/chain/wc_chainout.c @@ -586,7 +586,6 @@ boolean is_restoring; (*tdp->nprocs->win_putmsghistory)(msg, is_restoring); } -#ifdef STATUS_VIA_WINDOWPORT void chainout_status_init(vp) void *vp; @@ -619,33 +618,17 @@ boolean enable; } void -chainout_status_update(vp, idx, ptr, chg, percent) +chainout_status_update(vp, idx, ptr, chg, percent, color, colormasks) void *vp; int idx, chg, percent; genericptr_t ptr; +unsigned long *colormasks; { struct chainout_data *tdp = vp; - (*tdp->nprocs->win_status_update)(idx, ptr, chg, percent); + (*tdp->nprocs->win_status_update)(idx, ptr, chg, percent, color, colormasks); } -#ifdef STATUS_HILITES -void -chainout_status_threshold(vp, fldidx, thresholdtype, threshold, behavior, - under, over) -void *vp; -int fldidx, thresholdtype; -int behavior, under, over; -anything threshold; -{ - struct chainout_data *tdp = vp; - - (*tdp->nprocs->win_status_threshold)(fldidx, thresholdtype, threshold, - behavior, under, over); -} -#endif -#endif - boolean chainout_can_suspend(vp) void *vp; @@ -700,12 +683,7 @@ struct chain_procs chainout_procs = { chainout_outrip, chainout_preference_update, chainout_getmsghistory, chainout_putmsghistory, -#ifdef STATUS_VIA_WINDOWPORT chainout_status_init, chainout_status_finish, chainout_status_enablefield, chainout_status_update, -#ifdef STATUS_HILITES - chainout_status_threshold, -#endif -#endif chainout_can_suspend, }; diff --git a/win/chain/wc_trace.c b/win/chain/wc_trace.c index b3d21e7ea..60877623d 100644 --- a/win/chain/wc_trace.c +++ b/win/chain/wc_trace.c @@ -1027,7 +1027,6 @@ boolean is_restoring; POST; } -#ifdef STATUS_VIA_WINDOWPORT void trace_status_init(vp) void *vp; @@ -1084,10 +1083,11 @@ boolean enable; } void -trace_status_update(vp, idx, ptr, chg, percent) +trace_status_update(vp, idx, ptr, chg, color, colormasks) void *vp; int idx, chg, percent; genericptr_t ptr; +unsigned long *colormasks; { struct trace_data *tdp = vp; @@ -1095,33 +1095,10 @@ genericptr_t ptr; ptr, chg, percent); PRE; - (*tdp->nprocs->win_status_update)(tdp->ndata, idx, ptr, chg, percent); + (*tdp->nprocs->win_status_update)(tdp->ndata, idx, ptr, chg, color colormasks); POST; } -#ifdef STATUS_HILITES -void -trace_status_threshold(vp, fldidx, thresholdtype, threshold, behavior, under, - over) -void *vp; -int fldidx, thresholdtype; -int behavior, under, over; -anything threshold; -{ - struct trace_data *tdp = vp; - - /* XXX how do we print an anything? We don't. */ - fprintf(wc_tracelogf, "%sstatus_threshold(%d, %d, -, %d, %d, %d)\n", - INDENT, fldidx, thresholdtype, behavior, under, over); - - PRE; - (*tdp->nprocs->win_status_threshold)(tdp->ndata, fldidx, thresholdtype, - threshold, behavior, under, over); - POST; -} -#endif -#endif - boolean trace_can_suspend(vp) void *vp; @@ -1179,12 +1156,7 @@ struct chain_procs trace_procs = { trace_outrip, trace_preference_update, trace_getmsghistory, trace_putmsghistory, -#ifdef STATUS_VIA_WINDOWPORT trace_status_init, trace_status_finish, trace_status_enablefield, trace_status_update, -#ifdef STATUS_HILITES - trace_status_threshold, -#endif -#endif trace_can_suspend, }; diff --git a/win/gem/wingem.c b/win/gem/wingem.c index 72daffd14..8cd33ca2a 100644 --- a/win/gem/wingem.c +++ b/win/gem/wingem.c @@ -70,13 +70,8 @@ struct window_procs Gem_procs = { /* other defs that really should go away (they're tty specific) */ Gem_start_screen, Gem_end_screen, Gem_outrip, Gem_preference_update, genl_getmsghistory, genl_putmsghistory -#ifdef STATUS_VIA_WINDOWPORT genl_status_init, genl_status_finish, genl_status_enablefield, genl_status_update, -#ifdef STATUS_HILITES - genl_status_threshold, -#endif -#endif genl_can_suspend_no, }; diff --git a/win/gnome/gnbind.c b/win/gnome/gnbind.c index afeef0d1c..bc3521497 100644 --- a/win/gnome/gnbind.c +++ b/win/gnome/gnbind.c @@ -48,13 +48,8 @@ struct window_procs Gnome_procs = { /* other defs that really should go away (they're tty specific) */ gnome_start_screen, gnome_end_screen, gnome_outrip, genl_preference_update, genl_getmsghistory, genl_putmsghistory, -#ifdef STATUS_VIA_WINDOWPORT genl_status_init, genl_status_finish, genl_status_enablefield, genl_status_update, -#ifdef STATUS_HILITES - genl_status_threshold, -#endif -#endif genl_can_suspend_yes, }; diff --git a/win/tty/wintty.c b/win/tty/wintty.c index 7e504d669..2eb84b0cc 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -62,6 +62,9 @@ struct window_procs tty_procs = { WC_COLOR | WC_HILITE_PET | WC_INVERSE | WC_EIGHT_BIT_IN, #if defined(SELECTSAVED) WC2_SELECTSAVED | +#endif +#if defined(STATUS_HILITES) + WC2_HITPOINTBAR | WC2_FLUSH_STATUS | #endif WC2_DARKGRAY, tty_init_nhwindows, tty_player_selection, tty_askname, tty_get_nh_event, @@ -96,13 +99,12 @@ struct window_procs tty_procs = { genl_preference_update, #endif tty_getmsghistory, tty_putmsghistory, -#ifdef STATUS_VIA_WINDOWPORT tty_status_init, genl_status_finish, genl_status_enablefield, - tty_status_update, #ifdef STATUS_HILITES - tty_status_threshold, -#endif + tty_status_update, +#else + genl_status_update, #endif genl_can_suspend_yes, }; @@ -1293,9 +1295,7 @@ const char *str; } } WIN_MAP = WIN_MESSAGE = WIN_INVEN = WIN_ERR; /* these are all gone now */ -#ifndef STATUS_VIA_WINDOWPORT WIN_STATUS = WIN_ERR; -#endif #ifdef FREE_ALL_MEMORY if (BASE_WINDOW != WIN_ERR && wins[BASE_WINDOW]) { free_window_info(wins[BASE_WINDOW], TRUE); @@ -2520,14 +2520,32 @@ const char *str; nb = str; for (i = cw->curx + 1, n0 = cw->cols; i < n0; i++, nb++) { if (!*nb) { +#ifndef STATUS_HILITES if (*ob || context.botlx) { +#else + /* STATUS_HILITES will call cl_end() when finished + * its sequence of putstr's. We don't want to call + * cl_end() with each putstr() which may cause flashing + * in the Windows port + */ + if (context.botlx) { +#endif /* last char printed may be in middle of line */ tty_curs(WIN_STATUS, i, cw->cury); cl_end(); } break; } +#ifdef STATUS_HILITES + /* Don't optimize the putsym away, in case it happens + to be the same character but different color/attr. + We don't optimize on iflags.use_status_hilites either, + in case old chars were written with highlighting and + that option has just now been toggled off. [We could + do better by tracking color/attr more closely.] */ +#else if (*ob != *nb) +#endif tty_putsym(WIN_STATUS, i, cw->cury, *nb); if (*ob) ob++; @@ -2535,13 +2553,9 @@ 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 */ -#ifdef STATUS_VIA_WINDOWPORT - if (!iflags.use_status_hilites) { -#endif - cw->cury = (cw->cury + 1) % 2; - cw->curx = 0; -#ifdef STATUS_VIA_WINDOWPORT - } +#ifndef STATUS_HILITES + cw->cury = (cw->cury + 1) % 2; + cw->curx = 0; #endif break; case NHW_MAP: @@ -3402,40 +3416,26 @@ char *posbar; } #endif -#ifdef STATUS_VIA_WINDOWPORT /* * The following data structures come from the genl_ routines in * src/windows.c and as such are considered to be on the window-port * "side" of things, rather than the NetHack-core "side" of things. */ -extern const char *status_fieldnm[MAXBLSTATS]; extern const char *status_fieldfmt[MAXBLSTATS]; extern char *status_vals[MAXBLSTATS]; extern boolean status_activefields[MAXBLSTATS]; extern winid WIN_STATUS; #ifdef STATUS_HILITES -typedef struct hilite_data_struct { - int thresholdtype; - anything threshold; - int behavior; - int under; - int over; -} hilite_data_t; -static hilite_data_t tty_status_hilites[MAXBLSTATS]; +static long tty_condition_bits; static int tty_status_colors[MAXBLSTATS]; +int hpbar_percent, hpbar_color; +#endif /* STATUS_HILITES */ -struct color_option { - int color; - int attr_bits; -}; +static int FDECL(condcolor, (long, unsigned long *)); +static int FDECL(condattr, (long, unsigned long *)); -static void FDECL(start_color_option, (struct color_option)); -static void FDECL(end_color_option, (struct color_option)); -static void FDECL(apply_color_option, (struct color_option, const char *)); -static void FDECL(add_colored_text, (const char *, char *)); -#endif void tty_status_init() @@ -3445,15 +3445,11 @@ tty_status_init() /* let genl_status_init do most of the initialization */ genl_status_init(); - for (i = 0; i < MAXBLSTATS; ++i) { #ifdef STATUS_HILITES + for (i = 0; i < MAXBLSTATS; ++i) { tty_status_colors[i] = NO_COLOR; /* no color */ - tty_status_hilites[i].thresholdtype = 0; - tty_status_hilites[i].behavior = BL_TH_NONE; - tty_status_hilites[i].under = BL_HILITE_NONE; - tty_status_hilites[i].over = BL_HILITE_NONE; -#endif /* STATUS_HILITES */ } +#endif /* STATUS_HILITES */ } /* @@ -3490,22 +3486,102 @@ tty_status_init() * symbol for GOLD "\GXXXXNNNN:nnn". If the window port needs to use * the textual gold amount without the leading "$:" the port will * have to skip past ':' in the passed "ptr" for the BL_GOLD case. + * -- color is an unsigned int. + * color_index = color & 0x00FF; CLR_* value + * attribute = color & 0xFF00 >> 8; BL_* values + * This holds the color and attribute that the field should + * be displayed in. + * This is relevant for everything except BL_CONDITION fldindex. + * If fldindex is BL_CONDITION, this parameter should be ignored, + * as condition hilighting is done via the next colormasks + * parameter instead. + * + * -- colormasks - pointer to cond_hilites[] array of colormasks. + * Only relevant for BL_CONDITION fldindex. The window port + * should ignore this parameter for other fldindex values. + * Each condition bit must only ever appear in one of the + * CLR_ array members, but can appear in multiple HL_ATTCLR_ + * offsets (because more than one attribute can co-exist). + * See doc/window.doc for more details. */ + + +/* new approach through status_update() only */ +#define Begin_Attr(m) \ + if (m) { \ + if ((m) & HL_BOLD) \ + term_start_attr(ATR_BOLD); \ + if ((m) & HL_INVERSE) \ + term_start_attr(ATR_INVERSE); \ + if ((m) & HL_ULINE) \ + term_start_attr(ATR_ULINE); \ + if ((m) & HL_BLINK) \ + term_start_attr(ATR_BLINK); \ + if ((m) & HL_DIM) \ + term_start_attr(ATR_DIM); \ + } + +#define End_Attr(m) \ + if (m) { \ + if ((m) & HL_DIM) \ + term_end_attr(ATR_DIM); \ + if ((m) & HL_BLINK) \ + term_end_attr(ATR_BLINK); \ + if ((m) & HL_ULINE) \ + term_end_attr(ATR_ULINE); \ + if ((m) & HL_INVERSE) \ + term_end_attr(ATR_INVERSE); \ + if ((m) & HL_BOLD) \ + term_end_attr(ATR_BOLD); \ + } + +#ifdef STATUS_HILITES + +#ifdef TEXTCOLOR +#define MaybeDisplayCond(bm,txt) \ + if (tty_condition_bits & (bm)) { \ + putstr(WIN_STATUS, 0, " "); \ + if (iflags.hilite_delta) { \ + attrmask = condattr(bm, colormasks); \ + Begin_Attr(attrmask); \ + if ((coloridx = condcolor(bm, colormasks)) != NO_COLOR) \ + term_start_color(coloridx); \ + } \ + putstr(WIN_STATUS, 0, txt); \ + if (iflags.hilite_delta) { \ + if (coloridx != NO_COLOR) \ + term_end_color(); \ + End_Attr(attrmask); \ + } \ + } +#else +#define MaybeDisplayCond(bm,txt) \ + if (tty_condition_bits & (bm)) { \ + putstr(WIN_STATUS, 0, " "); \ + if (iflags.hilite_delta) { \ + attrmask = condattr(bm, colormasks); \ + Begin_Attr(attrmask); \ + } \ + putstr(WIN_STATUS, 0, txt); \ + if (iflags.hilite_delta) { \ + End_Attr(attrmask); \ + } \ + } +#endif + void -tty_status_update(fldidx, ptr, chg, percent) -int fldidx, chg, percent; +tty_status_update(fldidx, ptr, chg, percent, color, colormasks) +int fldidx, chg UNUSED, percent UNUSED, color; genericptr_t ptr; +unsigned long *colormasks; { - long cond, *condptr = (long *) ptr; - register int i; + long *condptr = (long *) ptr; + int i, attrmask = 0; +#ifdef TEXTCOLOR + int coloridx = NO_COLOR; +#endif char *text = (char *) ptr; - /* Mapping BL attributes to tty attributes - * BL_HILITE_NONE -1 + 3 = 2 (statusattr[2]) - * BL_HILITE_INVERSE -2 + 3 = 1 (statusattr[1]) - * BL_HILITE_BOLD -3 + 3 = 0 (statusattr[0]) - */ - long value = -1L; - static boolean beenhere = FALSE; + static boolean oncearound = FALSE; /* prevent premature partial status display */ enum statusfields fieldorder[2][15] = { { 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, @@ -3514,269 +3590,236 @@ genericptr_t ptr; BL_AC, BL_XP, BL_EXP, BL_HD, BL_TIME, BL_HUNGER, BL_CAP, BL_CONDITION, BL_FLUSH } }; -#ifdef STATUS_HILITES - static int statusattr[] = { ATR_BOLD, ATR_INVERSE, ATR_NONE }; int attridx = 0; -#else - nhUse(chg); - nhUse(percent); -#endif if (fldidx != BL_FLUSH) { if (!status_activefields[fldidx]) return; switch (fldidx) { case BL_CONDITION: - cond = *condptr; - *status_vals[fldidx] = '\0'; - if (cond & BL_MASK_STONE) - Strcat(status_vals[fldidx], " Stone"); - if (cond & BL_MASK_SLIME) - Strcat(status_vals[fldidx], " Slime"); - if (cond & BL_MASK_STRNGL) - Strcat(status_vals[fldidx], " Strngl"); - if (cond & BL_MASK_FOODPOIS) - Strcat(status_vals[fldidx], " FoodPois"); - if (cond & BL_MASK_TERMILL) - Strcat(status_vals[fldidx], " TermIll"); - if (cond & BL_MASK_BLIND) - Strcat(status_vals[fldidx], " Blind"); - if (cond & BL_MASK_DEAF) - Strcat(status_vals[fldidx], " Deaf"); - if (cond & BL_MASK_STUN) - Strcat(status_vals[fldidx], " Stun"); - if (cond & BL_MASK_CONF) - Strcat(status_vals[fldidx], " Conf"); - if (cond & BL_MASK_HALLU) - Strcat(status_vals[fldidx], " Hallu"); - if (cond & BL_MASK_LEV) - Strcat(status_vals[fldidx], " Lev"); - if (cond & BL_MASK_FLY) - Strcat(status_vals[fldidx], " Fly"); - if (cond & BL_MASK_RIDE) - Strcat(status_vals[fldidx], " Ride"); - value = cond; + tty_condition_bits = *condptr; + oncearound = TRUE; break; default: - value = atol(text); Sprintf(status_vals[fldidx], + (fldidx == BL_TITLE && iflags.wc2_hitpointbar) ? "%-30s" : status_fieldfmt[fldidx] ? status_fieldfmt[fldidx] : "%s", text); - break; - } - -#ifdef STATUS_HILITES - switch (tty_status_hilites[fldidx].behavior) { - case BL_TH_NONE: +#ifdef TEXTCOLOR + tty_status_colors[fldidx] = color; +#else tty_status_colors[fldidx] = NO_COLOR; - break; - case BL_TH_UPDOWN: - if (chg > 0) - tty_status_colors[fldidx] = tty_status_hilites[fldidx].over; - else if (chg < 0) - tty_status_colors[fldidx] = tty_status_hilites[fldidx].under; - else - tty_status_colors[fldidx] = NO_COLOR; - break; - case BL_TH_VAL_PERCENTAGE: { - int pct_th = 0; - - if (tty_status_hilites[fldidx].thresholdtype != ANY_INT) { - impossible( - "tty_status_update: unsupported percentage threshold type %d", - tty_status_hilites[fldidx].thresholdtype); - } else { - pct_th = tty_status_hilites[fldidx].threshold.a_int; - tty_status_colors[fldidx] = (percent >= pct_th) - ? tty_status_hilites[fldidx].over - : tty_status_hilites[fldidx].under; +#endif + if (iflags.wc2_hitpointbar && fldidx == BL_HP) { + hpbar_percent = percent; +#ifdef TEXTCOLOR + hpbar_color = color; +#else + hpbar_color = NO_COLOR; +#endif } break; } - case BL_TH_VAL_ABSOLUTE: { - int c = NO_COLOR; - int o = tty_status_hilites[fldidx].over; - int u = tty_status_hilites[fldidx].under; - anything *t = &tty_status_hilites[fldidx].threshold; - - switch (tty_status_hilites[fldidx].thresholdtype) { - case ANY_LONG: - c = (value >= t->a_long) ? o : u; - break; - case ANY_INT: - c = (value >= t->a_int) ? o : u; - break; - case ANY_UINT: - c = ((unsigned long) value >= t->a_uint) ? o : u; - break; - case ANY_ULONG: - c = ((unsigned long) value >= t->a_ulong) ? o : u; - break; - case ANY_MASK32: - c = (value & t->a_ulong) ? o : u; - break; - default: - impossible( - "tty_status_update: unsupported absolute threshold type %d\n", - tty_status_hilites[fldidx].thresholdtype); - break; - } - tty_status_colors[fldidx] = c; - break; - } /* case */ - } /* switch */ -#endif /* STATUS_HILITES */ } - /* For now, this version copied from the genl_ version currently - * updates everything on the display, everytime - */ - - if (!beenhere || !iflags.use_status_hilites) { - char newbot1[MAXCO], newbot2[MAXCO]; - - newbot1[0] = '\0'; - for (i = 0; fieldorder[0][i] >= 0; ++i) { - int idx1 = fieldorder[0][i]; - - if (status_activefields[idx1]) - Strcat(newbot1, status_vals[idx1]); - } - newbot2[0] = '\0'; - for (i = 0; fieldorder[1][i] >= 0; ++i) { - int idx2 = fieldorder[1][i]; - - if (status_activefields[idx2]) - Strcat(newbot2, status_vals[idx2]); - } - - curs(WIN_STATUS, 1, 0); - putstr(WIN_STATUS, 0, newbot1); - curs(WIN_STATUS, 1, 1); - putmixed(WIN_STATUS, 0, newbot2); /* putmixed() due to GOLD glyph */ - beenhere = TRUE; - return; - } + if (!oncearound) return; curs(WIN_STATUS, 1, 0); for (i = 0; fieldorder[0][i] != BL_FLUSH; ++i) { int fldidx1 = fieldorder[0][i]; - if (status_activefields[fldidx1]) { -#ifdef STATUS_HILITES - if (tty_status_colors[fldidx1] < 0 - && tty_status_colors[fldidx1] >= -3) { - /* attribute, not a color */ - attridx = tty_status_colors[fldidx1] + 3; - term_start_attr(statusattr[attridx]); - putstr(WIN_STATUS, 0, status_vals[fldidx1]); - term_end_attr(statusattr[attridx]); - } else + if (fldidx1 != BL_TITLE || !iflags.wc2_hitpointbar) { #ifdef TEXTCOLOR - if (tty_status_colors[fldidx1] != CLR_MAX) { - if (tty_status_colors[fldidx1] != NO_COLOR) - term_start_color(tty_status_colors[fldidx1]); - putstr(WIN_STATUS, 0, status_vals[fldidx1]); - if (tty_status_colors[fldidx1] != NO_COLOR) - term_end_color(); - } else + coloridx = tty_status_colors[fldidx1] & 0x00FF; #endif -#endif /* STATUS_HILITES */ - putstr(WIN_STATUS, 0, status_vals[fldidx1]); + attridx = (tty_status_colors[fldidx1] & 0xFF00) >> 8; + text = status_vals[fldidx1]; + if (iflags.hilite_delta) { + if (*text == ' ') { + putstr(WIN_STATUS, 0, " "); + text++; + } + /* multiple attributes can be in effect concurrently */ + Begin_Attr(attridx); +#ifdef TEXTCOLOR + if (coloridx != NO_COLOR && coloridx != CLR_MAX) + term_start_color(coloridx); +#endif + } + + putstr(WIN_STATUS, 0, text); + + if (iflags.hilite_delta) { +#ifdef TEXTCOLOR + if (coloridx != NO_COLOR) + term_end_color(); +#endif + End_Attr(attridx); + } + } else { + /* hitpointbar using hp percent calculation */ + int bar_pos, bar_len; + char *bar2 = (char *)0; + char bar[MAXCO], savedch; + boolean twoparts = FALSE; + + text = status_vals[fldidx1]; + bar_len = strlen(text); + if (bar_len < MAXCO-1) { + Strcpy(bar, text); + bar_pos = (bar_len * hpbar_percent) / 100; + if (bar_pos < 1 && hpbar_percent > 0) + bar_pos = 1; + if (bar_pos >= bar_len && hpbar_percent < 100) + bar_pos = bar_len - 1; + if (bar_pos > 0 && bar_pos < bar_len) { + twoparts = TRUE; + bar2 = &bar[bar_pos]; + savedch = *bar2; + *bar2 = '\0'; + } + } + if (iflags.hilite_delta && iflags.wc2_hitpointbar) { + putstr(WIN_STATUS, 0, "["); +#ifdef TEXTCOLOR + coloridx = hpbar_color & 0x00FF; + /* attridx = (hpbar_color & 0xFF00) >> 8; */ + if (coloridx != NO_COLOR) + term_start_color(coloridx); +#endif + term_start_attr(ATR_INVERSE); + putstr(WIN_STATUS, 0, bar); + term_end_attr(ATR_INVERSE); +#ifdef TEXTCOLOR + if (coloridx != NO_COLOR) + term_end_color(); +#endif + if (twoparts) { + *bar2 = savedch; + putstr(WIN_STATUS, 0, bar2); + } + putstr(WIN_STATUS, 0, "]"); + } else + putstr(WIN_STATUS, 0, text); + } } } + cl_end(); curs(WIN_STATUS, 1, 1); for (i = 0; fieldorder[1][i] != BL_FLUSH; ++i) { int fldidx2 = fieldorder[1][i]; if (status_activefields[fldidx2]) { -#ifdef STATUS_HILITES - if (tty_status_colors[fldidx2] < 0 - && tty_status_colors[fldidx2] >= -3) { - /* attribute, not a color */ - attridx = tty_status_colors[fldidx2] + 3; - term_start_attr(statusattr[attridx]); - putstr(WIN_STATUS, 0, status_vals[fldidx2]); - term_end_attr(statusattr[attridx]); - } else + if (fldidx2 != BL_CONDITION) { #ifdef TEXTCOLOR - if (tty_status_colors[fldidx2] != CLR_MAX) { - if (tty_status_colors[fldidx2] != NO_COLOR) - term_start_color(tty_status_colors[fldidx2]); + coloridx = tty_status_colors[fldidx2] & 0x00FF; +#endif + attridx = (tty_status_colors[fldidx2] & 0xFF00) >> 8; + text = status_vals[fldidx2]; + if (iflags.hilite_delta) { + if (*text == ' ') { + putstr(WIN_STATUS, 0, " "); + text++; + } + /* multiple attributes can be in effect concurrently */ + Begin_Attr(attridx); +#ifdef TEXTCOLOR + if (coloridx != NO_COLOR && coloridx != CLR_MAX) + term_start_color(coloridx); +#endif + } + if (fldidx2 == BL_GOLD) { /* putmixed() due to GOLD glyph */ - putmixed(WIN_STATUS, 0, status_vals[fldidx2]); + putmixed(WIN_STATUS, 0, text); } else { - putstr(WIN_STATUS, 0, status_vals[fldidx2]); + putstr(WIN_STATUS, 0, text); } - if (tty_status_colors[fldidx2] != NO_COLOR) - term_end_color(); - } else + + if (iflags.hilite_delta) { +#ifdef TEXTCOLOR + if (coloridx != NO_COLOR) + term_end_color(); #endif -#endif /* STATUS_HILITES */ - putstr(WIN_STATUS, 0, status_vals[fldidx2]); + End_Attr(attridx); + } + } else { + MaybeDisplayCond(BL_MASK_STONE, "Stone"); + MaybeDisplayCond(BL_MASK_SLIME, "Slime"); + MaybeDisplayCond(BL_MASK_STRNGL, "Strngl"); + MaybeDisplayCond(BL_MASK_FOODPOIS, "FoodPois"); + MaybeDisplayCond(BL_MASK_TERMILL, "TermIll"); + MaybeDisplayCond(BL_MASK_BLIND, "Blind"); + MaybeDisplayCond(BL_MASK_DEAF, "Deaf"); + MaybeDisplayCond(BL_MASK_STUN, "Stun"); + MaybeDisplayCond(BL_MASK_CONF, "Conf"); + MaybeDisplayCond(BL_MASK_HALLU, "Hallu"); + MaybeDisplayCond(BL_MASK_LEV, "Lev"); + MaybeDisplayCond(BL_MASK_FLY, "Fly"); + MaybeDisplayCond(BL_MASK_RIDE, "Ride"); + } } } + cl_end(); return; } -#ifdef STATUS_HILITES +#ifdef TEXTCOLOR /* - * status_threshold(int fldidx, int threshholdtype, anything threshold, - * int behavior, int under, int over) - * - * -- called when a hiliting preference is added, changed, or - * removed. - * -- the fldindex identifies which field is having its hiliting - * preference set. It is an integer index value from botl.h - * -- fldindex could be any one of the following from botl.h: - * BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, - * BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX, - * BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX, - * BL_LEVELDESC, BL_EXP, BL_CONDITION - * -- datatype is P_INT, P_UINT, P_LONG, or P_MASK. - * -- threshold is an "anything" union which can contain the - * datatype value. - * -- behavior is used to define how threshold is used and can - * be BL_TH_NONE, BL_TH_VAL_PERCENTAGE, BL_TH_VAL_ABSOLUTE, - * or BL_TH_UPDOWN. BL_TH_NONE means don't do anything above - * or below the threshold. BL_TH_VAL_PERCENTAGE treats the - * threshold value as a precentage of the maximum possible - * value. BL_TH_VAL_ABSOLUTE means that the threshold is an - * actual value. BL_TH_UPDOWN means that threshold is not - * used, and the two below/above hilite values indicate how - * to display something going down (under) or rising (over). - * -- under is the hilite attribute used if value is below the - * threshold. The attribute can be BL_HILITE_NONE, - * BL_HILITE_INVERSE, BL_HILITE_BOLD (-1, -2, or -3), or one - * of the color indexes of CLR_BLACK, CLR_RED, CLR_GREEN, - * CLR_BROWN, CLR_BLUE, CLR_MAGENTA, CLR_CYAN, CLR_GRAY, - * CLR_ORANGE, CLR_BRIGHT_GREEN, CLR_YELLOW, CLR_BRIGHT_BLUE, - * CLR_BRIGHT_MAGENTA, CLR_BRIGHT_CYAN, or CLR_WHITE (0 - 15). - * -- over is the hilite attribute used if value is at or above - * the threshold. The attribute can be BL_HILITE_NONE, - * BL_HILITE_INVERSE, BL_HILITE_BOLD (-1, -2, or -3), or one - * of the color indexes of CLR_BLACK, CLR_RED, CLR_GREEN, - * CLR_BROWN, CLR_BLUE, CLR_MAGENTA, CLR_CYAN, CLR_GRAY, - * CLR_ORANGE, CLR_BRIGHT_GREEN, CLR_YELLOW, CLR_BRIGHT_BLUE, - * CLR_BRIGHT_MAGENTA, CLR_BRIGHT_CYAN, or CLR_WHITE (0 - 15). + * Return what color this condition should + * be displayed in based on user settings. */ -void -tty_status_threshold(fldidx, thresholdtype, threshold, behavior, under, over) -int fldidx, thresholdtype; -int behavior, under, over; -anything threshold; +int condcolor(bm, bmarray) +long bm; +unsigned long *bmarray; { - tty_status_hilites[fldidx].thresholdtype = thresholdtype; - tty_status_hilites[fldidx].threshold = threshold; - tty_status_hilites[fldidx].behavior = behavior; - tty_status_hilites[fldidx].under = under; - tty_status_hilites[fldidx].over = over; - return; -} +#ifdef STATUS_HILITES + int i; + if (bm && bmarray) + for (i = 0; i < CLR_MAX; ++i) { + if (bmarray[i] && (bm & bmarray[i])) + return i; + } +#endif + return NO_COLOR; +} +#endif /* TEXTCOLOR */ + +int condattr(bm, bmarray) +long bm; +unsigned long *bmarray; +{ + int attr = 0; + int i; + + if (bm && bmarray) { + for (i = HL_ATTCLR_DIM; i < BL_ATTCLR_MAX; ++i) { + if (bmarray[i] && (bm & bmarray[i])) { + switch(i) { + case HL_ATTCLR_DIM: + attr |= HL_DIM; + break; + case HL_ATTCLR_BLINK: + attr |= HL_BLINK; + break; + case HL_ATTCLR_ULINE: + attr |= HL_ULINE; + break; + case HL_ATTCLR_INVERSE: + attr |= HL_INVERSE; + break; + case HL_ATTCLR_BOLD: + attr |= HL_BOLD; + break; + } + } + } + } + return attr; +} #endif /* STATUS_HILITES */ -#endif /*STATUS_VIA_WINDOWPORT*/ + #endif /* TTY_GRAPHICS */ diff --git a/win/win32/mhmsg.h b/win/win32/mhmsg.h index 926f7d0eb..090068d82 100644 --- a/win/win32/mhmsg.h +++ b/win/win32/mhmsg.h @@ -72,6 +72,7 @@ typedef struct mswin_nhmsg_update_status { int n_fields; const char **vals; boolean *activefields; + int *percents; int *colors; } MSNHMsgUpdateStatus, *PMSNHMsgUpdateStatus; diff --git a/win/win32/mhstatus.c b/win/win32/mhstatus.c index fce51f268..5e522b7b1 100644 --- a/win/win32/mhstatus.c +++ b/win/win32/mhstatus.c @@ -18,10 +18,10 @@ typedef struct mswin_nethack_status_window { int n_fields; const char **vals; boolean *activefields; + int *percents; int *colors; } NHStatusWindow, *PNHStatusWindow; -#ifdef STATUS_VIA_WINDOWPORT static int fieldorder1[] = { BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, BL_ALIGN, BL_SCORE, -1 }; static int fieldorder2[] = { BL_LEVELDESC, BL_GOLD, BL_HP, BL_HPMAX, @@ -29,7 +29,6 @@ static int fieldorder2[] = { BL_LEVELDESC, BL_GOLD, BL_HP, BL_HPMAX, BL_EXP, BL_HD, BL_TIME, BL_HUNGER, BL_CAP, BL_CONDITION, -1 }; static int *fieldorders[] = { fieldorder1, fieldorder2, NULL }; -#endif /* STATUS_VIA_WINDOWPORT */ static TCHAR szStatusWindowClass[] = TEXT("MSNHStatusWndClass"); LRESULT CALLBACK StatusWndProc(HWND, UINT, WPARAM, LPARAM); @@ -133,9 +132,10 @@ StatusWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) case MSNH_MSG_GETTEXT: { PMSNHMsgGetText msg_data = (PMSNHMsgGetText) lParam; -#ifdef STATUS_VIA_WINDOWPORT +#ifdef STATUS_HILITES int **fop; int *f; + msg_data->buffer[0] = '\0'; if (data->n_fields > 0) { for (fop = fieldorders; *fop; fop++) { @@ -143,20 +143,20 @@ StatusWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) if (data->activefields[*f]) strncat(msg_data->buffer, data->vals[*f], msg_data->max_size - - strlen(msg_data->buffer)); + - strlen(msg_data->buffer)); } strncat(msg_data->buffer, "\r\n", msg_data->max_size - strlen(msg_data->buffer)); } } -#else /* STATUS_VIA_WINDOWPORT */ +#else strncpy(msg_data->buffer, data->window_text[0], msg_data->max_size); strncat(msg_data->buffer, "\r\n", msg_data->max_size - strlen(msg_data->buffer)); strncat(msg_data->buffer, data->window_text[1], msg_data->max_size - strlen(msg_data->buffer)); -#endif /* STATUS_VIA_WINDOWPORT */ +#endif } break; case MSNH_MSG_UPDATE_STATUS: { @@ -164,6 +164,7 @@ StatusWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) data->n_fields = msg_data->n_fields; data->vals = msg_data->vals; data->activefields = msg_data->activefields; + data->percents = msg_data->percents; data->colors = msg_data->colors; InvalidateRect(hWnd, NULL, TRUE); } break; @@ -206,10 +207,12 @@ StatusWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) return 0; } -#ifdef STATUS_VIA_WINDOWPORT +#ifdef STATUS_HILITES static LRESULT onWMPaint(HWND hWnd, WPARAM wParam, LPARAM lParam) { + int hpbar_percent = 100; + int hpbar_color = NO_COLOR; int *f; int **fop; SIZE sz; @@ -238,45 +241,83 @@ onWMPaint(HWND hWnd, WPARAM wParam, LPARAM lParam) : (COLORREF) GetSysColor(DEFAULT_COLOR_FG_STATUS); OldFg = SetTextColor(hdc, Fg); + if (iflags.wc2_hitpointbar && BL_HP < data->n_fields + && data->activefields[BL_HP]) { + hpbar_percent = data->percents[BL_HP]; + hpbar_color = data->colors[BL_HP] & 0x00ff; + } + for (fop = fieldorders; *fop; fop++) { LONG left = rt.left; LONG cy = 0; int vlen; for (f = *fop; *f != -1; f++) { + int clr, atr; + int fntatr = ATR_NONE; + HGDIOBJ fnt; + COLORREF nFg, nBg; + if (((*f) >= data->n_fields) || (!data->activefields[*f])) continue; + clr = data->colors[*f] & 0x00ff; + atr = (data->colors[*f] & 0xff00) >> 8; vlen = strlen(data->vals[*f]); NH_A2W(data->vals[*f], wbuf, SIZE(wbuf)); - if (!iflags.use_status_hilites) { - SelectObject(hdc, normalFont); - SetBkColor(hdc, Bg); - SetTextColor(hdc, Fg); - } else if (data->colors[*f] == CLR_MAX - || data->colors[*f] == BL_HILITE_NONE) { - SelectObject(hdc, normalFont); - SetBkColor(hdc, Bg); - SetTextColor(hdc, Fg); - } else if (data->colors[*f] > 0) { - SelectObject(hdc, normalFont); - SetBkColor(hdc, Bg); - SetTextColor(hdc, nhcolor_to_RGB(data->colors[*f])); - } else if (data->colors[*f] == BL_HILITE_INVERSE) { - SelectObject(hdc, normalFont); - SetBkColor(hdc, Fg); - SetTextColor(hdc, Bg); - } else if (data->colors[*f] == BL_HILITE_BOLD) { - SelectObject(hdc, boldFont); - SetBkColor(hdc, Bg); - SetTextColor(hdc, Fg); - } else { - SelectObject(hdc, normalFont); - SetBkColor(hdc, Bg); - SetTextColor(hdc, Fg); - } + if (atr & HL_BOLD) + fntatr = ATR_BOLD; + else if (atr & HL_INVERSE) + fntatr = ATR_INVERSE; + else if (atr & HL_ULINE) + fntatr = ATR_ULINE; + else if (atr & HL_BLINK) + fntatr = ATR_BLINK; + else if (atr & HL_DIM) + fntatr = ATR_DIM; + fnt = mswin_get_font(NHW_STATUS, fntatr, hdc, FALSE); + nFg = (clr >= 0 && clr < CLR_MAX) ? nhcolor_to_RGB(clr) : Fg; + nBg = Bg; GetTextExtentPoint32(hdc, wbuf, vlen, &sz); - DrawText(hdc, wbuf, vlen, &rt, DT_LEFT); + if (*f == BL_TITLE && iflags.wc2_hitpointbar) { + HBRUSH back_brush = CreateSolidBrush(nhcolor_to_RGB(hpbar_color)); + RECT barrect; + + /* first draw title normally */ + SelectObject(hdc, fnt); + SetBkMode(hdc, OPAQUE); + SetBkColor(hdc, Bg); + SetTextColor(hdc, nhcolor_to_RGB(hpbar_color)); + DrawText(hdc, wbuf, vlen, &rt, DT_LEFT); + + /* calc bar length */ + barrect.left = rt.left; + barrect.top = rt.top; + barrect.bottom = sz.cy; + if (hpbar_percent > 0) + barrect.right = (int)((hpbar_percent * sz.cx) / 100); + else + barrect.right = sz.cx; + + /* then draw hpbar on top of title */ + FillRect(hdc, &barrect, back_brush); + SetBkMode(hdc, TRANSPARENT); + SetTextColor(hdc, nBg); + DrawText(hdc, wbuf, vlen, &barrect, DT_LEFT); + + DeleteObject(back_brush); + } else { + if (atr & HL_INVERSE) { + COLORREF tmp = nFg; + nFg = nBg; + nBg = tmp; + } + SelectObject(hdc, fnt); + SetBkMode(hdc, OPAQUE); + SetBkColor(hdc, nBg); + SetTextColor(hdc, nFg); + DrawText(hdc, wbuf, vlen, &rt, DT_LEFT); + } rt.left += sz.cx; cy = max(cy, sz.cy); @@ -336,7 +377,7 @@ onWMPaint(HWND hWnd, WPARAM wParam, LPARAM lParam) return 0; } -#endif /*STATUS_VIA_WINDOWPORT*/ +#endif /* !STATUS_HILITES */ void mswin_status_window_size(HWND hWnd, LPSIZE sz) diff --git a/win/win32/mswproc.c b/win/win32/mswproc.c index 7b80cfbc6..db045a4cc 100644 --- a/win/win32/mswproc.c +++ b/win/win32/mswproc.c @@ -85,6 +85,9 @@ struct window_procs mswin_procs = { | WC_FONTSIZ_TEXT | WC_TILE_WIDTH | WC_TILE_HEIGHT | WC_TILE_FILE | WC_VARY_MSGCOUNT | WC_WINDOWCOLORS | WC_PLAYER_SELECTION | WC_SPLASH_SCREEN | WC_POPUP_DIALOG | WC_MOUSE_SUPPORT, +#ifdef STATUS_HILITES + WC2_HITPOINTBAR | WC2_FLUSH_STATUS | +#endif 0L, mswin_init_nhwindows, mswin_player_selection, mswin_askname, mswin_get_nh_event, mswin_exit_nhwindows, mswin_suspend_nhwindows, mswin_resume_nhwindows, mswin_create_nhwindow, mswin_clear_nhwindow, @@ -108,13 +111,8 @@ struct window_procs mswin_procs = { /* other defs that really should go away (they're tty specific) */ mswin_start_screen, mswin_end_screen, mswin_outrip, mswin_preference_update, mswin_getmsghistory, mswin_putmsghistory, -#ifdef STATUS_VIA_WINDOWPORT mswin_status_init, mswin_status_finish, mswin_status_enablefield, mswin_status_update, -#ifdef STATUS_HILITES - mswin_status_threshold, -#endif -#endif genl_can_suspend_yes, }; @@ -2713,18 +2711,18 @@ NHMessageBox(HWND hWnd, LPCTSTR text, UINT type) return MessageBox(hWnd, text, title, type); } -#ifdef STATUS_VIA_WINDOWPORT static const char *_status_fieldnm[MAXBLSTATS]; static const char *_status_fieldfmt[MAXBLSTATS]; static char *_status_vals[MAXBLSTATS]; static int _status_colors[MAXBLSTATS]; +static int _status_percents[MAXBLSTATS]; static boolean _status_activefields[MAXBLSTATS]; extern winid WIN_STATUS; #ifdef STATUS_HILITES typedef struct hilite_data_struct { int thresholdtype; - anything threshold; + anything value; int behavior; int under; int over; @@ -2747,6 +2745,7 @@ mswin_status_init(void) _status_activefields[i] = FALSE; _status_fieldfmt[i] = (const char *) 0; _status_colors[i] = CLR_MAX; /* no color */ + _status_percents[i] = 0; #ifdef STATUS_HILITES _status_hilites[i].thresholdtype = 0; _status_hilites[i].behavior = BL_TH_NONE; @@ -2804,64 +2803,9 @@ mswin_status_enablefield(int fieldidx, const char *nm, const char *fmt, _status_activefields[fieldidx] = enable; } -#ifdef STATUS_HILITES -/* -status_threshold(int fldidx, int threshholdtype, anything threshold, - int behavior, int under, int over) - -- called when a hiliting preference is added, changed, or - removed. - -- the fldindex identifies which field is having its hiliting - preference set. It is an integer index value from botl.h - -- fldindex could be any one of the following from botl.h: - BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, - BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX, - BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX, - BL_LEVELDESC, BL_EXP, BL_CONDITION - -- datatype is P_INT, P_UINT, P_LONG, or P_MASK. - -- threshold is an "anything" union which can contain the - datatype value. - -- behavior is used to define how threshold is used and can - be BL_TH_NONE, BL_TH_VAL_PERCENTAGE, BL_TH_VAL_ABSOLUTE, - or BL_TH_UPDOWN. BL_TH_NONE means don't do anything above - or below the threshold. BL_TH_VAL_PERCENTAGE treats the - threshold value as a precentage of the maximum possible - value. BL_TH_VAL_ABSOLUTE means that the threshold is an - actual value. BL_TH_UPDOWN means that threshold is not - used, and the two below/above hilite values indicate how - to display something going down (under) or rising (over). - -- under is the hilite attribute used if value is below the - threshold. The attribute can be BL_HILITE_NONE, - BL_HILITE_INVERSE, BL_HILITE_BOLD (-1, -2, or -3), or one - of the color indexes of CLR_BLACK, CLR_RED, CLR_GREEN, - CLR_BROWN, CLR_BLUE, CLR_MAGENTA, CLR_CYAN, CLR_GRAY, - CLR_ORANGE, CLR_BRIGHT_GREEN, CLR_YELLOW, CLR_BRIGHT_BLUE, - CLR_BRIGHT_MAGENTA, CLR_BRIGHT_CYAN, or CLR_WHITE (0 - 15). - -- over is the hilite attribute used if value is at or above - the threshold. The attribute can be BL_HILITE_NONE, - BL_HILITE_INVERSE, BL_HILITE_BOLD (-1, -2, or -3), or one - of the color indexes of CLR_BLACK, CLR_RED, CLR_GREEN, - CLR_BROWN, CLR_BLUE, CLR_MAGENTA, CLR_CYAN, CLR_GRAY, - CLR_ORANGE, CLR_BRIGHT_GREEN, CLR_YELLOW, CLR_BRIGHT_BLUE, - CLR_BRIGHT_MAGENTA, CLR_BRIGHT_CYAN, or CLR_WHITE (0 - 15). -*/ -void -mswin_status_threshold(int fldidx, int thresholdtype, anything threshold, - int behavior, int under, int over) -{ - logDebug("mswin_status_threshold(%d, %d, %d, %d, %d)\n", fldidx, - thresholdtype, behavior, under, over); - assert(fldidx >= 0 && fldidx < MAXBLSTATS); - _status_hilites[fldidx].thresholdtype = thresholdtype; - _status_hilites[fldidx].threshold = threshold; - _status_hilites[fldidx].behavior = behavior; - _status_hilites[fldidx].under = under; - _status_hilites[fldidx].over = over; -} -#endif /* STATUS_HILITES */ - /* -status_update(int fldindex, genericptr_t ptr, int chg, int percentage) +status_update(int fldindex, genericptr_t ptr, int chg, int percent, int color, unsigned long *colormasks) -- update the value of a status field. -- the fldindex identifies which field is changing and is an integer index value from botl.h @@ -2894,9 +2838,13 @@ status_update(int fldindex, genericptr_t ptr, int chg, int percentage) symbol for GOLD "\GXXXXNNNN:nnn". If window port needs textual gold amount without the leading "$:" the port will have to skip past ':' in passed "ptr" for the BL_GOLD case. + -- color is the color that the NetHack core is telling you to + use to display the text. + -- colormasks is a pointer to a set of CLR_MAX unsigned longs + telling you which fields should be displayed in each color. */ void -mswin_status_update(int idx, genericptr_t ptr, int chg, int percent) +mswin_status_update(int idx, genericptr_t ptr, int chg, int percent, int color, unsigned long *colormasks) { long cond, *condptr = (long *) ptr; char *text = (char *) ptr; @@ -2905,11 +2853,12 @@ mswin_status_update(int idx, genericptr_t ptr, int chg, int percent) unsigned ospecial; long value = -1; - logDebug("mswin_status_update(%d, %p, %d, %d)\n", idx, ptr, chg, percent); + logDebug("mswin_status_update(%d, %p, %d, %d, %x, %p)\n", idx, ptr, chg, percent, color, colormasks); if (idx != BL_FLUSH) { if (!_status_activefields[idx]) return; + _status_percents[idx] = percent; switch (idx) { case BL_CONDITION: { cond = *condptr; @@ -2971,75 +2920,16 @@ mswin_status_update(int idx, genericptr_t ptr, int chg, int percent) } } -#ifdef STATUS_HILITES - switch (_status_hilites[idx].behavior) { - case BL_TH_NONE: { - _status_colors[idx] = CLR_MAX; - } break; - - case BL_TH_UPDOWN: { - if (chg > 0) - _status_colors[idx] = _status_hilites[idx].over; - else if (chg < 0) - _status_colors[idx] = _status_hilites[idx].under; - else - _status_colors[idx] = CLR_MAX; - } break; - - case BL_TH_VAL_PERCENTAGE: { - int pct_th = 0; - if (_status_hilites[idx].thresholdtype != ANY_INT) { - impossible("mswin_status_update: unsupported percentage " - "threshold type %d", - _status_hilites[idx].thresholdtype); - break; - } - pct_th = _status_hilites[idx].threshold.a_int; - _status_colors[idx] = (percent >= pct_th) - ? _status_hilites[idx].over - : _status_hilites[idx].under; - } break; - - case BL_TH_VAL_ABSOLUTE: { - int c = CLR_MAX; - int o = _status_hilites[idx].over; - int u = _status_hilites[idx].under; - anything *t = &_status_hilites[idx].threshold; - switch (_status_hilites[idx].thresholdtype) { - case ANY_LONG: - c = (value >= t->a_long) ? o : u; - break; - case ANY_INT: - c = (value >= t->a_int) ? o : u; - break; - case ANY_UINT: - c = ((unsigned long) value >= t->a_uint) ? o : u; - break; - case ANY_ULONG: - c = ((unsigned long) value >= t->a_ulong) ? o : u; - break; - case ANY_MASK32: - c = (value & t->a_ulong) ? o : u; - break; - default: - impossible("mswin_status_update: unsupported absolute threshold " - "type %d\n", - _status_hilites[idx].thresholdtype); - break; - } - _status_colors[idx] = c; - } break; - } -#endif /* STATUS_HILITES */ + _status_colors[idx] = color; /* send command to status window */ ZeroMemory(&update_cmd_data, sizeof(update_cmd_data)); update_cmd_data.n_fields = MAXBLSTATS; update_cmd_data.vals = _status_vals; update_cmd_data.activefields = _status_activefields; + update_cmd_data.percents = _status_percents; update_cmd_data.colors = _status_colors; SendMessage(mswin_hwnd_from_winid(WIN_STATUS), WM_MSNH_COMMAND, (WPARAM) MSNH_MSG_UPDATE_STATUS, (LPARAM) &update_cmd_data); } -#endif /*STATUS_VIA_WINDOWPORT*/ diff --git a/win/win32/winMS.h b/win/win32/winMS.h index e34cb006f..b799da1e7 100644 --- a/win/win32/winMS.h +++ b/win/win32/winMS.h @@ -182,18 +182,11 @@ void mswin_preference_update(const char *pref); char *mswin_getmsghistory(BOOLEAN_P init); void mswin_putmsghistory(const char *msg, BOOLEAN_P); -#ifdef STATUS_VIA_WINDOWPORT void mswin_status_init(void); void mswin_status_finish(void); void mswin_status_enablefield(int fieldidx, const char *nm, const char *fmt, boolean enable); -void mswin_status_update(int idx, genericptr_t ptr, int chg, int percent); - -#ifdef STATUS_HILITES -void mswin_status_threshold(int fldidx, int thresholdtype, anything threshold, - int behavior, int under, int over); -#endif /* STATUS_HILITES */ -#endif /*STATUS_VIA_WINDOWPORT*/ +void mswin_status_update(int idx, genericptr_t ptr, int chg, int percent, int color, unsigned long *colormasks); /* helper function */ HWND mswin_hwnd_from_winid(winid wid);