From 118899a5f0dfcce29078ebc41476e5d2e50cac04 Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 18 Jun 2026 09:48:34 -0400 Subject: [PATCH 01/10] update tested versions of Visual Studio 2026-06-18 --- sys/windows/Makefile.nmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index 1029b68a9..5a0906c8b 100644 --- a/sys/windows/Makefile.nmake +++ b/sys/windows/Makefile.nmake @@ -8,8 +8,8 @@ # MS Visual Studio Visual C++ compiler # # Visual Studio Compilers Tested: -# - Microsoft Visual Studio Community 2022 v 17.14.33 -# - Microsoft Visual Studio Community 2026 v 18.7.0 +# - Microsoft Visual Studio Community 2022 v 17.14.35 +# - Microsoft Visual Studio Community 2026 v 18.7.1 # #============================================================================== # This is used for building two distinct executables of NetHack: @@ -1204,7 +1204,7 @@ rc=Rc.exe # # Recently tested versions: TESTEDVS2022 = 14.44.35225.0 -TESTEDVS2026 = 14.51.36247.0 +TESTEDVS2026 = 14.51.36248.0 VS20261ST = 1450000000 VS2026CUR = $(TESTEDVS2026:.=) From b54c38c65bce32c1fab174dec4651d1220775396 Mon Sep 17 00:00:00 2001 From: copperwater Date: Fri, 19 Jun 2026 08:40:35 -0400 Subject: [PATCH 02/10] Fix: a shopkeeper msg was printed even when deaf The message in question is '[shopkeeper] says "You be careful with my [item]!"' when you wield a shop-owned item, but it was being printed even when the hero is deaf. Following other examples of shopkeeper dialogue, the correct thing to do here is not to suppress the message if deaf but instead provide some nonverbal feedback, so that is what I did. --- src/wield.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/wield.c b/src/wield.c index 7c75cab31..51920b7d4 100644 --- a/src/wield.c +++ b/src/wield.c @@ -262,8 +262,13 @@ ready_weapon(struct obj *wep) if ((this_shkp = shop_keeper(inside_shop(u.ux, u.uy))) != (struct monst *) 0) { - pline("%s says \"You be careful with my %s!\"", - shkname(this_shkp), xname(wep)); + /* check msound because we don't have access to muteshk() */ + if (!Deaf && this_shkp->data->msound > MS_ANIMAL) + pline("%s says \"You be careful with my %s!\"", + shkname(this_shkp), xname(wep)); + else + pline("%s looks apprehensive about your wielding %s %s.", + shkname(this_shkp), mhis(this_shkp), xname(wep)); } } } From 7a23470c33513fdd6d458f1ea1e328454ca56520 Mon Sep 17 00:00:00 2001 From: copperwater Date: Fri, 19 Jun 2026 09:27:25 -0400 Subject: [PATCH 03/10] Fix: thrown ball travel did not check for regions This is an issue that we discovered in TNNT last year when we added a custom region with effects that trigger upon entry: it was possible to bypass those effects by entering the region via a thrown iron ball. This can be demonstrated by creating a poison gas cloud and then dragging oneself inside the cloud behind a thrown ball: you land in the cloud and are surrounded by poisonous gas, but are unharmed by it. This commit fixes the iron ball code to call in_out_region when appropriate, which handles the side effects of entering and exiting regions in addition to preventing travel into or out of a hypothetical region that blocks entry or exit. --- src/ball.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/ball.c b/src/ball.c index b622cb691..1531c3b9e 100644 --- a/src/ball.c +++ b/src/ball.c @@ -808,7 +808,8 @@ drag_ball(coordxy x, coordxy y, int *bc_control, miss(xname(uball), victim); } /* now check again in case mon died */ - if (!m_at(uchain->ox, uchain->oy)) { + if (!m_at(uchain->ox, uchain->oy) + && in_out_region(uchain->ox, uchain->oy)) { u.ux = uchain->ox; u.uy = uchain->oy; newsym(u.ux0, u.uy0); @@ -820,6 +821,9 @@ drag_ball(coordxy x, coordxy y, int *bc_control, *ballx = uchain->ox; *bally = uchain->oy; move_bc(0, *bc_control, *ballx, *bally, *chainx, *chainy); + /* weirdness: you were dragged back on account of the ball falling + * into the pit, but if you escape the pit, the ball is "on top of" + * the pit and does not hinder your movement further */ spoteffects(TRUE); return FALSE; } @@ -932,10 +936,11 @@ drop_ball(coordxy x, coordxy y) && (is_pool(x, y) || ((t = t_at(x, y)) && (is_pit(t->ttyp) - || is_hole(t->ttyp))))) { + || is_hole(t->ttyp)))) + && in_out_region(x, y)) { u.ux = x; u.uy = y; - } else { + } else if (in_out_region(x - u.dx, y - u.dy)) { u.ux = x - u.dx; u.uy = y - u.dy; } From 04963ecf81ac7ff7f4786f3934b429ee208d57fb Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 19 Jun 2026 10:08:42 -0400 Subject: [PATCH 04/10] follow-up: add a general catch for "says" --- include/extern.h | 1 + src/minion.c | 3 ++- src/rumors.c | 5 +++-- src/shk.c | 6 ++++++ src/wield.c | 4 ++-- 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/include/extern.h b/include/extern.h index 194547d2b..5da653c38 100644 --- a/include/extern.h +++ b/include/extern.h @@ -2996,6 +2996,7 @@ extern void credit_report(struct monst *shkp, int idx, boolean silent) NONNULLARG1; extern void use_unpaid_trapobj(struct obj *, coordxy, coordxy) NONNULLARG1; extern void noisy_shop(struct mkroom *); +extern const char *says(void); /* ### shknam.c ### */ diff --git a/src/minion.c b/src/minion.c index 03a782727..86a976908 100644 --- a/src/minion.c +++ b/src/minion.c @@ -299,7 +299,8 @@ demon_talk(struct monst *mtmp) pline("%s says, \"Good hunting, %s.\"", Amonnam(mtmp), flags.female ? "Sister" : "Brother"); else if (canseemon(mtmp)) - pline("%s says something.", Amonnam(mtmp)); + pline("%s %s something.", Amonnam(mtmp), + says()); if (!tele_restrict(mtmp)) (void) rloc(mtmp, RLOC_MSG); return 1; diff --git a/src/rumors.c b/src/rumors.c index 22d3e3077..b78bd2403 100644 --- a/src/rumors.c +++ b/src/rumors.c @@ -554,10 +554,11 @@ outrumor( switch (mechanism) { case BY_ORACLE: /* Oracle delivers the rumor */ - pline("True to her word, the Oracle %ssays: ", + pline("True to her word, the Oracle %s%s: ", (!rn2(4) ? "offhandedly " : (!rn2(3) ? "casually " - : (rn2(2) ? "nonchalantly " : "")))); + : (rn2(2) ? "nonchalantly " : ""))), + says()); SetVoice((struct monst *) 0, 0, 80, voice_oracle); verbalize1(line); /* [WIS exercised by getrumor()] */ diff --git a/src/shk.c b/src/shk.c index 749df40ae..117003f46 100644 --- a/src/shk.c +++ b/src/shk.c @@ -2049,6 +2049,12 @@ dopay(void) return paid ? ECMD_TIME : ECMD_OK; } +const char * +says(void) +{ + return Deaf ? "signs" : "says"; +} + /* for menustyle=Traditional, choose between paying for everything (by declining to itemize), asking item-by-item (by accepting itemization), or switch to selecting via menu (special 'm' answer at "Itemize? [ynq m]" diff --git a/src/wield.c b/src/wield.c index 51920b7d4..515caa938 100644 --- a/src/wield.c +++ b/src/wield.c @@ -264,8 +264,8 @@ ready_weapon(struct obj *wep) != (struct monst *) 0) { /* check msound because we don't have access to muteshk() */ if (!Deaf && this_shkp->data->msound > MS_ANIMAL) - pline("%s says \"You be careful with my %s!\"", - shkname(this_shkp), xname(wep)); + pline("%s %s \"You be careful with my %s!\"", + shkname(this_shkp), says(), xname(wep)); else pline("%s looks apprehensive about your wielding %s %s.", shkname(this_shkp), mhis(this_shkp), xname(wep)); From 43ae611d64ed7167e14da691b6af213550357d1f Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 19 Jun 2026 10:20:50 -0400 Subject: [PATCH 05/10] fixes5-0-1.txt entries following pull request --- doc/fixes5-0-1.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/fixes5-0-1.txt b/doc/fixes5-0-1.txt index 3fa4b761d..44e86f817 100644 --- a/doc/fixes5-0-1.txt +++ b/doc/fixes5-0-1.txt @@ -61,6 +61,8 @@ fix a bug in which steeds would continuously gain movement points while being ridden when hero's behavior makes erinyes stronger, cap level at 49 rather than 50 with inventory item action for leash, distingluish leash-in-use from empty one +deafness check was missing from a shopkeeper message in ready_weapon() +thrown ball travel did not check for regions (pr #1595 by copperwater) Platform- and/or Interface-Specific Fixes From 24adb1c592a306b67fbb3fa928fbe2fb5497ae79 Mon Sep 17 00:00:00 2001 From: Doktor L Date: Sat, 30 May 2026 18:03:20 +0200 Subject: [PATCH 06/10] curses: use existing background parameter for pile The blue background colour for piles was implemented by changing glyph colour to curses colour pair with the desired background, and then passed to curses_putch which takes character and background colour and makes curses colour pair out of them once again. Pass blue background to curses_putch instead and let it create curses colour pair just once. --- include/wincurs.h | 1 - win/curses/cursmain.c | 6 ++++-- win/curses/curswins.c | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/include/wincurs.h b/include/wincurs.h index 0911cf11c..aea0e9ebf 100644 --- a/include/wincurs.h +++ b/include/wincurs.h @@ -145,7 +145,6 @@ extern void curses_puts(winid wid, int attr, const char *text); extern void curses_clear_nhwin(winid wid); extern void curses_alert_win_border(winid wid, boolean onoff); extern void curses_alert_main_borders(boolean onoff); -extern int get_framecolor(int nhcolor, int framecolor); extern void curses_draw_map(int sx, int sy, int ex, int ey); extern boolean curses_map_borders(int *sx, int *sy, int *ex, int *ey, int ux, int uy); diff --git a/win/curses/cursmain.c b/win/curses/cursmain.c index 448d8c77d..ee45eb059 100644 --- a/win/curses/cursmain.c +++ b/win/curses/cursmain.c @@ -940,6 +940,7 @@ curses_print_glyph( int glyph; int ch; int color; + int framecolor; int nhcolor = 0; unsigned int special; int attr = -1; @@ -948,6 +949,7 @@ curses_print_glyph( special = glyphinfo->gm.glyphflags; ch = glyphinfo->ttychar; color = glyphinfo->gm.sym.color; + framecolor = bkglyphinfo->framecolor; /* Extra color handling * FIQ: The curses library does not support truecolor, only the more limited 256 * color mode. On top of this, the windowport only supports 16 color mode. @@ -981,7 +983,7 @@ curses_print_glyph( */ if ((special & MG_OBJPILE) && iflags.hilite_pile) { if (iflags.wc_color) - color = get_framecolor(color, CLR_BLUE); + framecolor = CLR_BLUE; else /* if (iflags.use_inverse) */ attr = A_REVERSE; } @@ -1007,7 +1009,7 @@ curses_print_glyph( ? glyphinfo->gm.u : NULL, #endif (nhcolor != 0) ? nhcolor : color, - bkglyphinfo->framecolor, attr); + framecolor, attr); } diff --git a/win/curses/curswins.c b/win/curses/curswins.c index 21e1bae09..50f8f171c 100644 --- a/win/curses/curswins.c +++ b/win/curses/curswins.c @@ -763,7 +763,7 @@ is_main_window(winid wid) coordinates without a refresh. Currently only used for the map. */ /* convert nhcolor (fg) and framecolor (bg) to curses colorpair */ -int +static int get_framecolor(int nhcolor, int framecolor) { /* curses_toggle_color_attr() adds the +1 and takes care of COLORS < 16 */ From f3d292e111e509a4ad6e426cc0f00da0238d97c6 Mon Sep 17 00:00:00 2001 From: Doktor L Date: Sat, 30 May 2026 18:13:36 +0200 Subject: [PATCH 07/10] Combine three arguments of curses_putch into one It will need another parameter for 256-colour support. To avoid having too many arguments, put glyph colour, background colour and attributes into a struct and, since to curses library all of that is attributes that are handled by same function, call the struct "gryph attributes". curses_putch was also declared in two different headers, remove one of those declarations. --- include/wincurs.h | 8 -------- win/curses/cursmain.c | 27 +++++++++++++-------------- win/curses/curswins.c | 8 ++++---- win/curses/curswins.h | 13 +++++++++---- 4 files changed, 26 insertions(+), 30 deletions(-) diff --git a/include/wincurs.h b/include/wincurs.h index aea0e9ebf..43780e276 100644 --- a/include/wincurs.h +++ b/include/wincurs.h @@ -128,14 +128,6 @@ extern void curses_refresh_nethack_windows(void); extern void curses_del_nhwin(winid wid); extern void curses_del_wid(winid wid); extern void curs_destroy_all_wins(void); -#ifdef ENHANCED_SYMBOLS -extern void curses_putch(winid wid, int x, int y, int ch, - struct unicode_representation *u, int color, - int framecolor, int attrs); -#else -extern void curses_putch(winid wid, int x, int y, int ch, int color, - int framecolor, int attrs); -#endif extern void curses_get_window_size(winid wid, int *height, int *width); extern boolean curses_window_has_border(winid wid); extern boolean curses_window_exists(winid wid); diff --git a/win/curses/cursmain.c b/win/curses/cursmain.c index ee45eb059..3966771f6 100644 --- a/win/curses/cursmain.c +++ b/win/curses/cursmain.c @@ -7,6 +7,7 @@ #include "hack.h" #include "color.h" #include "wincurs.h" +#include "curswins.h" #ifdef CURSES_UNICODE #include #endif @@ -939,17 +940,16 @@ curses_print_glyph( { int glyph; int ch; - int color; - int framecolor; + struct glyph_attributes attr; int nhcolor = 0; unsigned int special; - int attr = -1; + attr.attribute_flags = -1; glyph = glyphinfo->glyph; special = glyphinfo->gm.glyphflags; ch = glyphinfo->ttychar; - color = glyphinfo->gm.sym.color; - framecolor = bkglyphinfo->framecolor; + attr.color = glyphinfo->gm.sym.color; + attr.framecolor = bkglyphinfo->framecolor; /* Extra color handling * FIQ: The curses library does not support truecolor, only the more limited 256 * color mode. On top of this, the windowport only supports 16 color mode. @@ -958,7 +958,7 @@ curses_print_glyph( if (glyphinfo->gm.customcolor != 0 && (curses_procs.wincap2 & WC2_EXTRACOLORS) != 0) { if ((glyphinfo->gm.customcolor & NH_BASIC_COLOR) != 0) { - color = COLORVAL(glyphinfo->gm.customcolor); + attr.color = COLORVAL(glyphinfo->gm.customcolor); #if 0 } else { /* 24-bit color, NH_BASIC_COLOR == 0 */ @@ -967,10 +967,10 @@ curses_print_glyph( } } if ((special & MG_PET) && iflags.hilite_pet) { - attr = curses_convert_attr(iflags.wc2_petattr); + attr.attribute_flags = curses_convert_attr(iflags.wc2_petattr); } if ((special & MG_DETECT) && iflags.use_inverse) { - attr = A_REVERSE; + attr.attribute_flags = A_REVERSE; } if (SYMHANDLING(H_DEC)) ch = curses_convert_glyph(ch, glyph); @@ -983,9 +983,9 @@ curses_print_glyph( */ if ((special & MG_OBJPILE) && iflags.hilite_pile) { if (iflags.wc_color) - framecolor = CLR_BLUE; + attr.framecolor = CLR_BLUE; else /* if (iflags.use_inverse) */ - attr = A_REVERSE; + attr.attribute_flags = A_REVERSE; } /* water and lava look the same except for color; when color is off (checked by core), render lava in inverse video so that it looks @@ -994,11 +994,11 @@ curses_print_glyph( if ((special & (MG_BW_LAVA | MG_BW_ICE | MG_BW_SINK | MG_BW_ENGR)) != 0 && iflags.use_inverse) { /* reset_glyphmap() only sets MG_BW_foo if color is off */ - attr = A_REVERSE; + attr.attribute_flags = A_REVERSE; } /* highlight female monsters (wizard mode option) */ if ((special & MG_FEMALE) && wizard && iflags.wizmgender) { - attr = A_REVERSE; + attr.attribute_flags = A_REVERSE; } } @@ -1008,8 +1008,7 @@ curses_print_glyph( && glyphinfo->gm.u && glyphinfo->gm.u->utf8str) ? glyphinfo->gm.u : NULL, #endif - (nhcolor != 0) ? nhcolor : color, - framecolor, attr); + &attr); } diff --git a/win/curses/curswins.c b/win/curses/curswins.c index 50f8f171c..ee28d4508 100644 --- a/win/curses/curswins.c +++ b/win/curses/curswins.c @@ -532,7 +532,7 @@ curses_putch(winid wid, int x, int y, int ch, #ifdef ENHANCED_SYMBOLS struct unicode_representation *unicode_representation, #endif - int color, int framecolor, int attr) + const struct glyph_attributes *attr) { static boolean map_initted = FALSE; int sx, sy, ex, ey; @@ -554,9 +554,9 @@ curses_putch(winid wid, int x, int y, int ch, --x; /* map column [0] is not used; draw column [1] in first screen col */ map[y][x].ch = ch; - map[y][x].color = color; - map[y][x].framecolor = framecolor; - map[y][x].attr = attr; + map[y][x].color = attr->color; + map[y][x].framecolor = attr->framecolor; + map[y][x].attr = attr->attribute_flags; #ifdef ENHANCED_SYMBOLS map[y][x].unicode_representation = unicode_representation; #endif diff --git a/win/curses/curswins.h b/win/curses/curswins.h index ebf4c9bb5..992fea55a 100644 --- a/win/curses/curswins.h +++ b/win/curses/curswins.h @@ -6,6 +6,11 @@ #ifndef CURSWIN_H # define CURSWIN_H +struct glyph_attributes { + int color; + int framecolor; + int attribute_flags; +}; /* Global declarations */ @@ -27,11 +32,11 @@ void curses_del_wid(winid wid); void curs_destroy_all_wins(void); #ifdef ENHANCED_SYMBOLS void curses_putch(winid wid, int x, int y, int ch, - struct unicode_representation *ur, int color, - int framecolor, int attrs); + struct unicode_representation *ur, + const struct glyph_attributes *attr); #else -void curses_putch(winid wid, int x, int y, int ch, int color, - int framecolor, int attrs); +void curses_putch(winid wid, int x, int y, int ch, + const struct glyph_attributes *attr); #endif void curses_get_window_xy(winid wid, int *x, int *y); boolean curses_window_has_border(winid wid); From 7036f902d2aabad9d522133509aa9ee59d831398 Mon Sep 17 00:00:00 2001 From: Doktor L Date: Sat, 30 May 2026 18:28:08 +0200 Subject: [PATCH 08/10] Pass base-256 colour index to curses_putch And to its inner function write_char --- src/coloratt.c | 3 ++- win/curses/cursmain.c | 10 ++++------ win/curses/curswins.c | 6 ++++-- win/curses/curswins.h | 3 ++- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/coloratt.c b/src/coloratt.c index 10f216ba6..a5d040fda 100644 --- a/src/coloratt.c +++ b/src/coloratt.c @@ -886,7 +886,8 @@ static struct { int index; uint32 value; } color_256_definitions[] = { - /* color values are from unnethack */ + /* from unnethack - these are the colors used by xterm + when $TERM is set to xterm-256color */ { 16, 0x000000 }, { 17, 0x00005f }, { 18, 0x000087 }, { 19, 0x0000af }, { 20, 0x0000d7 }, { 21, 0x0000ff }, { 22, 0x005f00 }, { 23, 0x005f5f }, { 24, 0x005f87 }, diff --git a/win/curses/cursmain.c b/win/curses/cursmain.c index 3966771f6..f6b5e3013 100644 --- a/win/curses/cursmain.c +++ b/win/curses/cursmain.c @@ -941,14 +941,14 @@ curses_print_glyph( int glyph; int ch; struct glyph_attributes attr; - int nhcolor = 0; unsigned int special; attr.attribute_flags = -1; glyph = glyphinfo->glyph; special = glyphinfo->gm.glyphflags; ch = glyphinfo->ttychar; - attr.color = glyphinfo->gm.sym.color; + attr.basic_color = glyphinfo->gm.sym.color; + attr.color256 = 0; attr.framecolor = bkglyphinfo->framecolor; /* Extra color handling * FIQ: The curses library does not support truecolor, only the more limited 256 @@ -958,12 +958,10 @@ curses_print_glyph( if (glyphinfo->gm.customcolor != 0 && (curses_procs.wincap2 & WC2_EXTRACOLORS) != 0) { if ((glyphinfo->gm.customcolor & NH_BASIC_COLOR) != 0) { - attr.color = COLORVAL(glyphinfo->gm.customcolor); -#if 0 + attr.basic_color = COLORVAL(glyphinfo->gm.customcolor); } else { /* 24-bit color, NH_BASIC_COLOR == 0 */ - nhcolor = COLORVAL(glyphinfo->gm.customcolor); -#endif + attr.color256 = glyphinfo->gm.color256idx; } } if ((special & MG_PET) && iflags.hilite_pet) { diff --git a/win/curses/curswins.c b/win/curses/curswins.c index ee28d4508..6ce123c34 100644 --- a/win/curses/curswins.c +++ b/win/curses/curswins.c @@ -40,7 +40,8 @@ typedef struct nhwd { typedef struct nhchar { int ch; /* character */ - int color; /* color info for character */ + int color; /* basic color info for character */ + int color256; /* extended color for the character, 0 if none */ int framecolor; /* background color info for character */ int attr; /* attributes of character */ struct unicode_representation *unicode_representation; @@ -554,7 +555,8 @@ curses_putch(winid wid, int x, int y, int ch, --x; /* map column [0] is not used; draw column [1] in first screen col */ map[y][x].ch = ch; - map[y][x].color = attr->color; + map[y][x].color = attr->basic_color; + map[y][x].color256 = attr->color256; map[y][x].framecolor = attr->framecolor; map[y][x].attr = attr->attribute_flags; #ifdef ENHANCED_SYMBOLS diff --git a/win/curses/curswins.h b/win/curses/curswins.h index 992fea55a..13985ce1b 100644 --- a/win/curses/curswins.h +++ b/win/curses/curswins.h @@ -7,7 +7,8 @@ # define CURSWIN_H struct glyph_attributes { - int color; + int basic_color; // basic color + int color256; // extended color (0 if not defined), used when supported int framecolor; int attribute_flags; }; From 19c87585fa5cabb8869bc70e2c65c215a66b981b Mon Sep 17 00:00:00 2001 From: Doktor L Date: Sat, 30 May 2026 19:29:11 +0200 Subject: [PATCH 09/10] Create curses colour pairs for 256 colours Keep only 8 background colours but if curses supports 256 colours and 256*8 colours pairs, create colours pairs for 256 foregrounds rather than just 16. --- win/curses/cursinit.c | 12 +++++++----- win/curses/cursmisc.c | 8 +++++++- win/curses/cursmisc.h | 3 +++ win/curses/curswins.c | 6 +++++- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/win/curses/cursinit.c b/win/curses/cursinit.c index 1135d0eb8..523ae92d3 100644 --- a/win/curses/cursinit.c +++ b/win/curses/cursinit.c @@ -7,6 +7,7 @@ #include "hack.h" #include "wincurs.h" #include "cursinit.h" +#include "cursmisc.h" /* Initialization and startup functions for curses interface */ @@ -334,18 +335,18 @@ curses_init_nhcolors(void) /* COLOR_foo + 8 means COLOR | A_BOLD when COLORS < 16 */ /* otherwise assume the terminal has least 16 different colors */ /* these map to the NetHack CLR_ defines */ - static const int fg_clr[16] = { + static const int basic_fg_clr[16] = { COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW, COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE, -1, COLOR_RED + 8, COLOR_GREEN + 8, COLOR_YELLOW + 8, COLOR_BLUE + 8, COLOR_MAGENTA + 8, COLOR_CYAN + 8, COLOR_WHITE + 8 }; - static const int bg_clr[8] = { + static const int bg_clr[CURSES_NUM_BACKGROUND_COLORS] = { -1, COLOR_RED, COLOR_GREEN, COLOR_YELLOW, COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE }; int bg, nhclr; - int maxc = (COLORS >= 16) ? 16 : 8; + int maxc = curses_has_256color() ? 256 : (COLORS >= 16) ? 16 : 8; if (!has_colors()) return; @@ -353,8 +354,9 @@ curses_init_nhcolors(void) use_default_colors(); for (nhclr = CLR_BLACK; nhclr < maxc; nhclr++) { - for (bg = 0; bg < 8; bg++) { - init_pair((maxc * bg) + nhclr + 1, fg_clr[nhclr], bg_clr[bg]); + for (bg = 0; bg < CURSES_NUM_BACKGROUND_COLORS; bg++) { + int fg_color = (nhclr < 16) ? basic_fg_clr[nhclr] : nhclr; + init_pair((maxc * bg) + nhclr + 1, fg_color, bg_clr[bg]); } } diff --git a/win/curses/cursmisc.c b/win/curses/cursmisc.c index efa5da83a..30e782411 100644 --- a/win/curses/cursmisc.c +++ b/win/curses/cursmisc.c @@ -91,6 +91,12 @@ curses_read_char(void) return ch; } +boolean +curses_has_256color(void) +{ + return (COLORS >= 256) && (COLOR_PAIRS >= 256 * CURSES_NUM_BACKGROUND_COLORS); +} + /* Turn on or off the specified color and / or attribute */ void @@ -147,7 +153,7 @@ curses_toggle_color_attr(WINDOW *win, int color, int attr, int onoff) if (use_bold) { wattron(win, A_BOLD); } - wattron(win, COLOR_PAIR(curses_color)); + wcolor_set(win, curses_color, &curses_color); } if (attr != NONE) { diff --git a/win/curses/cursmisc.h b/win/curses/cursmisc.h index dc79ebfbe..81c34135d 100644 --- a/win/curses/cursmisc.h +++ b/win/curses/cursmisc.h @@ -8,8 +8,11 @@ /* Global declarations */ +#define CURSES_NUM_BACKGROUND_COLORS 8 + int curses_getch(void); int curses_read_char(void); +boolean curses_has_256color(void); void curses_toggle_color_attr(WINDOW *win, int color, int attr, int onoff); void curses_menu_color_attr(WINDOW *win, int color, int attr, int onoff); void curses_bail(const char *mesg); diff --git a/win/curses/curswins.c b/win/curses/curswins.c index 6ce123c34..a09b3f3f0 100644 --- a/win/curses/curswins.c +++ b/win/curses/curswins.c @@ -769,7 +769,11 @@ static int get_framecolor(int nhcolor, int framecolor) { /* curses_toggle_color_attr() adds the +1 and takes care of COLORS < 16 */ - return (16 * (framecolor % 8)) + (nhcolor % 16); + if (curses_has_256color()) { + return (256 * (framecolor % 8)) + (nhcolor % 256); + } else + return (16 * (framecolor % 8)) + (nhcolor % 16); + } static void From 08ac68b754c1f22ae6e6b7a33d323e682333c5b6 Mon Sep 17 00:00:00 2001 From: Doktor L Date: Sat, 30 May 2026 19:31:37 +0200 Subject: [PATCH 10/10] Use 256 colours in curses window port Only glyphs that are customised via config can have colours different from the basic 16. For such glyphs, when curses supports 256 colours, use them. --- win/curses/curswins.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/win/curses/curswins.c b/win/curses/curswins.c index a09b3f3f0..1044cd61b 100644 --- a/win/curses/curswins.c +++ b/win/curses/curswins.c @@ -766,25 +766,26 @@ coordinates without a refresh. Currently only used for the map. */ /* convert nhcolor (fg) and framecolor (bg) to curses colorpair */ static int -get_framecolor(int nhcolor, int framecolor) +get_framecolor(const nethack_char *nch) { + int bgcolor = (nch->framecolor != NO_COLOR) ? nch->framecolor : 0; /* curses_toggle_color_attr() adds the +1 and takes care of COLORS < 16 */ if (curses_has_256color()) { - return (256 * (framecolor % 8)) + (nhcolor % 256); + int color = nch->color256 ? nch->color256 : nch->color; + return (256 * (bgcolor % CURSES_NUM_BACKGROUND_COLORS)) + (color % 256); } else - return (16 * (framecolor % 8)) + (nhcolor % 16); - + return (16 * (bgcolor % CURSES_NUM_BACKGROUND_COLORS)) + (nch->color % 16); } static void write_char(WINDOW * win, int x, int y, nethack_char nch) { - int curscolor = nch.color, cursattr = nch.attr; + int curscolor; + int cursattr = nch.attr; - if (nch.framecolor != NO_COLOR) { - curscolor = get_framecolor(nch.color, nch.framecolor); - if (nch.attr == A_REVERSE) - cursattr = A_NORMAL; /* hilited pet looks odd otherwise */ + curscolor = get_framecolor(&nch); + if ((nch.framecolor != NO_COLOR) && (nch.attr == A_REVERSE)) { + cursattr = A_NORMAL; /* hilited pet looks odd otherwise */ } curses_toggle_color_attr(win, curscolor, cursattr, ON); #if defined(CURSES_UNICODE) && defined(ENHANCED_SYMBOLS)