Merge branch 'NetHack-3.7' into twitch-interact

This commit is contained in:
2026-04-19 08:36:20 -07:00
29 changed files with 3472 additions and 2808 deletions
+3
View File
@@ -3,6 +3,7 @@ Boolean options not under specific compile flags (with default values in []):
option setting, which is reached via the 'O' command.) option setting, which is reached via the 'O' command.)
acoustics can your character hear anything [True] acoustics can your character hear anything [True]
armorstatus show extra status field summarizing worn armor [False]
autodescribe describe the terrain under cursor [False] autodescribe describe the terrain under cursor [False]
autodig dig if moving and wielding digging tool [False] autodig dig if moving and wielding digging tool [False]
autoopen walking into a door attempts to open it [True] autoopen walking into a door attempts to open it [True]
@@ -73,6 +74,7 @@ sparkle display sparkly effect for resisted magical [True]
attacks (e.g. fire attack on fire-resistant monster) attacks (e.g. fire attack on fire-resistant monster)
standout use standout mode for --More-- on messages [False] standout use standout mode for --More-- on messages [False]
status_updates update the status lines [True] status_updates update the status lines [True]
terrainstatus show extra status field describing current location [False]
time display elapsed game time, in moves [False] time display elapsed game time, in moves [False]
tips show some helpful tips during gameplay [True] tips show some helpful tips during gameplay [True]
tombstone print tombstone when you die [True] tombstone print tombstone when you die [True]
@@ -83,6 +85,7 @@ travel enables travelling via mouse click if supported; [True]
use_darkgray use bold black instead of blue for black glyphs. [True] use_darkgray use bold black instead of blue for black glyphs. [True]
use_inverse display detected monsters in highlighted manner [False] use_inverse display detected monsters in highlighted manner [False]
verbose print more commentary during the game [True] verbose print more commentary during the game [True]
weaponstatus show extra status field listing wielded weapon(s) [False]
whatis_menu show menu when getting a map location [False] whatis_menu show menu when getting a map location [False]
whatis_moveskip skip same glyphs when getting a map location [False] whatis_moveskip skip same glyphs when getting a map location [False]
+42 -2
View File
@@ -60,7 +60,7 @@
.ds f0 \*(vr .ds f0 \*(vr
.ds f1 \" empty .ds f1 \" empty
.\"DO NOT REMOVE NH_DATESUB .ds f2 Date(%B %-d, %Y) .\"DO NOT REMOVE NH_DATESUB .ds f2 Date(%B %-d, %Y)
.ds f2 March 25, 2026 .ds f2 April 16, 2026
. .
.\" A note on some special characters: .\" A note on some special characters:
.\" \(lq = left double quote .\" \(lq = left double quote
@@ -2724,6 +2724,12 @@ Some of the more obscure weapons (such as the \fIaklys\fP,
\fIlucern hammer\fP, and \fIbec-de-corbin\fP) are defined in an \fIlucern hammer\fP, and \fIbec-de-corbin\fP) are defined in an
appendix to \fIUnearthed Arcana\fP, an AD&D supplement. appendix to \fIUnearthed Arcana\fP, an AD&D supplement.
.pg .pg
Some interfaces support the
.op weaponstatus
option.
When it is enabled, an extra status condition is displayed, describing
the currently wielded weapon.
.pg
The commands to use weapons are \(oqw\(cq (wield), \(oqt\(cq (throw), The commands to use weapons are \(oqw\(cq (wield), \(oqt\(cq (throw),
\(oqf\(cq (fire), \(oqQ\(cq (quiver), \(oqf\(cq (fire), \(oqQ\(cq (quiver),
\(oqx\(cq (exchange), \(oqX\(cq (twoweapon), and \(lq#enhance\(rq \(oqx\(cq (exchange), \(oqX\(cq (twoweapon), and \(lq#enhance\(rq
@@ -2956,6 +2962,12 @@ option can be set (prior to game start) to attempt to play the entire
game without wearing any armor (a self-imposed challenge which is game without wearing any armor (a self-imposed challenge which is
extremely difficult to accomplish). extremely difficult to accomplish).
.pg .pg
Some interfaces support the
.op armorstatus
option.
When it is enabled, an extra status condition is displayed, summarizing
currently worn armor.
.pg
The commands to use armor are \(oqW\(cq (wear) and \(oqT\(cq (take off). The commands to use armor are \(oqW\(cq (wear) and \(oqT\(cq (take off).
The \(oqA\(cq command can be used to take off armor as well as other The \(oqA\(cq command can be used to take off armor as well as other
worn items. worn items.
@@ -3734,7 +3746,7 @@ Otherwise the last section extends to the end of the options file.
Highlight menu lines with different colors. Highlight menu lines with different colors.
See the \(lqConfiguring Menu Colors\(rq section. See the \(lqConfiguring Menu Colors\(rq section.
.lp MSGTYPE .lp MSGTYPE
Change the way messages are shown in the top status line. Change the way messages are shown in the top line.
See the \(lqConfiguring Message Types\(rq section. See the \(lqConfiguring Message Types\(rq section.
.lp ROGUESYMBOLS .lp ROGUESYMBOLS
Custom symbols for the rogue level's symbol set. Custom symbols for the rogue level's symbol set.
@@ -3883,6 +3895,21 @@ If \f(CRalign\fP is not specified, there is no default value;
player will be prompted unless role and/or race forces a choice for alignment. player will be prompted unless role and/or race forces a choice for alignment.
Cannot be set with the \(oq\f(CRO\fP\(cq command. Cannot be set with the \(oq\f(CRO\fP\(cq command.
Persistent. Persistent.
.lp armorstatus
Display an extra status condition which summarizes currently worn armor
(default off, not supported by all interfaces).
.lp ""
For the usual case where more than one piece of armor is worn, a list of
letters is shown in the following order:
.sd
\f(CRG\fP - gloves;
\f(CRC\fP - cloak;
\f(CRA\fP - suit;
\f(CRU\fP - shirt;
\f(CRH\fP - helmet;
\f(CRB\fP - boots;
\f(CRS\fP - shield.
.ed
.lp autodescribe .lp autodescribe
Automatically describe the terrain under cursor when asked to get a location Automatically describe the terrain under cursor when asked to get a location
on the map (default true). on the map (default true).
@@ -4877,6 +4904,9 @@ and prior versions (for example \(lqsuppress_alert:3.3.1\(rq).
This option may be used to select one of the named symbol sets found within This option may be used to select one of the named symbol sets found within
\(lqsymbols\(rq to alter the symbols displayed on the screen. \(lqsymbols\(rq to alter the symbols displayed on the screen.
Use \(lqsymset:default\(rq to explicitly select the default symbols. Use \(lqsymset:default\(rq to explicitly select the default symbols.
.lp terrainstatus
Display an extra status condition describing the spot beneath the hero's
feet (default off, not supported by all interfaces).
.lp "time " .lp "time "
Show the elapsed game time in turns on bottom line (default off). Show the elapsed game time in turns on bottom line (default off).
Persistent. Persistent.
@@ -4913,6 +4943,16 @@ Play a tutorial level at the start of the game.
Setting this option on or off in the config file will skip the query. Setting this option on or off in the config file will skip the query.
.lp "verbose " .lp "verbose "
Provide more commentary during the game (default on). Persistent. Provide more commentary during the game (default on). Persistent.
.lp weaponstatus
Display an extra status condition which describes currently wielded weapon
(default off, not supported by all interfaces).
.lp ""
Some possible displayed values are:
.sd
\f(CRbare-hnds\fP - no weapon and no gloves;
\f(CRempty-hnd\fP - no weapon but gloves are worn;
\f(CRdual-weps\fP - wielding two weapons.
.ed
.lp whatis_coord .lp whatis_coord
When using the \(oq/\(cq or \(oq;\(cq commands to look around on the map with When using the \(oq/\(cq or \(oq;\(cq commands to look around on the map with
.op autodescribe .op autodescribe
+42 -2
View File
@@ -25,7 +25,7 @@
\author{Original version - Eric S. Raymond\\ \author{Original version - Eric S. Raymond\\
(Edited and expanded for 3.7.0 by Mike Stephenson and others)} (Edited and expanded for 3.7.0 by Mike Stephenson and others)}
%DO NOT REMOVE NH_DATESUB \date{Date(%B %-d, %Y)} %DO NOT REMOVE NH_DATESUB \date{Date(%B %-d, %Y)}
\date{March 25, 2026} \date{April 16, 2026}
\maketitle \maketitle
@@ -2933,6 +2933,11 @@ weapon which existed in AD\&D does roughly the same damage to monsters in
\textit{aklys}, \textit{lucern hammer}, and \textit{bec-de-corbin}) are defined \textit{aklys}, \textit{lucern hammer}, and \textit{bec-de-corbin}) are defined
in an appendix to \textit{Unearthed Arcana}, an AD\&D supplement. in an appendix to \textit{Unearthed Arcana}, an AD\&D supplement.
%.pg
Some interfaces support the \texttt{weaponstatus} option.
When it is enabled, an extra status condition is displayed, describing
the currently wielded weapon.
%.pg %.pg
The commands to use weapons are `\texttt{w}' (wield), `\texttt{t}' (throw), The commands to use weapons are `\texttt{w}' (wield), `\texttt{t}' (throw),
`\texttt{f}' (fire), `\texttt{Q}' (quiver), `\texttt{f}' (fire), `\texttt{Q}' (quiver),
@@ -3161,6 +3166,11 @@ option can be set (prior to game start) to attempt to play the entire
game without wearing any armor (a self-imposed challenge which is game without wearing any armor (a self-imposed challenge which is
extremely difficult to accomplish). extremely difficult to accomplish).
%.pg
Some interfaces support the \texttt{armorstatus} option.
When it is enabled, an extra status condition is displayed, summarizing
currently worn armor.
%.pg %.pg
The commands to use armor are `\texttt{W}' (wear) and `\texttt{T}' (take off). The commands to use armor are `\texttt{W}' (wear) and `\texttt{T}' (take off).
The `\texttt{A}' command can be used to take off armor as well as other The `\texttt{A}' command can be used to take off armor as well as other
@@ -4053,7 +4063,7 @@ Highlight menu lines with different colors.
See the ``Configuring Menu Colors`` section. See the ``Configuring Menu Colors`` section.
%.lp %.lp
\item[MSGTYPE] \item[MSGTYPE]
Change the way messages are shown in the top status line. Change the way messages are shown in the top line.
See the ``Configuring Message Types`` section. See the ``Configuring Message Types`` section.
%.lp %.lp
\item[ROGUESYMBOLS] \item[ROGUESYMBOLS]
@@ -4215,6 +4225,21 @@ If \texttt{align} is not specified, there is no default value;
player will be prompted unless role and/or race forces a choice for alignment. player will be prompted unless role and/or race forces a choice for alignment.
Cannot be set with the `\texttt{O}' command. Persistent. Cannot be set with the `\texttt{O}' command. Persistent.
%.lp %.lp
\item[armorstatus]
Display an extra status condition which summarizes currently worn armor
(default off, not supported by all interfaces).
%.lp ""
\\
For the usual case where more than one piece of armor is worn, a list of
letters is shown in the following order:
\\
\texttt{G} - gloves;\\
\texttt{C} - cloak;\\
\texttt{A} - suit;\\
\texttt{U} - shirt;\\
\texttt{H} - helmet;\\
\texttt{B} - boots;\\
\texttt{S} - shield.
\item[autodescribe] \item[autodescribe]
Automatically describe the terrain under cursor when asked to get a location Automatically describe the terrain under cursor when asked to get a location
on the map (default true). on the map (default true).
@@ -5307,6 +5332,10 @@ This option may be used to select one of the named symbol sets found within
\texttt{symbols} to alter the symbols displayed on the screen. \texttt{symbols} to alter the symbols displayed on the screen.
Use ``\texttt{symset:default}'' to explicitly select the default symbols. Use ``\texttt{symset:default}'' to explicitly select the default symbols.
%.lp %.lp
\item[terrainstatus]
Display an extra status condition describing the spot beneath the hero's
feet (default off, not supported by all interfaces).
%.lp
\item[time] \item[time]
Show the elapsed game time in turns on bottom line (default off). Persistent. Show the elapsed game time in turns on bottom line (default off). Persistent.
%.lp %.lp
@@ -5347,6 +5376,17 @@ Setting this option on or off in the config file will skip the query.
\item[verbose] \item[verbose]
Provide more commentary during the game (default on). Persistent. Provide more commentary during the game (default on). Persistent.
%.lp %.lp
\item[weaponstatus]
Display an extra status condition which describes currently wielded weapon
(default off, not supported by all interfaces).
%.lp ""
\\
Some possible displayed values are:
\\
\texttt{bare-hnds} - no weapon and no gloves;\\
\texttt{empty-hnd} - no weapon but gloves are worn;\\
\texttt{dual-weps} - wielding two weapons.
%.lp
\item[whatis\textunderscore coord] \item[whatis\textunderscore coord]
When using the `\texttt{/}' or `\texttt{;}' commands to look around on the map with When using the `\texttt{/}' or `\texttt{;}' commands to look around on the map with
``\texttt{autodescribe}'' ``\texttt{autodescribe}''
+890 -824
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -2203,6 +2203,7 @@ mounted hero was able to deliver joust hits when trapped
timer sanity check for melting ice gave false complaint about non-ice for timer sanity check for melting ice gave false complaint about non-ice for
frozen moat under open drawbridge frozen moat under open drawbridge
mhitm_ad_phys() was not applying Half_physical_damage when hero was target mhitm_ad_phys() was not applying Half_physical_damage when hero was target
callers of thitu() were not applyibg Half_physical_damage either
throwing crystal plate mail or helm of brilliance up against the ceiling could throwing crystal plate mail or helm of brilliance up against the ceiling could
result in the item being cracked and then vanishing result in the item being cracked and then vanishing
yn_function (used all over the place) sometimes triggered an impossible() yn_function (used all over the place) sometimes triggered an impossible()
+22 -10
View File
@@ -5,7 +5,12 @@
#ifndef BOTL_H #ifndef BOTL_H
#define BOTL_H #define BOTL_H
/* MAXCO must hold longest uncompressed status line, and must be larger /* Note: this comment is about the pre-VIA_WINDOWPORT two line status
* which is still available but has not added a bunch of conditional
* extra status conditions (Grab, InLava, Held, Zzz and many others)
* or the new fields Weapon, Armor, and Terrain.
*
* MAXCO must hold longest uncompressed status line, and must be larger
* than COLNO * than COLNO
* *
* longest practical second status line at the moment is * longest practical second status line at the moment is
@@ -39,13 +44,20 @@ enum statusfields {
BL_CHARACTERISTICS = -3, /* alias for BL_STR..BL_CH */ BL_CHARACTERISTICS = -3, /* alias for BL_STR..BL_CH */
BL_RESET = -2, /* Force everything to redisplay */ BL_RESET = -2, /* Force everything to redisplay */
BL_FLUSH = -1, /* Finished cycling through bot fields */ BL_FLUSH = -1, /* Finished cycling through bot fields */
/*
* Note: status_sanity_check() in wintty.c has strings for the rest
* of these, so if any get renumbered or more get added, be sure to
* keep those in sync.
*/
BL_TITLE = 0, BL_TITLE = 0,
BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, /* 1..6 */ 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_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, /* 7..10 */
BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, /* 13..18 */ BL_ENE, BL_ENEMAX, BL_XP, BL_AC, BL_HD, /* 11..15 */
BL_HPMAX, BL_LEVELDESC, BL_EXP, BL_CONDITION, /* 19..22 */ BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX, /* 16..19 */
BL_VERS, /* 23 */ BL_LEVELDESC, BL_EXP, BL_CONDITION, /* 20..22 */
MAXBLSTATS, /* [24] */ BL_WEAPON, BL_ARMOR, BL_TERRAIN, /* 23..25 */
BL_VERS, /* 26 */
MAXBLSTATS /* [27] */
}; };
enum relationships { enum relationships {
@@ -55,7 +67,7 @@ enum relationships {
}; };
enum blconditions { enum blconditions {
bl_bareh, bl_bareh, /* deprecated -- bl_weapon encompasses this */
bl_blind, bl_blind,
bl_busy, bl_busy,
bl_conf, bl_conf,
@@ -67,7 +79,7 @@ enum blconditions {
bl_grab, bl_grab,
bl_hallu, bl_hallu,
bl_held, bl_held,
bl_icy, bl_icy, /* bl_terrain encompasses this */
bl_inlava, bl_inlava,
bl_lev, bl_lev,
bl_parlyz, bl_parlyz,
@@ -78,7 +90,7 @@ enum blconditions {
bl_stone, bl_stone,
bl_strngl, bl_strngl,
bl_stun, bl_stun,
bl_submerged, bl_submerged, /* bl_terrain encompasses this */
bl_termill, bl_termill,
bl_tethered, bl_tethered,
bl_trapped, bl_trapped,
+3
View File
@@ -276,6 +276,8 @@ extern void max_rank_sz(void);
extern long botl_score(void); extern long botl_score(void);
#endif #endif
extern int describe_level(char *, int); extern int describe_level(char *, int);
extern char *weapon_status(char *) NONNULL NONNULLARG1;
extern char *armor_status(char *) NONNULL NONNULLARG1;
extern void status_initialize(boolean); extern void status_initialize(boolean);
extern void status_finish(void); extern void status_finish(void);
extern boolean exp_percent_changing(void); extern boolean exp_percent_changing(void);
@@ -1215,6 +1217,7 @@ extern void runmode_delay_output(void);
extern void overexert_hp(void); extern void overexert_hp(void);
extern boolean overexertion(void); extern boolean overexertion(void);
extern void invocation_message(void); extern void invocation_message(void);
extern void classify_terrain(void);
extern void switch_terrain(void); extern void switch_terrain(void);
extern void set_uinwater(int); extern void set_uinwater(int);
extern boolean pooleffects(boolean); extern boolean pooleffects(boolean);
+4
View File
@@ -17,6 +17,7 @@
struct flag { struct flag {
boolean acoustics; /* allow dungeon sound messages */ boolean acoustics; /* allow dungeon sound messages */
boolean armorstatus; /* show armor info on status lines */
boolean autodig; /* MRKR: Automatically dig */ boolean autodig; /* MRKR: Automatically dig */
boolean autoquiver; /* Automatically fill quiver */ boolean autoquiver; /* Automatically fill quiver */
boolean autoopen; /* open doors by walking into them */ boolean autoopen; /* open doors by walking into them */
@@ -63,9 +64,11 @@ struct flag {
boolean sortpack; /* sorted inventory */ boolean sortpack; /* sorted inventory */
boolean sparkle; /* show "resisting" special FX (Scott Bigham) */ boolean sparkle; /* show "resisting" special FX (Scott Bigham) */
boolean standout; /* use standout for --More-- */ boolean standout; /* use standout for --More-- */
boolean terrainstatus; /* show terrain info on status lines */
boolean time; /* display elapsed 'time' */ boolean time; /* display elapsed 'time' */
boolean tombstone; /* print tombstone */ boolean tombstone; /* print tombstone */
boolean verbose; /* max battle info */ boolean verbose; /* max battle info */
boolean weaponstatus; /* show weapon info on status lines */
int end_top, end_around; /* describe desired score list */ int end_top, end_around; /* describe desired score list */
unsigned autounlock; /* locked door/chest action */ unsigned autounlock; /* locked door/chest action */
#define AUTOUNLOCK_UNTRAP 1 #define AUTOUNLOCK_UNTRAP 1
@@ -307,6 +310,7 @@ struct instance_flags {
int getpos_coords; /* show coordinates when getting cursor position */ int getpos_coords; /* show coordinates when getting cursor position */
int menuinvertmode; /* 0 = invert toggles every item; int menuinvertmode; /* 0 = invert toggles every item;
* 1 = invert skips 'all items' item */ * 1 = invert skips 'all items' item */
int terrain_typ; /* index into terrain_descr[] for botl */
color_attr menu_headings; /* CLR_ and ATR_ for menu headings */ color_attr menu_headings; /* CLR_ and ATR_ for menu headings */
uint32_t colorcount; /* store how many colors terminal is capable of */ uint32_t colorcount; /* store how many colors terminal is capable of */
boolean use_truecolor; /* force use of truecolor */ boolean use_truecolor; /* force use of truecolor */
+11 -2
View File
@@ -164,11 +164,14 @@ static int optfn_##a(int, int, boolean, char *, char *);
Off, Yes, No, No, NoAlias, (boolean *) 0, Term_False, Off, Yes, No, No, NoAlias, (boolean *) 0, Term_False,
(char *)0) (char *)0)
#endif #endif
NHOPTB(armorstatus, Advanced, 0, opt_in, set_in_game,
Off, Yes, No, No, NoAlias, &flags.armorstatus, Term_False,
"summarize currently worn armor in a status field")
NHOPTB(ascii_map, Advanced, 0, opt_in, set_in_game, NHOPTB(ascii_map, Advanced, 0, opt_in, set_in_game,
ascii_map_Def, Yes, No, No, NoAlias, &iflags.wc_ascii_map, ascii_map_Def, Yes, No, No, NoAlias, &iflags.wc_ascii_map,
Term_False, "show map as text") Term_False, "show map as text")
NHOPTO("autocompletions", Advanced, o_autocomplete, BUFSZ, opt_in, set_in_game, NHOPTO("autocompletions", Advanced, o_autocomplete, BUFSZ, opt_in,
No, Yes, No, NoAlias, "edit autocompletions") set_in_game, No, Yes, No, NoAlias, "edit autocompletions")
NHOPTB(autodescribe, Advanced, 0, opt_out, set_in_game, NHOPTB(autodescribe, Advanced, 0, opt_out, set_in_game,
On, Yes, No, No, NoAlias, &iflags.autodescribe, Term_False, On, Yes, No, No, NoAlias, &iflags.autodescribe, Term_False,
"describe terrain under cursor") "describe terrain under cursor")
@@ -741,6 +744,9 @@ static int optfn_##a(int, int, boolean, char *, char *);
No, Yes, No, No, "termcolumns", "number of columns") No, Yes, No, No, "termcolumns", "number of columns")
NHOPTC(term_rows, Advanced, 6, opt_in, set_in_config, NHOPTC(term_rows, Advanced, 6, opt_in, set_in_config,
No, Yes, No, No, NoAlias, "number of rows") No, Yes, No, No, NoAlias, "number of rows")
NHOPTB(terrainstatus, Advanced, 0, opt_in, set_in_game,
Off, Yes, No, No, NoAlias, &flags.terrainstatus, Term_False,
"show hero's location as a status field")
NHOPTC(tile_file, Advanced, 70, opt_in, set_gameview, NHOPTC(tile_file, Advanced, 70, opt_in, set_gameview,
No, Yes, No, No, NoAlias, "name of tile file") No, Yes, No, No, NoAlias, "name of tile file")
NHOPTC(tile_height, Advanced, 20, opt_in, set_gameview, NHOPTC(tile_height, Advanced, 20, opt_in, set_gameview,
@@ -853,6 +859,9 @@ static int optfn_##a(int, int, boolean, char *, char *);
#endif #endif
NHOPTC(warnings, Advanced, 10, opt_in, set_in_config, NHOPTC(warnings, Advanced, 10, opt_in, set_in_config,
No, Yes, No, No, NoAlias, "display characters for warnings") No, Yes, No, No, NoAlias, "display characters for warnings")
NHOPTB(weaponstatus, Advanced, 0, opt_in, set_in_game,
Off, Yes, No, No, NoAlias, &flags.weaponstatus, Term_False,
"show currently wielded weapon in a status field")
NHOPTC(whatis_coord, Advanced, 1, opt_in, set_in_game, NHOPTC(whatis_coord, Advanced, 1, opt_in, set_in_game,
Yes, Yes, No, Yes, NoAlias, Yes, Yes, No, Yes, NoAlias,
"show coordinates when auto-describing cursor position") "show coordinates when auto-describing cursor position")
+13
View File
@@ -92,7 +92,20 @@ enum levl_typ_types {
CLOUD = 36, CLOUD = 36,
MAX_TYPE = 37, MAX_TYPE = 37,
/* for special levels */
MATCH_WALL = 38, MATCH_WALL = 38,
/* these aren't levl[][].typ values, they're additional indices
into terrain_descr[] for status feedback */
xFLOOR = 39,
xGROUND = 40,
xOPENDOOR = 41,
xSHUTDOOR = 42,
xSWAMP = 43,
xSUBMERGED = 44,
xSEA = 45,
xWATERWALL = 46,
INVALID_TYPE = 127 INVALID_TYPE = 127
}; };
+3
View File
@@ -262,6 +262,9 @@ extern
#define WC2_U_UTF8STR 0x020000L /* 18 utf8str support */ #define WC2_U_UTF8STR 0x020000L /* 18 utf8str support */
#define WC2_EXTRACOLORS 0x040000L /* 19 color support beyond NH_BASIC_COLOR */ #define WC2_EXTRACOLORS 0x040000L /* 19 color support beyond NH_BASIC_COLOR */
/* 13 free bits */ /* 13 free bits */
#define WC2_EXTRASTATUS 0x080000L /* 20 optional weaponstatus, armorstatus,
* terrainstatus */
/* 12 free bits */
#define ALIGN_LEFT 1 #define ALIGN_LEFT 1
#define ALIGN_RIGHT 2 #define ALIGN_RIGHT 2
+3 -2
View File
@@ -3190,10 +3190,11 @@ use_whip(struct obj *obj)
if (!rn2(25)) { if (!rn2(25)) {
/* proficient with whip, but maybe not /* proficient with whip, but maybe not
so proficient at catching weapons */ so proficient at catching weapons */
int hitu, hitvalu; int dam, hitvalu, hitu;
dam = dmgval(otmp, &gy.youmonst);
hitvalu = 8 + otmp->spe; hitvalu = 8 + otmp->spe;
hitu = thitu(hitvalu, dmgval(otmp, &gy.youmonst), hitu = thitu(hitvalu, Maybe_Half_Phys(dam),
&otmp, (char *) 0); &otmp, (char *) 0);
if (hitu) { if (hitu) {
pline_The("%s hits you as you try to snatch it!", pline_The("%s hits you as you try to snatch it!",
+274 -14
View File
@@ -475,6 +475,145 @@ describe_level(
return ret; return ret;
} }
/* weapon description for status lines; started as a terser version of
what ^X shows but has diverged to some extent */
char *
weapon_status(char *outbuf)
{
const char *res = 0;
*outbuf = '\0'; /* lint suppression */
if (!uwep) {
/* no weapon; gloves imply hands; humanoid also implies hands;
otherwise make no assumptions */
res = uarmg ? "Empty-hnd" /* empty handed means "gloves only" */
: humanoid(gy.youmonst.data) ? "Bare-hnds" /* bare hands */
: "No-weapon";
} else if (u.twoweap) {
/* two-weaponing implies hands and a weapon or wep-tool
(not other odd stuff) in each hand */
res = "Dual-weps";
/* note: dual wielding two lances doesn't produce double joust */
if (u.usteed && (weapon_type(uwep) == P_LANCE
|| weapon_type(uswapwep) == P_LANCE))
res = "Dual+joust"; /* lance behaves specially when mounted */
} else {
/* report most weapons by their skill class (so a katana will be
described as a long sword, for instance; mattock and hook are
exceptions), or wielded non-weapon item by its object class */
char *p;
int skill = weapon_type(uwep);
if (u.usteed && skill == P_LANCE) {
/* lance behaves specially when hero is mounted */
res = "joust";
} else if (uwep->otyp == AKLYS) {
/* aklys behaves specially when thrown while wielded, so
give it a distinct name instead of skill name of "club";
[maybe FIXME?] for the time being
use real name even if 'obj' is undiscovered "thonged club" */
res = "aklys";
} else if (is_sword(uwep)) {
/* simplify short short/broad sword/long sword/two-handed sword
(similar to messages when dropped due to slippery fingers) */
res = "sword";
} else {
/* shorten several */
switch (skill) {
case P_QUARTERSTAFF:
res = "staff";
break;
case P_MORNING_STAR:
res = "mrng-star"; /* still pretty long */
break;
case P_POLEARMS:
res = "pole";
break;
case P_UNICORN_HORN:
res = "unihorn";
break;
default:
res = weapon_descr(uwep);
/* [should this be moved into weapon_descr()?] */
if (!strcmpi(res, "food") && uwep->otyp == CREAM_PIE)
res = "pie";
break;
}
}
if ((uwep->oclass == WEAPON_CLASS || is_weptool(uwep))
&& bimanual(uwep) && *res != '2' && strncmpi(res, "two", 3))
Strcat(outbuf, "2H-");
Strcpy(p = eos(outbuf), res), res = outbuf;
*p = highc(*p);
/* avoid embedded spaces since its designed to appear as part
of a space-separated status line */
(void) strNsubst(outbuf, " ", "-", 0);
}
return (outbuf == res) ? outbuf : strcpy(outbuf, res);
}
/* armor description for status lines */
char *
armor_status(char *armbuf)
{
int n = !!uarmg + !!uarmc + !!uarm + !!uarmu + !!uarmh + !!uarmf + !!uarms;
/*
* FIXME: ^X needs to provide non-abbreviated version of this info.
* At present it just reports the "no armor" case.
*/
if (n == 0) { /* no armor */
Strcpy(armbuf, "naked");
} else if (n == 1) { /* just one piece; spell it out */
Strcpy(armbuf, uarmg ? "gloves"
: uarmc ? "cloak"
: uarm ? "suit"
: uarmu ? "shirt"
: uarmh ? helm_simple_name(uarmh) /* hat|helm */
: uarmf ? "boots"
: uarms ? "shield"
: ""); /* not possible */
} else { /* more than one piece */
char *p = armbuf;
/* gloves first since this is expected to follow weapon_status();
cloak next since it tends to provide the most protection
aside from raw AC */
if (uarmg)
*p++ = 'G'; /* gloves */
if (uarmc)
*p++ = 'C'; /* cloak */
if (uarm)
*p++ = 'A'; /* suit but 's' is for shield */
if (uarmu)
*p++ = 'U'; /* underwear? => shirt */
if (uarmh)
*p++ = 'H'; /* hat/helm */
if (uarmf)
*p++ = 'B'; /* footwear => boots */
if (uarms)
*p++ = 'S'; /* shield */
*p = '\0';
}
/*
* Add a hint about MC by appending a plus sign if that's augmented.
* Bug: we should modfiy magical_negation() to return extra info and
* call it to scan whole inventory looking for sources of protection.
* This is a hack for efficiency to avoid that during status updates.
*/
if ((uright && uright->otyp == RIN_PROTECTION)
|| (uleft && uleft->otyp == RIN_PROTECTION)
|| (uamul && uamul->otyp == AMULET_OF_GUARDING)
|| (uarmc && uarmc->otyp == CLOAK_OF_PROTECTION)
|| (uarmh && uarmh->oartifact == ART_MITRE_OF_HOLINESS)
|| (uwep && uwep->oartifact == ART_TSURUGI_OF_MURAMASA))
(void) strkitten(armbuf, '+');
return upstart(armbuf);
}
/* =======================================================================*/ /* =======================================================================*/
/* statusnew routines */ /* statusnew routines */
/* =======================================================================*/ /* =======================================================================*/
@@ -550,9 +689,17 @@ staticfn void status_hilites_viewall(void);
{ (genericptr_t) 0 }, { (genericptr_t) 0 }, (char *) 0, \ { (genericptr_t) 0 }, { (genericptr_t) 0 }, (char *) 0, \
wid, maxfld, fld INIT_THRESH } wid, maxfld, fld INIT_THRESH }
/* If entries are added to this, botl.h will require updating too. /*
'max' value of BL_EXP gets special handling since the percentage * If entries are added to this, botl.h will require updating too.
involved isn't a direct 100*current/maximum calculation. */ *
* 'max' values of BL_XP and BL_EXP get special handling since the
* percentage involved isn't a direct 100*current/maximum calculation.
*
* long int fields are given a buffer size of 30 which is guaranteed to
* be big enough for short prefix followed by a 20+ digit 64-bit long
* even though the actual values will be much smaller. The gold field
* is even bigger due to its encoded dollar sign prefix.
*/
static struct istat_s initblstats[MAXBLSTATS] = { static struct istat_s initblstats[MAXBLSTATS] = {
INIT_BLSTAT("title", "%s", ANY_STR, MAXVALWIDTH, BL_TITLE), INIT_BLSTAT("title", "%s", ANY_STR, MAXVALWIDTH, BL_TITLE),
INIT_BLSTAT("strength", " St:%s", ANY_INT, 10, BL_STR), INIT_BLSTAT("strength", " St:%s", ANY_INT, 10, BL_STR),
@@ -561,28 +708,36 @@ static struct istat_s initblstats[MAXBLSTATS] = {
INIT_BLSTAT("intelligence", " In:%s", ANY_INT, 10, BL_IN), INIT_BLSTAT("intelligence", " In:%s", ANY_INT, 10, BL_IN),
INIT_BLSTAT("wisdom", " Wi:%s", ANY_INT, 10, BL_WI), INIT_BLSTAT("wisdom", " Wi:%s", ANY_INT, 10, BL_WI),
INIT_BLSTAT("charisma", " Ch:%s", ANY_INT, 10, BL_CH), INIT_BLSTAT("charisma", " Ch:%s", ANY_INT, 10, BL_CH),
INIT_BLSTAT("alignment", " %s", ANY_STR, 40, BL_ALIGN), INIT_BLSTAT("alignment", " %s", ANY_STR, 20, BL_ALIGN),
INIT_BLSTAT("score", " S:%s", ANY_LONG, 20, BL_SCORE), INIT_BLSTAT("score", " S:%s", ANY_LONG, 30, BL_SCORE),
INIT_BLSTAT("carrying-capacity", " %s", ANY_INT, 20, BL_CAP), INIT_BLSTAT("carrying-capacity", " %s", ANY_INT, 20, BL_CAP),
INIT_BLSTAT("gold", " %s", ANY_LONG, 30, BL_GOLD), INIT_BLSTAT("gold", " %s", ANY_LONG, 40, BL_GOLD),
INIT_BLSTATP("power", " Pw:%s", ANY_INT, 10, BL_ENEMAX, BL_ENE), INIT_BLSTATP("power", " Pw:%s", ANY_INT, 10, BL_ENEMAX, BL_ENE),
INIT_BLSTAT("power-max", "(%s)", ANY_INT, 10, BL_ENEMAX), INIT_BLSTAT("power-max", "(%s)", ANY_INT, 10, BL_ENEMAX),
INIT_BLSTATP("experience-level", " Xp:%s", ANY_INT, 10, BL_EXP, BL_XP), INIT_BLSTATP("experience-level", " Xp:%s", ANY_INT, 10, BL_EXP, BL_XP),
INIT_BLSTAT("armor-class", " AC:%s", ANY_INT, 10, BL_AC), INIT_BLSTAT("armor-class", " AC:%s", ANY_INT, 10, BL_AC),
INIT_BLSTAT("HD", " HD:%s", ANY_INT, 10, BL_HD), INIT_BLSTAT("HD", " HD:%s", ANY_INT, 10, BL_HD),
INIT_BLSTAT("time", " T:%s", ANY_LONG, 20, BL_TIME), INIT_BLSTAT("time", " T:%s", ANY_LONG, 30, BL_TIME),
/* hunger used to be 'ANY_UINT'; see note below in bot_via_windowport() */ /* hunger used to be 'ANY_UINT'; see note below in bot_via_windowport() */
INIT_BLSTAT("hunger", " %s", ANY_INT, 40, BL_HUNGER), INIT_BLSTAT("hunger", " %s", ANY_INT, 20, BL_HUNGER),
INIT_BLSTATP("hitpoints", " HP:%s", ANY_INT, 10, BL_HPMAX, BL_HP), INIT_BLSTATP("hitpoints", " HP:%s", ANY_INT, 10, BL_HPMAX, BL_HP),
INIT_BLSTAT("hitpoints-max", "(%s)", ANY_INT, 10, BL_HPMAX), INIT_BLSTAT("hitpoints-max", "(%s)", ANY_INT, 10, BL_HPMAX),
INIT_BLSTAT("dungeon-level", "%s", ANY_STR, MAXVALWIDTH, BL_LEVELDESC), INIT_BLSTAT("dungeon-level", "%s", ANY_STR, MAXVALWIDTH, BL_LEVELDESC),
INIT_BLSTATP("experience", "/%s", ANY_LONG, 20, BL_EXP, BL_EXP), INIT_BLSTATP("experience", "/%s", ANY_LONG, 30, BL_EXP, BL_EXP),
INIT_BLSTAT("condition", "%s", ANY_MASK32, 0, BL_CONDITION), INIT_BLSTAT("condition", "%s", ANY_MASK32, 0, BL_CONDITION),
/* optional; once set it doesn't change unless 'showvers' option is /* optional; once set it doesn't change unless 'showvers' option is
toggled or player modifies the 'versinfo' option; toggled or player modifies the 'versinfo' option;
available mostly for screenshots or someone looking over shoulder; available mostly for screenshots or someone looking over shoulder;
blstat[][BL_VERS] is actually an int copy of flags.versinfo (0...7) */ blstat[][BL_VERS] is actually an int copy of flags.versinfo (0...7) */
INIT_BLSTAT("version", " %s", ANY_STR, MAXVALWIDTH, BL_VERS), INIT_BLSTAT("version", " %s", ANY_STR, MAXVALWIDTH, BL_VERS),
/* weapon and armor are constructed strings with no particular numeric
equivalent */
INIT_BLSTAT("weapon", " %s", ANY_STR, 20, BL_WEAPON),
INIT_BLSTAT("armor", " %s", ANY_STR, 20, BL_ARMOR),
/* terrain is tracked by a number but designating it as type 'int'
isn't useful; using type 'string' allows highlighting based on text
matching which is potentially useful */
INIT_BLSTAT("terrain", " %s", ANY_STR, 20, BL_TERRAIN),
}; };
#undef INIT_BLSTATP #undef INIT_BLSTATP
@@ -638,25 +793,27 @@ const struct conditions_t conditions[] = {
{ 10, BL_MASK_HALLU, bl_hallu, { "Hallu", "Hal", "Hl" } }, { 10, BL_MASK_HALLU, bl_hallu, { "Hallu", "Hal", "Hl" } },
{ 20, BL_MASK_HELD, bl_held, { "Held", "Hld", "Hd" } }, { 20, BL_MASK_HELD, bl_held, { "Held", "Hld", "Hd" } },
{ 20, BL_MASK_ICY, bl_icy, { "Icy", "Icy", "Ic" } }, { 20, BL_MASK_ICY, bl_icy, { "Icy", "Icy", "Ic" } },
{ 8, BL_MASK_INLAVA, bl_inlava, { "Lava", "Lav", "La" } }, { 8, BL_MASK_INLAVA, bl_inlava, { "InLava", "Lav", "La" } },
{ 10, BL_MASK_LEV, bl_lev, { "Lev", "Lev", "Lv" } }, { 10, BL_MASK_LEV, bl_lev, { "Lev", "Lev", "Lv" } },
{ 20, BL_MASK_PARLYZ, bl_parlyz, { "Parlyz", "Para", "Par" } }, { 20, BL_MASK_PARLYZ, bl_parlyz, { "Parlyz", "Para", "Par" } },
{ 10, BL_MASK_RIDE, bl_ride, { "Ride", "Rid", "Rd" } }, { 10, BL_MASK_RIDE, bl_ride, { "Ride", "Rid", "Rd" } },
{ 20, BL_MASK_SLEEPING, bl_sleeping, { "Zzz", "Zzz", "Zz" } }, { 20, BL_MASK_SLEEPING, bl_sleeping, { "Zzz", "Zzz", "Zz" } },
{ 6, BL_MASK_SLIME, bl_slime, { "Slime", "Slim", "Slm" } }, { 6, BL_MASK_SLIME, bl_slime, { "Slime", "Slim", "Slm" } },
{ 20, BL_MASK_SLIPPERY, bl_slippery, { "Slip", "Sli", "Sl" } }, { 20, BL_MASK_SLIPPERY, bl_slippery, { "Slip", "Slp", "Sl" } },
{ 6, BL_MASK_STONE, bl_stone, { "Stone", "Ston", "Sto" } }, { 6, BL_MASK_STONE, bl_stone, { "Stone", "Ston", "Sto" } },
{ 4, BL_MASK_STRNGL, bl_strngl, { "Strngl", "Stngl", "Str" } }, { 4, BL_MASK_STRNGL, bl_strngl, { "Strngl", "Stngl", "Str" } },
{ 10, BL_MASK_STUN, bl_stun, { "Stun", "Stun", "St" } }, { 10, BL_MASK_STUN, bl_stun, { "Stun", "Stun", "St" } },
{ 15, BL_MASK_SUBMERGED, bl_submerged, { "Sub", "Sub", "Sw" } }, { 15, BL_MASK_SUBMERGED, bl_submerged, { "Submrg", "Subm", "Sm" } },
{ 6, BL_MASK_TERMILL, bl_termill, { "TermIll", "Ill", "Ill" } }, { 6, BL_MASK_TERMILL, bl_termill, { "TermIll", "Ill", "Ill" } },
{ 20, BL_MASK_TETHERED, bl_tethered, { "Teth", "Tth", "Te" } }, { 20, BL_MASK_TETHERED, bl_tethered, { "Teth", "Tth", "Te" } },
{ 20, BL_MASK_TRAPPED, bl_trapped, { "Trap", "Trp", "Tr" } }, { 20, BL_MASK_TRAPPED, bl_trapped, { "Trap", "Trp", "Tr" } },
{ 20, BL_MASK_UNCONSC, bl_unconsc, { "Out", "Out", "KO" } }, { 20, BL_MASK_UNCONSC, bl_unconsc, { "Out", "Out", "KO" } },
{ 20, BL_MASK_WOUNDEDL, bl_woundedl, { "Legs", "Leg", "Lg" } }, { 20, BL_MASK_WOUNDEDL, bl_woundedl, { "WLegs", "Leg", "Lg" } },
{ 20, BL_MASK_HOLDING, bl_holding, { "UHold", "UHld", "UHd" } }, { 20, BL_MASK_HOLDING, bl_holding, { "UHold", "UHld", "UHd" } },
}; };
/* [perhaps these should all be opt_out with default of 'in';
otherwise some players may never learn about them] */
struct condtests_t condtests[CONDITION_COUNT] = { struct condtests_t condtests[CONDITION_COUNT] = {
/* id, useropt, opt_in or out, enabled, configchoice, testresult; /* id, useropt, opt_in or out, enabled, configchoice, testresult;
default value for enabled is !opt_in but can get changed via options */ default value for enabled is !opt_in but can get changed via options */
@@ -694,6 +851,70 @@ struct condtests_t condtests[CONDITION_COUNT] = {
/* condition indexing */ /* condition indexing */
int cond_idx[CONDITION_COUNT] = { 0 }; int cond_idx[CONDITION_COUNT] = { 0 };
static const char c_Wall[] = "Wall";
/*
* Terrain descriptions for flags.terrainstatus; simplified from
* def_syms[].name and indexed by iflags.terrain_typ; should be
* kept in sync with rm.h types and the first half of def_syms[].
* The extra pseudo-types are specified by classify_terrain()
* when it sets up iflags.terrain_typ. Walls and a few of the
* others can only occur when hero has the Passes_walls ability.
*/
const char *terrain_descr[] = {
/* 0*/ "Stone", /* stone */
c_Wall, /* vwall */
c_Wall, /* hwall */
c_Wall, /* tlcorner */
c_Wall, /* trcorner */
c_Wall, /* blcorner */
c_Wall, /* brcorner */
c_Wall, /* crosswall */
c_Wall, /* tuwall */
c_Wall, /* tdwall */
/*10*/ c_Wall, /* tlwall */
c_Wall, /* trwall */
"Portcullis", /* dbwall, closed drawbridge 'door' */
"Tree",
c_Wall, /* sdoor: secret door */
"Stone", /* scorr: secret corridor */
"Pool", /* pool or non-moat water; can be boiled away */
"Moat", /* water that can't be boiled away */
"Water", /* water on Water level; can't be boiled or frozen */
"(gap)", /* drawbridge_up; replaced by whatever is under */
/*20*/ "Lava", /* lavapool */
"LavaWall", /* lava that extends to ceiling */
"Bars", /* ironbars */
"Doorway", /* doorless or broken door; diagonal movement is ok */
"Corridor", /* replaced by "Floor" */
"Room", /* also replaced by "Floor" */
"Stairs",
"Ladder",
"Fountain",
"Throne",
/*30*/ "Sink",
"Grave",
"Altar",
"Ice",
"Bridge", /* drawbridge_down, span across moat/ice/lava/floor */
"Air", /* open air on Air level or bubble on Water level */
"Cloud", /* [part of] a cloud or Air level */
/*
*/
/*37*/ "", /* MAX_TYPE; skipped ratther than overloaded */
/*38*/ c_Wall, /* MATCH_WALL for special levels; shouldn't happen */
/*
* additional terrain names that aren't simple levl[][].typ values
*/
/*39*/ "Floor", /* substituted for room or corridor */
/*40*/ "Ground", /* 'room' on Earth level */
"Open-door", /* open (not broken or doorless) */
"Shut-door", /* closed or locked (or trapped) */
"Swamp", /* Juiblex level */
"Submerged", /* under water */
"Sea", /* moat terrain on Medusa's level: "shallow sea" */
"WaterWall", /* water that extends to the ceiling */
};
/* cache-related */ /* cache-related */
static boolean cache_avail[3] = { FALSE, FALSE, FALSE }; static boolean cache_avail[3] = { FALSE, FALSE, FALSE };
static boolean cache_reslt[3] = { FALSE, FALSE, FALSE }; static boolean cache_reslt[3] = { FALSE, FALSE, FALSE };
@@ -1024,6 +1245,35 @@ bot_via_windowport(void)
} }
#undef cond_bitset #undef cond_bitset
/*
* Optionally displayed weapon(s), armor, and terrain.
*/
if (flags.weaponstatus)
(void) weapon_status(gb.blstats[idx][BL_WEAPON].val);
else
*gb.blstats[idx][BL_WEAPON].val = '\0';
if (flags.armorstatus)
(void) armor_status(gb.blstats[idx][BL_ARMOR].val);
else
*gb.blstats[idx][BL_ARMOR].val = '\0';
if (flags.terrainstatus) {
if (iflags.terrain_typ == MAX_TYPE)
classify_terrain();
i = iflags.terrain_typ;
if (gb.blstats[idx][BL_TERRAIN].a.a_int != i) {
Strcpy(gb.blstats[idx][BL_TERRAIN].val, terrain_descr[i]);
gb.blstats[idx][BL_TERRAIN].a.a_int = i;
}
} else {
*gb.blstats[idx][BL_TERRAIN].val = '\0';
/* MAX_TYPE is "none of the above" for levl[][].typ */
gb.blstats[idx][BL_TERRAIN].a.a_int = MAX_TYPE;
}
gv.valset[BL_TERRAIN] = TRUE;
/* now request rendering */
evaluate_and_notify_windowport(gv.valset, idx); evaluate_and_notify_windowport(gv.valset, idx);
#undef test_if_enabled #undef test_if_enabled
} }
@@ -1383,8 +1633,11 @@ evaluate_and_notify_windowport(
|| ((fld == BL_EXP) && !flags.showexp) || ((fld == BL_EXP) && !flags.showexp)
|| ((fld == BL_TIME) && !flags.time) || ((fld == BL_TIME) && !flags.time)
|| ((fld == BL_HD) && !Upolyd) || ((fld == BL_HD) && !Upolyd)
|| ((fld == BL_XP || i == BL_EXP) && Upolyd) || ((fld == BL_XP || fld == BL_EXP) && Upolyd)
|| ((fld == BL_VERS) && !flags.showvers) || ((fld == BL_VERS) && !flags.showvers)
|| ((fld == BL_TERRAIN) && !flags.terrainstatus)
|| ((fld == BL_WEAPON) && !flags.weaponstatus)
|| ((fld == BL_ARMOR) && !flags.armorstatus)
) { ) {
continue; continue;
} }
@@ -1452,6 +1705,9 @@ status_initialize(
: (fld == BL_XP) ? (boolean) !Upolyd : (fld == BL_XP) ? (boolean) !Upolyd
: (fld == BL_HD) ? (boolean) Upolyd : (fld == BL_HD) ? (boolean) Upolyd
: (fld == BL_VERS) ? flags.showvers : (fld == BL_VERS) ? flags.showvers
: (fld == BL_WEAPON) ? flags.weaponstatus
: (fld == BL_ARMOR) ? flags.armorstatus
: (fld == BL_TERRAIN) ? flags.terrainstatus
: TRUE; : TRUE;
fieldname = initblstats[i].fldname; fieldname = initblstats[i].fldname;
@@ -1569,6 +1825,10 @@ compare_blstats(struct istat_s *bl1, struct istat_s *bl2)
fmt_ptr((genericptr_t) bl1->a.a_void), fmt_ptr((genericptr_t) bl1->a.a_void),
fmt_ptr((genericptr_t) bl2->a.a_void)); fmt_ptr((genericptr_t) bl2->a.a_void));
} }
/* cheat; terrain is highlighted as a string but we have a handy int
reflecting its value to use when checking for changes */
if (bl1->fld == BL_TERRAIN)
anytype = ANY_INT;
fld = bl1->fld; fld = bl1->fld;
use_rawval = (fld == BL_HP || fld == BL_HPMAX use_rawval = (fld == BL_HP || fld == BL_HPMAX
+8 -2
View File
@@ -1585,12 +1585,18 @@ u_on_newpos(coordxy x, coordxy y)
u.usteed->mx = u.ux, u.usteed->my = u.uy; u.usteed->mx = u.ux, u.usteed->my = u.uy;
/* when changing levels, don't leave old position set with /* when changing levels, don't leave old position set with
stale values from previous level */ stale values from previous level */
if (!on_level(&u.uz, &u.uz0)) if (!on_level(&u.uz, &u.uz0)) {
u.ux0 = u.ux, u.uy0 = u.uy; u.ux0 = u.ux, u.uy0 = u.uy;
else if (!Blind && !Hallucination && !u.uswallow) /* sets lastseentyp[u.ux][u.uy]; needed for switch_terrain()
somewhere back up the call chain */
map_location(u.ux, u.uy, FALSE);
iflags.terrain_typ = MAX_TYPE; /* "none of the above" value */
} else {
/* still on same level; might have come close enough to /* still on same level; might have come close enough to
generic object(s) to redisplay them as specific objects */ generic object(s) to redisplay them as specific objects */
if (!Blind && !Hallucination && !u.uswallow)
see_nearby_objects(); see_nearby_objects();
}
earth_sense(); earth_sense();
} }
+5 -3
View File
@@ -718,7 +718,8 @@ struct scatter_chain {
/* returns number of scattered objects */ /* returns number of scattered objects */
long long
scatter(coordxy sx, coordxy sy, /* location of objects to scatter */ scatter(
coordxy sx, coordxy sy, /* location of objects to scatter */
int blastforce, /* force behind the scattering */ int blastforce, /* force behind the scattering */
unsigned int scflags, unsigned int scflags,
struct obj *obj) /* only scatter this obj */ struct obj *obj) /* only scatter this obj */
@@ -864,14 +865,15 @@ scatter(coordxy sx, coordxy sy, /* location of objects to scatter */
} }
} else if (u_at(gb.bhitpos.x, gb.bhitpos.y)) { } else if (u_at(gb.bhitpos.x, gb.bhitpos.y)) {
if (scflags & MAY_HITYOU) { if (scflags & MAY_HITYOU) {
int hitvalu, hitu; int dam, hitvalu, hitu;
if (gm.multi) if (gm.multi)
nomul(0); nomul(0);
dam = dmgval(stmp->obj, &gy.youmonst);
hitvalu = 8 + stmp->obj->spe; hitvalu = 8 + stmp->obj->spe;
if (bigmonst(gy.youmonst.data)) if (bigmonst(gy.youmonst.data))
hitvalu++; hitvalu++;
hitu = thitu(hitvalu, dmgval(stmp->obj, &gy.youmonst), hitu = thitu(hitvalu, Maybe_Half_Phys(dam),
&stmp->obj, (char *) 0); &stmp->obj, (char *) 0);
if (!stmp->obj) if (!stmp->obj)
stmp->stopped = TRUE; stmp->stopped = TRUE;
+110 -4
View File
@@ -3084,6 +3084,93 @@ invocation_message(void)
} }
} }
/* for status: set up iflags.terrain_typ, an index into terrain_descrp[];
some types need fixing up */
void
classify_terrain(void)
{
struct rm *lev = &levl[u.ux][u.uy];
int typ = svl.lastseentyp[u.ux][u.uy]; /* lev->typ */
/*
* If the terrain under the hero is different now from what it
* was on the previous check, bring iflags.terrain_typ up to date
* and request a status update. Unless hero is running--then the
* update request will be suppressed.
*/
if (Underwater) {
typ = xSUBMERGED;
} else {
switch (typ) {
case STONE:
if (svl.level.flags.arboreal)
typ = TREE;
break;
case CORR:
case ROOM:
/* this matches surface() but 'floor' is odd in many places */
typ = !Is_earthlevel(&u.uz) ? xFLOOR : xGROUND;
break;
case DOOR:
/* defaults to "doorway" (door-less or broken) */
if ((lev->doormask & D_ISOPEN) != 0)
typ = xOPENDOOR;
else if ((lev->doormask & (D_CLOSED | D_LOCKED | D_TRAPPED)) != 0)
typ = xSHUTDOOR;
break;
case DRAWBRIDGE_UP:
/* ICE, MOAT, LAVA, or 'STONE' (which ought to be 'room') */
typ = db_under_typ(lev->drawbridgemask);
if (typ == STONE || typ == ROOM)
typ = xGROUND;
break;
case MOAT:
/* moat and swamp handling match waterbody_name()'s result */
if (Is_medusa_level(&u.uz))
typ = xSEA;
else if (Is_juiblex_level(&u.uz))
typ = xSWAMP;
break;
case WATER:
if (!Is_waterlevel(&u.uz))
typ = xWATERWALL;
break;
#if 0 /* don't bother -- Passes_walls for hero is rare, moving
* from one type of wall to another even rarer, and the
* cost of some extra once per move status updates is low */
case VWALL:
case HWALL:
case TLCORNER:
case TRCORNER:
case BLCORNER:
case BRCORNER:
case CROSSWALL:
case TUWALL:
case TDWALL:
case TLWALL:
case TRWALL:
case SDOOR: /* (note: lastseentyp[][] never yields SDOOR) */
/* any wall type would do, terrain_descr[] is "Wall" for all;
forcing just one avoids false 'changed' detection below if
hero with Passes_walls ability moves from one to another */
typ = VWALL;
break;
#endif
default:
break;
}
}
if (typ != iflags.terrain_typ) {
/* terrain at hero's spot is different */
iflags.terrain_typ = typ;
/* request a status update unless hero is running */
if (flags.terrainstatus && !svc.context.run)
disp.botl = TRUE;
}
}
/* moving onto different terrain; /* moving onto different terrain;
might be going into solid rock, inhibiting levitation or flight, might be going into solid rock, inhibiting levitation or flight,
or coming back out of such, reinstating levitation/flying */ or coming back out of such, reinstating levitation/flying */
@@ -3124,13 +3211,19 @@ switch_terrain(void)
} }
if ((!!Levitation ^ was_levitating) || (!!Flying ^ was_flying)) if ((!!Levitation ^ was_levitating) || (!!Flying ^ was_flying))
disp.botl = TRUE; /* update Lev/Fly status condition */ disp.botl = TRUE; /* update Lev/Fly status condition */
if (flags.terrainstatus)
classify_terrain();
} }
/* set or clear u.uinwater */ /* set or clear u.uinwater */
void void
set_uinwater(int in_out) set_uinwater(int in_out)
{ {
if (in_out != (int) u.uinwater) {
u.uinwater = in_out ? 1 : 0; u.uinwater = in_out ? 1 : 0;
switch_terrain();
}
} }
/* extracted from spoteffects; called by spoteffects to check for entering or /* extracted from spoteffects; called by spoteffects to check for entering or
@@ -3246,8 +3339,11 @@ spoteffects(boolean pick)
spotterrain = levl[u.ux][u.uy].typ; spotterrain = levl[u.ux][u.uy].typ;
spotloc.x = u.ux, spotloc.y = u.uy; spotloc.x = u.ux, spotloc.y = u.uy;
/* moving onto different terrain might cause Lev or Fly to toggle */ /* moving onto different terrain might cause Lev or Fly to toggle;
if (spotterrain != levl[u.ux0][u.uy0].typ || !on_level(&u.uz, &u.uz0)) level change sets <ux0,uy0> to <ux,uy>, so this spotterrain
check always fails then, but it also sets iflags.terrain_typ */
if (spotterrain != levl[u.ux0][u.uy0].typ
|| iflags.terrain_typ == MAX_TYPE)
switch_terrain(); switch_terrain();
if (pooleffects(TRUE)) if (pooleffects(TRUE))
@@ -4035,9 +4131,19 @@ end_running(boolean and_travel)
{ {
/* moveloop() suppresses time_botl when context.run is non-zero; when /* moveloop() suppresses time_botl when context.run is non-zero; when
running stops, update 'time' even if other botl status is unchanged */ running stops, update 'time' even if other botl status is unchanged */
if (flags.time && svc.context.run) if (svc.context.run) {
disp.time_botl = TRUE;
svc.context.run = 0; svc.context.run = 0;
if (flags.time)
disp.time_botl = TRUE;
/* classify_terrain() suppresses setting disp.botl when
running; after that, it can no longer compare current terrain
against iflaga.terrain_typ to detect a change, so recompute */
if (flags.terrainstatus) {
iflags.terrain_typ = MAX_TYPE; /* "none of the above" value */
classify_terrain();
}
}
/* 'context.mv' isn't travel but callers who want to end travel /* 'context.mv' isn't travel but callers who want to end travel
all clear it too */ all clear it too */
if (and_travel) if (and_travel)
+1 -1
View File
@@ -93,7 +93,7 @@ thitu(
kprefix = KILLED_BY; /* killer_name supplies "an" if warranted */ kprefix = KILLED_BY; /* killer_name supplies "an" if warranted */
} else { } else {
knm = name; knm = name;
/* [perhaps ought to check for plural here to] */ /* [perhaps ought to check for plural here too] */
if (!strncmpi(name, "the ", 4) || !strncmpi(name, "an ", 3) if (!strncmpi(name, "the ", 4) || !strncmpi(name, "an ", 3)
|| !strncmpi(name, "a ", 2)) || !strncmpi(name, "a ", 2))
kprefix = KILLED_BY; kprefix = KILLED_BY;
+18 -3
View File
@@ -5320,12 +5320,24 @@ optfn_boolean(
return optn_ok; return optn_ok;
switch (optidx) { switch (optidx) {
case opt_time: case opt_terrainstatus:
#ifdef SCORE_ON_BOTL classify_terrain(); /* bring iflags.terrain_typ up to date */
FALLTHROUGH;
/*FALLTHRU*/
case opt_weaponstatus:
case opt_armorstatus:
if (!wc2_supported(allopt[optidx].name)) {
/* not actually an error */
config_error_add("'%s' is not supported.",
allopt[optidx].name);
return optn_ok;
}
FALLTHROUGH;
/*FALLTHRU*/
case opt_showscore: case opt_showscore:
#endif
case opt_showvers: case opt_showvers:
case opt_showexp: case opt_showexp:
case opt_time:
if (VIA_WINDOWPORT()) if (VIA_WINDOWPORT())
status_initialize(REASSESS_ONLY); status_initialize(REASSESS_ONLY);
disp.botl = TRUE; disp.botl = TRUE;
@@ -9827,6 +9839,7 @@ static struct wc_Opt wc_options[] = {
{ (char *) 0, 0L } { (char *) 0, 0L }
}; };
static struct wc_Opt wc2_options[] = { static struct wc_Opt wc2_options[] = {
{ "armorstatus", WC2_EXTRASTATUS },
{ "fullscreen", WC2_FULLSCREEN }, { "fullscreen", WC2_FULLSCREEN },
{ "guicolor", WC2_GUICOLOR }, { "guicolor", WC2_GUICOLOR },
{ "hilite_status", WC2_HILITE_STATUS }, { "hilite_status", WC2_HILITE_STATUS },
@@ -9841,7 +9854,9 @@ static struct wc_Opt wc2_options[] = {
{ "statuslines", WC2_STATUSLINES }, { "statuslines", WC2_STATUSLINES },
{ "term_cols", WC2_TERM_SIZE }, { "term_cols", WC2_TERM_SIZE },
{ "term_rows", WC2_TERM_SIZE }, { "term_rows", WC2_TERM_SIZE },
{ "terrainstatus", WC2_EXTRASTATUS },
{ "use_darkgray", WC2_DARKGRAY }, { "use_darkgray", WC2_DARKGRAY },
{ "weaponstatus", WC2_EXTRASTATUS },
{ "windowborders", WC2_WINDOWBORDERS }, { "windowborders", WC2_WINDOWBORDERS },
{ "wraptext", WC2_WRAPTEXT }, { "wraptext", WC2_WRAPTEXT },
{ (char *) 0, 0L } { (char *) 0, 0L }
+9 -4
View File
@@ -1193,6 +1193,7 @@ trapeffect_arrow_trap(
unsigned trflags UNUSED) unsigned trflags UNUSED)
{ {
struct obj *otmp; struct obj *otmp;
int dam;
if (mtmp == &gy.youmonst) { if (mtmp == &gy.youmonst) {
if (trap->once && trap->tseen && !rn2(15)) { if (trap->once && trap->tseen && !rn2(15)) {
@@ -1206,9 +1207,10 @@ trapeffect_arrow_trap(
seetrap(trap); seetrap(trap);
pline("An arrow shoots out at you!"); pline("An arrow shoots out at you!");
otmp = t_missile(ARROW, trap); otmp = t_missile(ARROW, trap);
dam = dmgval(otmp, &gy.youmonst);
if (u.usteed && !rn2(2) && steedintrap(trap, otmp)) { if (u.usteed && !rn2(2) && steedintrap(trap, otmp)) {
; /* nothing */ ; /* nothing */
} else if (thitu(8, dmgval(otmp, &gy.youmonst), &otmp, "arrow")) { } else if (thitu(8, Maybe_Half_Phys(dam), &otmp, "arrow")) {
if (otmp) if (otmp)
obfree(otmp, (struct obj *) 0); obfree(otmp, (struct obj *) 0);
} else { } else {
@@ -1252,6 +1254,7 @@ trapeffect_dart_trap(
unsigned int trflags UNUSED) unsigned int trflags UNUSED)
{ {
struct obj *otmp; struct obj *otmp;
int dam;
if (mtmp == &gy.youmonst) { if (mtmp == &gy.youmonst) {
int oldumort = u.umortality; int oldumort = u.umortality;
@@ -1269,10 +1272,10 @@ trapeffect_dart_trap(
otmp = t_missile(DART, trap); otmp = t_missile(DART, trap);
if (!rn2(6)) if (!rn2(6))
otmp->opoisoned = 1; otmp->opoisoned = 1;
dam = dmgval(otmp, &gy.youmonst);
if (u.usteed && !rn2(2) && steedintrap(trap, otmp)) { if (u.usteed && !rn2(2) && steedintrap(trap, otmp)) {
; /* nothing */ ; /* nothing */
} else if (thitu(7, dmgval(otmp, &gy.youmonst), } else if (thitu(7, Maybe_Half_Phys(dam), &otmp, "little dart")) {
&otmp, "little dart")) {
if (otmp) { if (otmp) {
if (otmp->opoisoned) if (otmp->opoisoned)
poisoned("dart", A_CON, "little dart", poisoned("dart", A_CON, "little dart",
@@ -3395,9 +3398,11 @@ launch_obj(
break; break;
} }
} else if (u_at(x, y)) { } else if (u_at(x, y)) {
int dam = dmgval(singleobj, &gy.youmonst);
if (gm.multi) if (gm.multi)
nomul(0); nomul(0);
if (thitu(9 + singleobj->spe, dmgval(singleobj, &gy.youmonst), if (thitu(9 + singleobj->spe, Maybe_Half_Phys(dam),
&singleobj, (char *) 0)) &singleobj, (char *) 0))
stop_occupation(); stop_occupation();
} }
+1 -1
View File
@@ -1261,12 +1261,12 @@ ini_inv_use_obj(struct obj *obj)
if (obj->oclass == ARMOR_CLASS) { if (obj->oclass == ARMOR_CLASS) {
if (is_shield(obj) && !uarms && !(uwep && bimanual(uwep))) { if (is_shield(obj) && !uarms && !(uwep && bimanual(uwep))) {
setworn(obj, W_ARMS);
/* Prior to 3.6.2 this used to unset uswapwep if it was set, /* Prior to 3.6.2 this used to unset uswapwep if it was set,
but wearing a shield doesn't prevent having an alternate but wearing a shield doesn't prevent having an alternate
weapon ready to swap with the primary; just make sure we weapon ready to swap with the primary; just make sure we
aren't two-weaponing (academic; no one starts that way) */ aren't two-weaponing (academic; no one starts that way) */
set_twoweap(FALSE); /* u.twoweap = FALSE */ set_twoweap(FALSE); /* u.twoweap = FALSE */
setworn(obj, W_ARMS);
} else if (is_helmet(obj) && !uarmh) } else if (is_helmet(obj) && !uarmh)
setworn(obj, W_ARMH); setworn(obj, W_ARMH);
else if (is_gloves(obj) && !uarmg) else if (is_gloves(obj) && !uarmg)
+1 -1
View File
@@ -1551,12 +1551,12 @@ hmon_hitmon_jousting(
first_weapon_hit(obj); first_weapon_hit(obj);
if (hmd->jousting < 0) { if (hmd->jousting < 0) {
pline("%s shatters on impact!", Yname2(obj));
/* (must be either primary or secondary weapon to get here) */ /* (must be either primary or secondary weapon to get here) */
set_twoweap(FALSE); /* sets u.twoweap = FALSE; set_twoweap(FALSE); /* sets u.twoweap = FALSE;
* untwoweapon() is too verbose here */ * untwoweapon() is too verbose here */
if (obj == uwep) if (obj == uwep)
uwepgone(); /* set gu.unweapon */ uwepgone(); /* set gu.unweapon */
pline("%s shatters on impact!", Yname2(obj));
/* minor side-effect: broken lance won't split puddings */ /* minor side-effect: broken lance won't split puddings */
useup(obj); useup(obj);
obj = (struct obj *) 0; obj = (struct obj *) 0;
+12 -3
View File
@@ -103,10 +103,15 @@ setuwep(struct obj *obj)
if (obj == uwep) if (obj == uwep)
return; /* necessary to not set gu.unweapon */ return; /* necessary to not set gu.unweapon */
/* This message isn't printed in the caller because it happens
* *whenever* Sunsword is unwielded, from whatever cause.
*/
setworn(obj, W_WEP); setworn(obj, W_WEP);
/* handle Ogresmasher before Sunsword; even though they can't be happening
at the same time, botl flag update should come before pline message */
if (uwep == obj
&& ((uwep && uwep->oartifact == ART_OGRESMASHER)
|| (olduwep && olduwep->oartifact == ART_OGRESMASHER)))
disp.botl = TRUE; /* gaining or losing Con bonus */
/* This message isn't printed in the caller because it happens
* *whenever* Sunsword is unwielded, from whatever cause. */
if (uwep == obj && artifact_light(olduwep) && olduwep->lamplit) { if (uwep == obj && artifact_light(olduwep) && olduwep->lamplit) {
end_burn(olduwep, FALSE); end_burn(olduwep, FALSE);
if (!Blind) if (!Blind)
@@ -828,7 +833,11 @@ drop_uswapwep(void)
void void
set_twoweap(boolean on_off) set_twoweap(boolean on_off)
{ {
if (on_off != u.twoweap) {
u.twoweap = on_off; u.twoweap = on_off;
if (flags.weaponstatus)
disp.botl = TRUE;
}
} }
/* the #twoweapon command */ /* the #twoweapon command */
+8
View File
@@ -137,6 +137,9 @@ setworn(struct obj *obj, long mask)
/* tux -> tuxedo -> "monkey suit" -> monk's suit */ /* tux -> tuxedo -> "monkey suit" -> monk's suit */
iflags.tux_penalty = (uarm && Role_if(PM_MONK) && gu.urole.spelarmr); iflags.tux_penalty = (uarm && Role_if(PM_MONK) && gu.urole.spelarmr);
} }
if ((flags.weaponstatus && (mask & W_WEP) != 0L)
|| (flags.armorstatus && (mask & W_ARMOR) != 0L))
disp.botl = TRUE;
update_inventory(); update_inventory();
recalc_telepat_range(); recalc_telepat_range();
} }
@@ -148,6 +151,7 @@ setnotworn(struct obj *obj)
{ {
const struct worn *wp; const struct worn *wp;
int p; int p;
long unworn = 0L;
if (!obj) if (!obj)
return; return;
@@ -160,6 +164,7 @@ setnotworn(struct obj *obj)
cancel_doff(obj, wp->w_mask); cancel_doff(obj, wp->w_mask);
*(wp->w_obj) = (struct obj *) 0; *(wp->w_obj) = (struct obj *) 0;
unworn |= wp->w_mask;
p = objects[obj->otyp].oc_oprop; p = objects[obj->otyp].oc_oprop;
u.uprops[p].extrinsic = u.uprops[p].extrinsic & ~wp->w_mask; u.uprops[p].extrinsic = u.uprops[p].extrinsic & ~wp->w_mask;
monstunseesu_prop(p); /* remove this extrinsic from seenres */ monstunseesu_prop(p); /* remove this extrinsic from seenres */
@@ -171,6 +176,9 @@ setnotworn(struct obj *obj)
} }
if (!uarm) if (!uarm)
iflags.tux_penalty = FALSE; iflags.tux_penalty = FALSE;
if ((flags.weaponstatus && (unworn & W_WEP) != 0L)
|| (flags.armorstatus && (unworn & W_ARMOR) != 0L))
disp.botl = TRUE;
update_inventory(); update_inventory();
recalc_telepat_range(); recalc_telepat_range();
} }
+3 -1
View File
@@ -4201,8 +4201,10 @@ boomhit(struct obj *obj, int dx, int dy)
} }
if (u_at(gb.bhitpos.x, gb.bhitpos.y)) { /* ct == 9 */ if (u_at(gb.bhitpos.x, gb.bhitpos.y)) { /* ct == 9 */
if (Fumbling || rn2(20) >= ACURR(A_DEX)) { if (Fumbling || rn2(20) >= ACURR(A_DEX)) {
int dam = dmgval(obj, &gy.youmonst);
/* we hit ourselves */ /* we hit ourselves */
(void) thitu(10 + obj->spe, dmgval(obj, &gy.youmonst), &obj, (void) thitu(10 + obj->spe, Maybe_Half_Phys(dam), &obj,
"boomerang"); "boomerang");
endmultishot(TRUE); endmultishot(TRUE);
break; break;
+4 -4
View File
@@ -8,8 +8,8 @@
# MS Visual Studio Visual C++ compiler # MS Visual Studio Visual C++ compiler
# #
# Visual Studio Compilers Tested: # Visual Studio Compilers Tested:
# - Microsoft Visual Studio Community 2022 v 17.14.29 # - Microsoft Visual Studio Community 2022 v 17.14.30
# - Microsoft Visual Studio Community 2026 v 18.4.2 # - Microsoft Visual Studio Community 2026 v 18.5.0
# #
#============================================================================== #==============================================================================
# This is used for building two distinct executables of NetHack: # This is used for building two distinct executables of NetHack:
@@ -1189,8 +1189,8 @@ rc=Rc.exe
# is too old or untested. # is too old or untested.
# #
# Recently tested versions: # Recently tested versions:
TESTEDVS2022 = 14.44.35222.0 TESTEDVS2022 = 14.44.35225.0
TESTEDVS2026 = 14.50.35728.0 TESTEDVS2026 = 14.50.35729.0
VS20261ST = 1450000000 VS20261ST = 1450000000
VS2026CUR = $(TESTEDVS2026:.=) VS2026CUR = $(TESTEDVS2026:.=)
+1 -1
View File
@@ -197,11 +197,11 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/
#endif #endif
#endif #endif
gh.hname = "NetHack"; /* used for syntax messages */
set_emergency_io(); set_emergency_io();
#ifndef MSWIN_GRAPHICS #ifndef MSWIN_GRAPHICS
early_init(argc, argv); /* already in WinMain for MSWIN_GRAPHICS */ early_init(argc, argv); /* already in WinMain for MSWIN_GRAPHICS */
#endif #endif
gh.hname = "NetHack"; /* used for syntax messages */
set_default_prefix_locations( set_default_prefix_locations(
argv[0]); /* must be re-done after initoptions_init() argv[0]); /* must be re-done after initoptions_init()
* which clears out gp.fqn_prefix[] */ * which clears out gp.fqn_prefix[] */
+3 -1
View File
@@ -72,7 +72,9 @@ struct window_procs curses_procs = {
#endif #endif
| WC2_FLUSH_STATUS | WC2_TERM_SIZE | WC2_FLUSH_STATUS | WC2_TERM_SIZE
| WC2_STATUSLINES | WC2_WINDOWBORDERS | WC2_PETATTR | WC2_GUICOLOR | WC2_STATUSLINES | WC2_WINDOWBORDERS | WC2_PETATTR | WC2_GUICOLOR
| WC2_SUPPRESS_HIST | WC2_URGENT_MESG | WC2_MENU_SHIFT), | WC2_SUPPRESS_HIST | WC2_URGENT_MESG | WC2_MENU_SHIFT
| WC2_EXTRASTATUS
),
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* color availability */ {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* color availability */
curses_init_nhwindows, curses_init_nhwindows,
curses_player_selection, curses_player_selection,
+60 -11
View File
@@ -254,12 +254,13 @@ draw_horizontal(boolean border)
/* almost all fields already come with a leading space; /* almost all fields already come with a leading space;
"xspace" indicates places where we'll generate an extra one */ "xspace" indicates places where we'll generate an extra one */
static const enum statusfields static const enum statusfields
twolineorder[3][16] = { twolineorder[3][19] = {
{ BL_TITLE, { BL_TITLE,
/*xspace*/ BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, /*xspace*/ BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH,
/*xspace*/ BL_ALIGN, /*xspace*/ BL_ALIGN,
/*xspace*/ BL_SCORE, /*xspace*/ BL_SCORE,
BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD }, BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD,
blPAD, blPAD, blPAD },
{ BL_LEVELDESC, { BL_LEVELDESC,
/*xspace*/ BL_GOLD, /*xspace*/ BL_GOLD,
/*xspace*/ BL_HP, BL_HPMAX, /*xspace*/ BL_HP, BL_HPMAX,
@@ -267,32 +268,35 @@ draw_horizontal(boolean border)
/*xspace*/ BL_AC, /*xspace*/ BL_AC,
/*xspace*/ BL_XP, BL_EXP, BL_HD, /*xspace*/ BL_XP, BL_EXP, BL_HD,
/*xspace*/ BL_TIME, /*xspace*/ BL_TIME,
/*xspace*/ BL_HUNGER, BL_CAP, BL_CONDITION, BL_VERS, /*xspace*/ BL_HUNGER, BL_CAP,
BL_WEAPON, BL_ARMOR, BL_TERRAIN, BL_CONDITION, BL_VERS,
BL_FLUSH }, BL_FLUSH },
{ BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, { BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD,
blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD } blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD,
blPAD, blPAD, blPAD }
}, },
threelineorder[3][16] = { /* moves align to line 2, leveldesc+ to 3 */ threelineorder[3][19] = { /* moves align to line 2, leveldesc+ to 3 */
{ BL_TITLE, { BL_TITLE,
/*xspace*/ BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, /*xspace*/ BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH,
/*xspace*/ BL_SCORE, /*xspace*/ BL_SCORE,
BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD }, BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD,
blPAD, blPAD, blPAD },
{ BL_ALIGN, { BL_ALIGN,
/*xspace*/ BL_GOLD, /*xspace*/ BL_GOLD,
/*xspace*/ BL_HP, BL_HPMAX, /*xspace*/ BL_HP, BL_HPMAX,
/*xspace*/ BL_ENE, BL_ENEMAX, /*xspace*/ BL_ENE, BL_ENEMAX,
/*xspace*/ BL_AC, /*xspace*/ BL_AC,
/*xspace*/ BL_XP, BL_EXP, BL_HD, /*xspace*/ BL_XP, BL_EXP, BL_HD,
/*xspace*/ BL_HUNGER, BL_CAP, /*xspace*/ BL_HUNGER, BL_CAP, BL_WEAPON, BL_ARMOR, BL_TERRAIN,
BL_FLUSH, blPAD, blPAD, blPAD }, BL_FLUSH, blPAD, blPAD, blPAD },
{ BL_LEVELDESC, { BL_LEVELDESC,
/*xspace*/ BL_TIME, /*xspace*/ BL_TIME,
/*xspecial*/ BL_CONDITION, /*xspecial*/ BL_CONDITION,
/*xspecial*/ BL_VERS, /*xspecial*/ BL_VERS,
BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD,
blPAD, blPAD, blPAD, blPAD } blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD }
}; };
const enum statusfields (*fieldorder)[16]; const enum statusfields (*fieldorder)[19];
coordxy spacing[MAXBLSTATS], valline[MAXBLSTATS]; coordxy spacing[MAXBLSTATS], valline[MAXBLSTATS];
enum statusfields fld, prev_fld; enum statusfields fld, prev_fld;
char *text, *p, cbuf[BUFSZ], ebuf[STATVAL_WIDTH]; char *text, *p, cbuf[BUFSZ], ebuf[STATVAL_WIDTH];
@@ -401,6 +405,9 @@ draw_horizontal(boolean border)
case BL_ENEMAX: case BL_ENEMAX:
spacing[fld] = 0; /* no leading or extra space */ spacing[fld] = 0; /* no leading or extra space */
break; break;
case BL_WEAPON:
case BL_ARMOR:
case BL_TERRAIN:
case BL_DX: case BL_DX:
case BL_CO: case BL_CO:
case BL_IN: case BL_IN:
@@ -705,16 +712,18 @@ draw_vertical(boolean border)
/* 6:blank (if any of hunger, encumbrance, or conditions appear) */ /* 6:blank (if any of hunger, encumbrance, or conditions appear) */
BL_HUNGER, BL_CAP, /* these two are shown on same line */ BL_HUNGER, BL_CAP, /* these two are shown on same line */
BL_CONDITION, /* shown three per line so may take up to four lines */ BL_CONDITION, /* shown three per line so may take up to four lines */
BL_WEAPON, BL_ARMOR, BL_TERRAIN, /* same line if 2, two lines if 3 */
/* 1:blank (bottom justified) */ /* 1:blank (bottom justified) */
BL_VERS, BL_VERS,
BL_FLUSH BL_FLUSH
}; };
static const enum statusfields shrinkorder[] = { static const enum statusfields shrinkorder[] = {
BL_VERS, BL_STR, BL_SCORE, BL_TIME, BL_LEVELDESC, BL_HP, BL_VERS, BL_STR, BL_SCORE, BL_TIME, BL_LEVELDESC, BL_HP,
BL_TERRAIN, BL_ARMOR, BL_WEAPON,
BL_CONDITION, BL_CAP, BL_HUNGER BL_CONDITION, BL_CAP, BL_HUNGER
}; };
coordxy spacing[MAXBLSTATS]; coordxy spacing[MAXBLSTATS];
int i, fld, cap_and_hunger, time_and_score, cond_count, int i, fld, cap_and_hunger, time_and_score, cond_count, wep_arm_ter,
sho_vers, per_line; sho_vers, per_line;
char *text; char *text;
#ifdef STATUS_HILITES #ifdef STATUS_HILITES
@@ -758,6 +767,13 @@ draw_vertical(boolean border)
} }
per_line = 2; /* will be changed to 3 if status becomes too tall */ per_line = 2; /* will be changed to 3 if status becomes too tall */
sho_vers = (status_activefields[BL_VERS] ? 1 : 0); sho_vers = (status_activefields[BL_VERS] ? 1 : 0);
wep_arm_ter = 0;
if (status_activefields[BL_WEAPON] && *status_vals_long[BL_WEAPON])
wep_arm_ter |= 1;
if (status_activefields[BL_ARMOR] && *status_vals_long[BL_ARMOR])
wep_arm_ter |= 2;
if (status_activefields[BL_TERRAIN] && *status_vals_long[BL_TERRAIN])
wep_arm_ter |= 4;
/* count how many lines we'll need; we normally space several groups of /* count how many lines we'll need; we normally space several groups of
fields with blank lines but might need to compress some of those out */ fields with blank lines but might need to compress some of those out */
@@ -802,6 +818,32 @@ draw_vertical(boolean border)
if (cond_count > per_line) if (cond_count > per_line)
height_needed += (cond_count - 1) / per_line; height_needed += (cond_count - 1) / per_line;
break; break;
case BL_WEAPON:
/* two lines if no hunger or encumbrance and no conditions,
otherwise one line if weapon status being is shown */
spacing[fld] = ((wep_arm_ter & 1) == 1) /* show wep */
? ((!cond_count && !cap_and_hunger) ? 2 : 1)
: 0; /* no wep */
break;
case BL_ARMOR:
/* two lines if no hunger or encumbrance and no conditions
and no weapon status, otherwise one line if no weapon
status, else same line as weapon status */
spacing[fld] = ((wep_arm_ter & 3) == 2) /* show arm, no wep */
? ((!cond_count && !cap_and_hunger) ? 2 : 1)
: 0; /* wep and arm (on same line) or no arm */
break;
case BL_TERRAIN:
/* two lines if no hunger or encumbrance and no conditions
and no weapon status and no armor status, otherwise one
line if both weapon status and armor status or neither of
them and terrain status is being shown */
spacing[fld] = ((wep_arm_ter & 7) == 4) /* show ter, no wep|arm */
? ((!cond_count && !cap_and_hunger) ? 2 : 1)
/* if all three, put terrain on next line; else
same line for wep+ter or arm+ter or no ter */
: (((wep_arm_ter & 7) == 7) ? 1 : 0);
break;
case BL_VERS: case BL_VERS:
spacing[fld] = sho_vers ? 2 : 0; spacing[fld] = sho_vers ? 2 : 0;
break; break;
@@ -883,7 +925,11 @@ draw_vertical(boolean border)
the first (or only) and keep it for the second (if both) */ the first (or only) and keep it for the second (if both) */
if (*text == ' ' if (*text == ' '
&& (fld == BL_HUNGER && (fld == BL_HUNGER
|| (fld == BL_CAP && cap_and_hunger != 3))) || (fld == BL_CAP && cap_and_hunger != 3)
|| fld == BL_WEAPON
|| (fld == BL_ARMOR && (wep_arm_ter & 3) == 2)
|| (fld == BL_TERRAIN && ((wep_arm_ter & 7) == 4
|| (wep_arm_ter & 7) == 7))))
++text; ++text;
#ifdef STATUS_HILITES #ifdef STATUS_HILITES
coloridx = curses_status_colors[fld]; /* includes attributes */ coloridx = curses_status_colors[fld]; /* includes attributes */
@@ -1239,6 +1285,9 @@ curs_vert_status_vals(int win_width)
case BL_EXP: case BL_EXP:
case BL_HUNGER: case BL_HUNGER:
case BL_CAP: case BL_CAP:
case BL_WEAPON:
case BL_ARMOR:
case BL_TERRAIN:
case BL_TITLE: case BL_TITLE:
use_name = FALSE; use_name = FALSE;
break; break;
+18 -13
View File
@@ -121,6 +121,7 @@ struct window_procs tty_procs = {
#if !defined(NO_TERMS) || defined(WIN32CON) #if !defined(NO_TERMS) || defined(WIN32CON)
| WC2_EXTRACOLORS | WC2_EXTRACOLORS
#endif #endif
| WC2_EXTRASTATUS
), ),
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* color availability */ {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* color availability */
tty_init_nhwindows, tty_player_selection, tty_askname, tty_get_nh_event, tty_init_nhwindows, tty_player_selection, tty_askname, tty_get_nh_event,
@@ -4271,27 +4272,29 @@ static const char *const encvals[3][6] = {
{ "", "Brd", "Strs", "Strn", "Ovtx", "Ovld" } { "", "Brd", "Strs", "Strn", "Ovtx", "Ovld" }
}; };
#define blPAD BL_FLUSH #define blPAD BL_FLUSH
#define MAX_PER_ROW 16 #define MAX_PER_ROW 19
/* 2 or 3 status lines */ /* 2 or 3 status lines */
static const enum statusfields static const enum statusfields
twolineorder[3][MAX_PER_ROW] = { twolineorder[3][MAX_PER_ROW] = {
{ BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, BL_ALIGN, { BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, BL_ALIGN,
BL_SCORE, BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD }, BL_SCORE, BL_FLUSH,
blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD },
{ BL_LEVELDESC, BL_GOLD, BL_HP, BL_HPMAX, BL_ENE, BL_ENEMAX, { BL_LEVELDESC, BL_GOLD, BL_HP, BL_HPMAX, BL_ENE, BL_ENEMAX,
BL_AC, BL_XP, BL_EXP, BL_HD, BL_TIME, BL_HUNGER, BL_AC, BL_XP, BL_EXP, BL_HD, BL_TIME, BL_HUNGER, BL_CAP,
BL_CAP, BL_CONDITION, BL_VERS, BL_FLUSH }, BL_CONDITION, BL_WEAPON, BL_ARMOR, BL_TERRAIN, BL_VERS, BL_FLUSH },
/* third row of array isn't used for twolineorder */ /* third row of array isn't used for twolineorder */
{ BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, { BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD,
blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD } blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD }
}, },
/* Align moved from 1 to 2, Leveldesc+Time+Cond+Vers moved from 2 to 3 */ /* Align moved from 1 to 2, Leveldesc+Time+Cond+Vers moved from 2 to 3 */
threelineorder[3][MAX_PER_ROW] = { threelineorder[3][MAX_PER_ROW] = {
{ BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, { BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, BL_SCORE, BL_FLUSH,
BL_SCORE, BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD }, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD },
{ BL_ALIGN, BL_GOLD, BL_HP, BL_HPMAX, BL_ENE, BL_ENEMAX, { BL_ALIGN, BL_GOLD, BL_HP, BL_HPMAX, BL_ENE, BL_ENEMAX,
BL_AC, BL_XP, BL_EXP, BL_HD, BL_HUNGER, BL_AC, BL_XP, BL_EXP, BL_HD, BL_HUNGER, BL_CAP,
BL_CAP, BL_FLUSH, blPAD, blPAD, blPAD }, BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD },
{ BL_LEVELDESC, BL_TIME, BL_CONDITION, BL_VERS, BL_FLUSH, blPAD, { BL_LEVELDESC, BL_TIME, BL_CONDITION, BL_WEAPON, BL_ARMOR, BL_TERRAIN,
BL_VERS, BL_FLUSH, blPAD,
blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD } blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD }
}; };
static const enum statusfields (*fieldorder)[MAX_PER_ROW]; static const enum statusfields (*fieldorder)[MAX_PER_ROW];
@@ -4378,7 +4381,8 @@ tty_status_enablefield(
* BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, * 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_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_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX,
* BL_LEVELDESC, BL_EXP, BL_CONDITION, BL_VERS * BL_LEVELDESC, BL_EXP, BL_CONDITION,
* BL_WEAPON, BL_ARMOR, BL_TERRAIN, BL_VERS
* -- fldindex could also be BL_FLUSH (-1), which is not really * -- fldindex could also be BL_FLUSH (-1), which is not really
* a field index, but is a special trigger to tell the * a field index, but is a special trigger to tell the
* windowport that it should output all changes received * windowport that it should output all changes received
@@ -4748,7 +4752,8 @@ status_sanity_check(void)
"BL_ENE", "BL_ENEMAX", "BL_XP", "BL_AC", "BL_HD", /* 11..15 */ "BL_ENE", "BL_ENEMAX", "BL_XP", "BL_AC", "BL_HD", /* 11..15 */
"BL_TIME", "BL_HUNGER", "BL_HP", "BL_HPMAX", /* 16..19 */ "BL_TIME", "BL_HUNGER", "BL_HP", "BL_HPMAX", /* 16..19 */
"BL_LEVELDESC", "BL_EXP", "BL_CONDITION", /* 20..22 */ "BL_LEVELDESC", "BL_EXP", "BL_CONDITION", /* 20..22 */
"BL_VERS", /* 23 */ "BL_WEAPON", "BL_ARMOR", "BL_TERRAIN", /* 23..25 */
"BL_VERS", /* 26 */
}; };
static boolean in_sanity_check = FALSE; static boolean in_sanity_check = FALSE;
int i; int i;
@@ -4768,8 +4773,8 @@ status_sanity_check(void)
a corresponding expansion of idxtext[] here */ a corresponding expansion of idxtext[] here */
if (i < SIZE(idxtext)) if (i < SIZE(idxtext))
Strcpy(indxtxt, idxtext[i]); Strcpy(indxtxt, idxtext[i]);
else else /* blstats[] increased without doing same for idxtext[] */
Sprintf(indxtxt, "%d", i); Sprintf(indxtxt, "#%d", i);
Sprintf(panicmsg, "failed on tty_status[NOW][%s].", indxtxt); Sprintf(panicmsg, "failed on tty_status[NOW][%s].", indxtxt);
paniclog("status_sanity_check", panicmsg); paniclog("status_sanity_check", panicmsg);
tty_status[NOW][i].sanitycheck = FALSE; tty_status[NOW][i].sanitycheck = FALSE;