From 13f46e585fe773a8c032cc558e5d6f34142bb1d2 Mon Sep 17 00:00:00 2001 From: SHIRAKATA Kentaro Date: Thu, 29 May 2025 18:45:17 +0900 Subject: [PATCH 001/442] remove redundant assignments on join() The values assigned there are overwritten by later code. --- src/mklev.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/mklev.c b/src/mklev.c index a455639c6..11c7a0c5d 100644 --- a/src/mklev.c +++ b/src/mklev.c @@ -448,8 +448,6 @@ join(int a, int b, boolean nxcor) if (troom->lx > croom->hx) { dx = 1; dy = 0; - xx = croom->hx + 1; - tx = troom->lx - 1; if (!finddpos(&cc, DIR_E, croom)) return; if (!finddpos(&tt, DIR_W, troom)) @@ -457,8 +455,6 @@ join(int a, int b, boolean nxcor) } else if (troom->hy < croom->ly) { dy = -1; dx = 0; - yy = croom->ly - 1; - ty = troom->hy + 1; if (!finddpos(&cc, DIR_N, croom)) return; if (!finddpos(&tt, DIR_S, troom)) @@ -466,8 +462,6 @@ join(int a, int b, boolean nxcor) } else if (troom->hx < croom->lx) { dx = -1; dy = 0; - xx = croom->lx - 1; - tx = troom->hx + 1; if (!finddpos(&cc, DIR_W, croom)) return; if (!finddpos(&tt, DIR_E, troom)) @@ -475,8 +469,6 @@ join(int a, int b, boolean nxcor) } else { dy = 1; dx = 0; - yy = croom->hy + 1; - ty = troom->ly - 1; if (!finddpos(&cc, DIR_S, croom)) return; if (!finddpos(&tt, DIR_N, troom)) From 7e44aad627aad3b9e921d4b0af59147860da872e Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 31 Aug 2025 10:30:23 -0400 Subject: [PATCH 002/442] address a static analyzer complaint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mthrowu.c: In function ‘hit_bars’: mthrowu.c:1419:25: warning: ‘maybe_unused’ attribute ignored [-Wattributes] 1419 | static enum sound_effect_entries se[] SOUNDLIBONLY = { mthrowu.c:1419:46: warning: unused variable ‘se’ [-Wunused-variable] 1419 | static enum sound_effect_entries se[] SOUNDLIBONLY = { --- src/mthrowu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mthrowu.c b/src/mthrowu.c index 259484698..a52a678f2 100644 --- a/src/mthrowu.c +++ b/src/mthrowu.c @@ -1416,7 +1416,7 @@ hit_bars( } } else { if (!Deaf) { - static enum sound_effect_entries se[] SOUNDLIBONLY = { + static enum sound_effect_entries se[] = { se_zero_invalid, se_bars_whang, se_bars_whap, se_bars_flapp, se_bars_clink, se_bars_clonk @@ -1436,6 +1436,7 @@ hit_bars( Soundeffect(se[bsindx], 100); pline("%s!", barsounds[bsindx]); + nhUse(se[bsindx]); } if (!(harmless_missile(otmp) || is_flimsy(otmp))) noise = 4 * 4; From 137c0282002bc269b4bc4fb132c01c5419069919 Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 1 Sep 2025 09:47:03 -0400 Subject: [PATCH 003/442] bug diagnostic comment --- src/uhitm.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/uhitm.c b/src/uhitm.c index cbf797e8e..4e3fe19b3 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -1790,6 +1790,18 @@ hmon_hitmon( if (hmd.jousting) { hmon_hitmon_jousting(&hmd, mon, obj); + /* + * FIXME: + * If jousting occurred above, it can lead to: + * mhurtle_to_doom() + * mhurtle() + * mintrap() + * trapeffect_hole() + * trapeffect_level_telep() + * migrate_to_level() + * which results in mon-mx being set to 0, and that + * can lead to an impossible() in clone_mon() trying + * to create a monster at <0,0> */ } else if (hmd.unarmed && hmd.dmg > 1 && !thrown && !obj && !Upolyd) { hmon_hitmon_stagger(&hmd, mon, obj); } else if (!hmd.unarmed && hmd.dmg > 1 && !thrown && !Upolyd From 475134cfa56f0ee26922c8b80d06ef562ef299e9 Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 1 Sep 2025 09:53:20 -0400 Subject: [PATCH 004/442] comment update --- src/uhitm.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/uhitm.c b/src/uhitm.c index 4e3fe19b3..ecd3ef1be 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -1799,9 +1799,11 @@ hmon_hitmon( * trapeffect_hole() * trapeffect_level_telep() * migrate_to_level() - * which results in mon-mx being set to 0, and that + * which results in mon->mx being set to 0, and that * can lead to an impossible() in clone_mon() trying - * to create a monster at <0,0> */ + * to create a monster at <0,0> when the monster is + * a black pudding and hmon_hitmon_splitmon() gets called + * below */ } else if (hmd.unarmed && hmd.dmg > 1 && !thrown && !obj && !Upolyd) { hmon_hitmon_stagger(&hmd, mon, obj); } else if (!hmd.unarmed && hmd.dmg > 1 && !thrown && !Upolyd From 6fade4b1842bb32bb44588740b2bca27c2024e1e Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 1 Sep 2025 10:34:38 -0400 Subject: [PATCH 005/442] don't split pudding that got jousted into a hole --- doc/fixes3-7-0.txt | 1 + include/you.h | 1 + src/uhitm.c | 32 +++++++++++++++----------------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index fb87d1121..11e09b77f 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -2128,6 +2128,7 @@ successfully disarming a chest trap was clearing the chest's 'tknown' bit when game ended with 'force_invmenu' On, final disclosure of inventory would contain spurious menu entry "? - (list likely candidates)" avoid reporting "a gold pieces appears next to you" for mimic +don't try to split a pudding that got jousted into a hole and is off the map Fixes to 3.7.0-x Platform and/or Interface Problems Exposed Via git Repository diff --git a/include/you.h b/include/you.h index bbd679d8f..efa1baa69 100644 --- a/include/you.h +++ b/include/you.h @@ -534,6 +534,7 @@ struct _hitmon_data { boolean needpoismsg; boolean poiskilled; boolean already_killed; + boolean offmap; boolean destroyed; boolean dryit; boolean doreturn; diff --git a/src/uhitm.c b/src/uhitm.c index ecd3ef1be..dc6b1183e 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -1558,7 +1558,7 @@ hmon_hitmon_pet( struct obj *obj UNUSED) { if (mon->mtame && hmd->dmg > 0) { - /* do this even if the pet is being killed (affects revival) */ + /* do this even if the pet is being killed or migrating (affects revival) */ abuse_dog(mon); /* reduces tameness */ /* flee if still alive and still tame; if already suffering from untimed fleeing, no effect, otherwise increases timed fleeing */ @@ -1576,7 +1576,7 @@ hmon_hitmon_splitmon( if ((hmd->mdat == &mons[PM_BLACK_PUDDING] || hmd->mdat == &mons[PM_BROWN_PUDDING]) /* pudding is alive and healthy enough to split */ - && mon->mhp > 1 && !mon->mcan + && mon->mhp > 1 && !mon->mcan && !hmd->offmap /* iron weapon using melee or polearm hit [3.6.1: metal weapon too; also allow either or both weapons to cause split when twoweap] */ && obj && (obj == uwep || (u.twoweap && obj == uswapwep)) @@ -1790,20 +1790,6 @@ hmon_hitmon( if (hmd.jousting) { hmon_hitmon_jousting(&hmd, mon, obj); - /* - * FIXME: - * If jousting occurred above, it can lead to: - * mhurtle_to_doom() - * mhurtle() - * mintrap() - * trapeffect_hole() - * trapeffect_level_telep() - * migrate_to_level() - * which results in mon->mx being set to 0, and that - * can lead to an impossible() in clone_mon() trying - * to create a monster at <0,0> when the monster is - * a black pudding and hmon_hitmon_splitmon() gets called - * below */ } else if (hmd.unarmed && hmd.dmg > 1 && !thrown && !obj && !Upolyd) { hmon_hitmon_stagger(&hmd, mon, obj); } else if (!hmd.unarmed && hmd.dmg > 1 && !thrown && !Upolyd @@ -1828,6 +1814,18 @@ hmon_hitmon( a level draining artifact has already done to max HP */ if (mon->mhp > mon->mhpmax) mon->mhp = mon->mhpmax; + if (mon->mx == 0) { + /* + * jousting can lead to: + * mhurtle_to_doom() + * mhurtle() + * mintrap() + * trapeffect_hole() + * trapeffect_level_telep() + * migrate_to_level() + * Set offmap in that situation so code to follow can test for it.*/ + hmd.offmap = TRUE; + } if (DEADMONSTER(mon)) hmd.destroyed = TRUE; @@ -1888,7 +1886,7 @@ hmon_hitmon( Your("%s %s no longer poisoned.", hmd.saved_oname, vtense(hmd.saved_oname, "are")); - if (!hmd.destroyed) { + if (!hmd.destroyed && !hmd.offmap) { int hitflags = M_ATTK_HIT; wakeup(mon, TRUE); From e880b08be384188e2bff01601648cac3b43a158f Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 1 Sep 2025 10:49:17 -0400 Subject: [PATCH 006/442] offmap initial setting --- src/uhitm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/uhitm.c b/src/uhitm.c index dc6b1183e..bbea51d6a 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -1752,6 +1752,7 @@ hmon_hitmon( hmd.needpoismsg = FALSE; hmd.poiskilled = FALSE; hmd.already_killed = FALSE; + hmd.offmap = FALSE; hmd.destroyed = FALSE; hmd.dryit = FALSE; hmd.doreturn = FALSE; From e4f0d1a3e23854dab9d0564af9d5868dcbf8feca Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 1 Sep 2025 13:35:05 -0700 Subject: [PATCH 007/442] fix #4341 - jousting while trapped --- doc/fixes3-7-0.txt | 3 +++ src/uhitm.c | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 11e09b77f..70718735b 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -2129,6 +2129,9 @@ when game ended with 'force_invmenu' On, final disclosure of inventory would contain spurious menu entry "? - (list likely candidates)" avoid reporting "a gold pieces appears next to you" for mimic don't try to split a pudding that got jousted into a hole and is off the map +mounted hero was able to deliver joust hits when trapped +timer sanity check for melting ice gave false complaint about non-ice for + frozen moat under open drawbridge Fixes to 3.7.0-x Platform and/or Interface Problems Exposed Via git Repository diff --git a/src/uhitm.c b/src/uhitm.c index bbea51d6a..5f6e87d99 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -2072,6 +2072,11 @@ joust(struct monst *mon, /* target */ /* sanity check; lance must be wielded in order to joust */ if (obj != uwep && (obj != uswapwep || !u.twoweap)) return 0; + /* can't joust while trapped--not enough room to maneuver; + * TODO? if the steed is trapped in a pit, perhaps the hero ought to be + * able to joust against a monster that's in a conjoined pit */ + if (u.utrap) + return 0; /* if using two weapons, use worse of lance and two-weapon skills */ skill_rating = P_SKILL(weapon_type(obj)); /* lance skill */ From fe357a6f0434b800af9926207564eff367f70b7b Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 4 Sep 2025 22:13:07 -0400 Subject: [PATCH 008/442] avoid telling hero they missed a monster they aren't aware of Resolves #1441 --- include/extern.h | 3 ++- src/mthrowu.c | 2 +- src/uhitm.c | 8 ++++++++ src/zap.c | 11 ++++++----- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/include/extern.h b/include/extern.h index c8180ec44..3fffc7ab8 100644 --- a/include/extern.h +++ b/include/extern.h @@ -3409,6 +3409,7 @@ extern int passive(struct monst *, struct obj *, boolean, boolean, uchar, extern void passive_obj(struct monst *, struct obj *, struct attack *) NONNULLARG1; extern void that_is_a_mimic(struct monst *, unsigned) NONNULLARG1; extern void stumble_onto_mimic(struct monst *) NONNULLARG1; +extern boolean mimic_disguised_as_non_mon(struct monst *) NONNULLARG1; extern int flash_hits_mon(struct monst *, struct obj *) NONNULLARG12; extern void light_hits_gremlin(struct monst *, int) NONNULLARG1; @@ -3938,7 +3939,7 @@ extern int zhitm(struct monst *, int, int, struct obj **) NONNULLPTRS; extern int burn_floor_objects(coordxy, coordxy, boolean, boolean); extern void ubuzz(int, int); extern void buzz(int, int, coordxy, coordxy, int, int); -extern void dobuzz(int, int, coordxy, coordxy, int, int, boolean); +extern void dobuzz(int, int, coordxy, coordxy, int, int, boolean, boolean); extern void melt_ice(coordxy, coordxy, const char *) NO_NNARGS; extern void start_melt_ice_timeout(coordxy, coordxy, long); extern void melt_ice_away(union any *, long) NONNULLARG1; diff --git a/src/mthrowu.c b/src/mthrowu.c index a52a678f2..7b2a23ca7 100644 --- a/src/mthrowu.c +++ b/src/mthrowu.c @@ -1093,7 +1093,7 @@ breamm(struct monst *mtmp, struct attack *mattk, struct monst *mtarg) Monnam(mtmp), breathwep_name(typ)); gb.buzzer = mtmp; dobuzz(BZ_M_BREATH(BZ_OFS_AD(typ)), (int) mattk->damn, - mtmp->mx, mtmp->my, sgn(gt.tbx), sgn(gt.tby), utarget); + mtmp->mx, mtmp->my, sgn(gt.tbx), sgn(gt.tby), utarget, utarget); gb.buzzer = 0; nomul(0); /* breath runs out sometimes. Also, give monster some diff --git a/src/uhitm.c b/src/uhitm.c index 5f6e87d99..3e1b3a7d4 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -6259,6 +6259,14 @@ stumble_onto_mimic(struct monst *mtmp) map_invisible(mtmp->mx, mtmp->my); } +boolean +mimic_disguised_as_non_mon(struct monst *mtmp) +{ + return (!sensemon(mtmp) + && M_AP_TYPE(mtmp) + && M_AP_TYPE(mtmp) != M_AP_MONSTER); +} + staticfn void nohandglow(struct monst *mon) { diff --git a/src/zap.c b/src/zap.c index a1a5c3380..73666008f 100644 --- a/src/zap.c +++ b/src/zap.c @@ -4692,13 +4692,13 @@ disintegrate_mon( void ubuzz(int type, int nd) { - dobuzz(type, nd, u.ux, u.uy, u.dx, u.dy, TRUE); + dobuzz(type, nd, u.ux, u.uy, u.dx, u.dy, TRUE, FALSE); } void buzz(int type, int nd, coordxy sx, coordxy sy, int dx, int dy) { - dobuzz(type, nd, sx, sy, dx, dy, TRUE); + dobuzz(type, nd, sx, sy, dx, dy, TRUE, FALSE); } /* @@ -4716,7 +4716,7 @@ dobuzz( int nd, /* damage strength ('number of dice') */ coordxy sx, coordxy sy, /* starting point */ int dx, int dy, /* direction delta */ - boolean say) /* announce out of sight hit/miss events if true */ + boolean sayhit, boolean saymiss) /* announce out of sight hit/miss events if true */ { int range, fltyp = zaptype(type), damgtype = fltyp % 10; coordxy lsx, lsy; @@ -4865,7 +4865,7 @@ dobuzz( } else { if (!otmp) { /* normal non-fatal hit */ - if (say || canseemon(mon)) + if (sayhit || canseemon(mon)) hit(flash_str(fltyp, FALSE), mon, exclam(tmp)); } else { /* some armor was destroyed; no damage done */ @@ -4883,7 +4883,8 @@ dobuzz( } range -= 2; } else { - if (say || canseemon(mon)) + if (saymiss + || (canseemon(mon) && !mimic_disguised_as_non_mon(mon))) miss(flash_str(fltyp, FALSE), mon); } } else if (u_at(sx, sy) && range >= 0) { From 88eb33f198c26651671d41929878664df66298a9 Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 4 Sep 2025 22:38:49 -0400 Subject: [PATCH 009/442] more #1441 Address the second part of the GitHub issue: "Finally, when a wand of striking (or a shift+F melee attack for that matter, but the latter gives no tactical advantage compared to just bumping them) misses a disguised mimic, it is revealed. I don't know what to make of it" --- src/zap.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/zap.c b/src/zap.c index 73666008f..f7001d2ac 100644 --- a/src/zap.c +++ b/src/zap.c @@ -190,14 +190,16 @@ bhitm(struct monst *mtmp, struct obj *otmp) /*FALLTHRU*/ case SPE_FORCE_BOLT: reveal_invis = TRUE; - if (disguised_mimic) - seemimic(mtmp); learn_it = cansee(gb.bhitpos.x, gb.bhitpos.y); if (resists_magm(mtmp)) { /* match effect on player */ + if (mimic_disguised_as_non_mon(mtmp)) + seemimic(mtmp); shieldeff(mtmp->mx, mtmp->my); pline("Boing!"); /* 3.7: used to 'break' to avoid setting learn_it here */ } else if (u.uswallow || rnd(20) < 10 + find_mac(mtmp)) { + if (disguised_mimic) + seemimic(mtmp); dmg = d(2, 12); if (dbldam) dmg *= 2; @@ -206,7 +208,8 @@ bhitm(struct monst *mtmp, struct obj *otmp) hit(zap_type_text, mtmp, exclam(dmg)); (void) resist(mtmp, otmp->oclass, dmg, TELL); } else { - miss(zap_type_text, mtmp); + if (!disguised_mimic) + miss(zap_type_text, mtmp); learn_it = FALSE; } break; From 774129df114cf0a77379b1268083b8bdb42fd14e Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 4 Sep 2025 22:52:36 -0400 Subject: [PATCH 010/442] another #1441 follow-up bit --- include/extern.h | 1 + src/uhitm.c | 7 +++++++ src/zap.c | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/include/extern.h b/include/extern.h index 3fffc7ab8..739880db5 100644 --- a/include/extern.h +++ b/include/extern.h @@ -3410,6 +3410,7 @@ extern void passive_obj(struct monst *, struct obj *, struct attack *) NONNULLAR extern void that_is_a_mimic(struct monst *, unsigned) NONNULLARG1; extern void stumble_onto_mimic(struct monst *) NONNULLARG1; extern boolean mimic_disguised_as_non_mon(struct monst *) NONNULLARG1; +extern boolean mimic_disguised_as_mon(struct monst *) NONNULLARG1; extern int flash_hits_mon(struct monst *, struct obj *) NONNULLARG12; extern void light_hits_gremlin(struct monst *, int) NONNULLARG1; diff --git a/src/uhitm.c b/src/uhitm.c index 3e1b3a7d4..090275cb3 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -6267,6 +6267,13 @@ mimic_disguised_as_non_mon(struct monst *mtmp) && M_AP_TYPE(mtmp) != M_AP_MONSTER); } +boolean +mimic_disguised_as_mon(struct monst *mtmp) +{ + return (M_AP_TYPE(mtmp) + && M_AP_TYPE(mtmp) == M_AP_MONSTER); +} + staticfn void nohandglow(struct monst *mon) { diff --git a/src/zap.c b/src/zap.c index f7001d2ac..7cceffba3 100644 --- a/src/zap.c +++ b/src/zap.c @@ -192,7 +192,7 @@ bhitm(struct monst *mtmp, struct obj *otmp) reveal_invis = TRUE; learn_it = cansee(gb.bhitpos.x, gb.bhitpos.y); if (resists_magm(mtmp)) { /* match effect on player */ - if (mimic_disguised_as_non_mon(mtmp)) + if (!mimic_disguised_as_mon(mtmp)) seemimic(mtmp); shieldeff(mtmp->mx, mtmp->my); pline("Boing!"); From d3db5ce4f13a47cebcb24d7605b40e2706374e45 Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 4 Sep 2025 23:01:29 -0400 Subject: [PATCH 011/442] yet_another: !mimic_disguised_as_mon(mtmp) would be true for a non-mimic (mimic_disguised_as_non_mon(mtmp) would also have worked okay) --- src/zap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zap.c b/src/zap.c index 7cceffba3..c43598386 100644 --- a/src/zap.c +++ b/src/zap.c @@ -192,7 +192,7 @@ bhitm(struct monst *mtmp, struct obj *otmp) reveal_invis = TRUE; learn_it = cansee(gb.bhitpos.x, gb.bhitpos.y); if (resists_magm(mtmp)) { /* match effect on player */ - if (!mimic_disguised_as_mon(mtmp)) + if (disguised_mimic && !mimic_disguised_as_mon(mtmp)) seemimic(mtmp); shieldeff(mtmp->mx, mtmp->my); pline("Boing!"); From 602678aa5a768c9c7c8311763a25e110f42c632f Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 5 Sep 2025 09:26:00 -0400 Subject: [PATCH 012/442] follow-up: function name --- include/extern.h | 4 ++-- src/uhitm.c | 4 ++-- src/zap.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/extern.h b/include/extern.h index 739880db5..5f50b67ea 100644 --- a/include/extern.h +++ b/include/extern.h @@ -3409,8 +3409,8 @@ extern int passive(struct monst *, struct obj *, boolean, boolean, uchar, extern void passive_obj(struct monst *, struct obj *, struct attack *) NONNULLARG1; extern void that_is_a_mimic(struct monst *, unsigned) NONNULLARG1; extern void stumble_onto_mimic(struct monst *) NONNULLARG1; -extern boolean mimic_disguised_as_non_mon(struct monst *) NONNULLARG1; -extern boolean mimic_disguised_as_mon(struct monst *) NONNULLARG1; +extern boolean disguised_as_non_mon(struct monst *) NONNULLARG1; +extern boolean disguised_as_mon(struct monst *) NONNULLARG1; extern int flash_hits_mon(struct monst *, struct obj *) NONNULLARG12; extern void light_hits_gremlin(struct monst *, int) NONNULLARG1; diff --git a/src/uhitm.c b/src/uhitm.c index 090275cb3..cca56b435 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -6260,7 +6260,7 @@ stumble_onto_mimic(struct monst *mtmp) } boolean -mimic_disguised_as_non_mon(struct monst *mtmp) +disguised_as_non_mon(struct monst *mtmp) { return (!sensemon(mtmp) && M_AP_TYPE(mtmp) @@ -6268,7 +6268,7 @@ mimic_disguised_as_non_mon(struct monst *mtmp) } boolean -mimic_disguised_as_mon(struct monst *mtmp) +disguised_as_mon(struct monst *mtmp) { return (M_AP_TYPE(mtmp) && M_AP_TYPE(mtmp) == M_AP_MONSTER); diff --git a/src/zap.c b/src/zap.c index c43598386..930b4c150 100644 --- a/src/zap.c +++ b/src/zap.c @@ -192,7 +192,7 @@ bhitm(struct monst *mtmp, struct obj *otmp) reveal_invis = TRUE; learn_it = cansee(gb.bhitpos.x, gb.bhitpos.y); if (resists_magm(mtmp)) { /* match effect on player */ - if (disguised_mimic && !mimic_disguised_as_mon(mtmp)) + if (disguised_mimic && !disguised_as_mon(mtmp)) seemimic(mtmp); shieldeff(mtmp->mx, mtmp->my); pline("Boing!"); @@ -4887,7 +4887,7 @@ dobuzz( range -= 2; } else { if (saymiss - || (canseemon(mon) && !mimic_disguised_as_non_mon(mon))) + || (canseemon(mon) && !disguised_as_non_mon(mon))) miss(flash_str(fltyp, FALSE), mon); } } else if (u_at(sx, sy) && range >= 0) { From e8e1868b700bd73e70eadf78658b90ad5159e0c0 Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 5 Sep 2025 12:37:40 -0400 Subject: [PATCH 013/442] part3 of 3 for GitHub issue #1441 > Perhaps related: when a wand of sleep hits a disguised already-sleeping > mimic (about which it is a separate question if they should go into > disguise when sleeping, is it supposed to be automagic or conscious > effort for them? but I digress), the mimic is not revealed (should it?) > but the message says "hits a mimic". Adjust restrap() so that a revealed mimic won't disguise itself while sleeping. This seems to be in keeping with mimic lore. Also, normal shop sounds (chime of register etc.) will wake a mimic up from indeterminate sleep. Closes #1441 --- include/extern.h | 2 ++ src/mon.c | 7 +++++++ src/shk.c | 10 ++++++++++ src/sounds.c | 1 + 4 files changed, 20 insertions(+) diff --git a/include/extern.h b/include/extern.h index 5f50b67ea..670fb77a8 100644 --- a/include/extern.h +++ b/include/extern.h @@ -2920,6 +2920,8 @@ extern void globby_bill_fixup(struct obj *, struct obj *) NONNULLARG12; 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 *); + /* ### shknam.c ### */ diff --git a/src/mon.c b/src/mon.c index dce75521c..1536f460b 100644 --- a/src/mon.c +++ b/src/mon.c @@ -4647,6 +4647,13 @@ restrap(struct monst *mtmp) return FALSE; if (mtmp->data->mlet == S_MIMIC) { + if (mtmp->msleeping || mtmp->mfrozen) { + /* + * The mimic needs to be awake to disguise itself + * as something else. + */ + return FALSE; + } set_mimic_sym(mtmp); return TRUE; } else if (levl[mtmp->mx][mtmp->my].typ == ROOM) { diff --git a/src/shk.c b/src/shk.c index 3463effbd..d493caefb 100644 --- a/src/shk.c +++ b/src/shk.c @@ -1063,6 +1063,16 @@ tended_shop(struct mkroom *sroom) return !mtmp ? FALSE : (boolean) inhishop(mtmp); } +void +noisy_shop(struct mkroom *sroom) +{ + struct monst *mtmp = sroom->resident; + + if (mtmp && inhishop(mtmp)) { + wake_nearto(mtmp->mx, mtmp->my, 11 * 11); + } +} + staticfn struct bill_x * onbill(struct obj *obj, struct monst *shkp, boolean silent) { diff --git a/src/sounds.c b/src/sounds.c index 599469de8..5e4a0e1cd 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -323,6 +323,7 @@ dosounds(void) "the chime of a cash register.", "Neiman and Marcus arguing!", }; You_hear1(shop_msg[rn2(2) + hallu]); + noisy_shop(sroom); } return; } From d5d40b322c206ca2945b4c670f1dd5e2a089bc35 Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 5 Sep 2025 16:38:56 -0400 Subject: [PATCH 014/442] fix broken Windows GUI ascii mode text color Issue identified in https://github.com/NetHack/NetHack/issues/1359 Resolves #1359 --- win/win32/mhmap.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/win/win32/mhmap.c b/win/win32/mhmap.c index d5e6b116e..46a7ee1d0 100644 --- a/win/win32/mhmap.c +++ b/win/win32/mhmap.c @@ -982,13 +982,12 @@ paintGlyph(PNHMapWindow data, int i, int j, RECT * rect) ch = glyphinfo->gm.u->utf32ch; } #endif - if ((glyphinfo->gm.customcolor & NH_BASIC_COLOR) == 0) { - rgbcolor = RGB((glyphinfo->gm.customcolor >> 16) & 0xFF, - (glyphinfo->gm.customcolor >> 8) & 0xFF, - (glyphinfo->gm.customcolor >> 0) & 0xFF); - } else { - color = (int) COLORVAL(glyphinfo->gm.customcolor); - rgbcolor = nhcolor_to_RGB(color); + if (glyphinfo->gm.customcolor != 0 + && (mswin_procs.wincap2 & WC2_EXTRACOLORS) != 0) { + if ((glyphinfo->gm.customcolor & NH_BASIC_COLOR) != 0) { + color = (int) COLORVAL(glyphinfo->gm.customcolor); + rgbcolor = nhcolor_to_RGB(color); + } } if (((data->map[i][j].gm.glyphflags & MG_PET) && iflags.hilite_pet) || ((data->map[i][j].gm.glyphflags & (MG_DETECT | MG_BW_LAVA From 349fbe93b162b094648a85615695d840cb7d119b Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 7 Sep 2025 12:50:54 -0400 Subject: [PATCH 015/442] less verbose compiler command lines during build If using hints file sys/unix/hints/linux.370 or sys/unix/hints/macOS.370 allow the majority of the boilerplate compile switches to reside in a compiler response file, instead of on the command line. Include one of the following on your make command line: make response=1 or make resp=1 It can be combined with other make command line options. See sys/unix/README-hints for further information about those. The response files that it uses are: CC (clang or gcc) src/nethack_cc.rsp CXx (clang++ or g++) src/nethack_cxx.rsp Note: I think the reduced clutter should actually become the default, and the override should be noresponse=1 to NOT use it, but I'm not sure how others feel, so for now, it requires make resp=1 Feedback on whether that should become the default or not is welcome. Tested on Linux with gcc-15 and on Linux with clang-20. I haven't had a chance to test it on macOS yet. --- src/.gitignore | 1 + sys/unix/Makefile.src | 9 ++++++-- sys/unix/README-hints | 5 ++++- sys/unix/hints/include/response.370 | 33 +++++++++++++++++++++++++++++ sys/unix/hints/linux.370 | 4 ++++ sys/unix/hints/macOS.370 | 3 +++ 6 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 sys/unix/hints/include/response.370 diff --git a/src/.gitignore b/src/.gitignore index 13097d000..30c020d13 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -16,6 +16,7 @@ nethack tiles.bmp *.moc *.lnk +*.rsp graphicschk nhdat o diff --git a/sys/unix/Makefile.src b/sys/unix/Makefile.src index 57bd4b1db..e5763785f 100644 --- a/sys/unix/Makefile.src +++ b/sys/unix/Makefile.src @@ -198,7 +198,7 @@ ARFLAGS = rcs # The Qt and Be window systems are written in C++, while the rest of # NetHack is standard C. If using Qt, uncomment the LINK line here to get # the C++ libraries linked in. -CXXFLAGS = $(CCXXFLAGS) -I. -I$(QTDIR)/include $(QTCXXFLAGS) +CXXFLAGS ?= $(CCXXFLAGS) -I. -I$(QTDIR)/include $(QTCXXFLAGS) CXX ?= g++ MOC ?= moc MOCPATH ?= $(QTDIR)/bin/$(MOC) @@ -642,7 +642,11 @@ DATE_O = $(TARGETPFX)date.o all: $(GAME) @echo "" -pregame: +create_responsefiles: + echo $(CC_COMPILER_SWITCHES) >$(CC_RESPONSEFILE) + echo $(CXX_COMPILER_SWITCHES) >$(CXX_RESPONSEFILE) + +pregame: $(RESPONSEFILES) $(PREGAME) $(GAME): pregame $(MAKEDEFS) $(LUALIB) $(WAVS) $(SYSTEM) @@ -808,6 +812,7 @@ clean: -rm -f *.o $(HACK_H) $(CONFIG_H) -rm -f monstr.c vis_tab.c ../include/vis_tab.h #obsolete generated files true; $(CLEANMORE) + true; $(CLEAN_CC_RESPONSEFILE) $(CLEAN_CXX_RESPONSEFILE) spotless: clean -rm -f a.out core $(HACKLIB) $(GAMEBIN) Sys* diff --git a/sys/unix/README-hints b/sys/unix/README-hints index 9c5f06934..03c3fd893 100644 --- a/sys/unix/README-hints +++ b/sys/unix/README-hints @@ -49,7 +49,10 @@ make musl=1 Build with settings appropriate for linking with musl libc, instead of glibc. This causes NOCRASHREPORT to be defined, and avoids the use of 'col' during the build. - +make resp=1 Place the majority of the compiler switches into + a response file to de-clutter the build compiler + command lines and eliminate some of the + unsightly wrapping that occurs. make CROSS_TO_MSDOS=1 package Cross-compile for an MSDOS target package. make CROSS_TO_WASM=1 Cross-compile for a WASM target. make CROSS_TO_MIPS=1 Cross-compile for a mips target. diff --git a/sys/unix/hints/include/response.370 b/sys/unix/hints/include/response.370 new file mode 100644 index 000000000..4e893ea3e --- /dev/null +++ b/sys/unix/hints/include/response.370 @@ -0,0 +1,33 @@ +#------------------------------------------------------------------------------ +# NetHack 3.7 response.370 $NHDT-Date: 1668359835 2022/11/13 17:17:15 $ $NHDT-Branch: NetHack-3.7 $ + +ifeq "$(RESP)" "1" +USE_RESPONSEFILE=1 +endif +ifeq "$(resp)" "1" +USE_RESPONSEFILE=1 +endif +ifeq "$(RESPONSE)" "1" +USE_RESPONSEFILE=1 +endif +ifeq "$(response)" "1" +USE_RESPONSEFILE=1 +endif + +ifeq "$(USE_RESPONSEFILE)" "1" +RESPONSEFILES=create_responsefiles +CXXFLAGS = $(CCXXFLAGS) -I. -I$(QTDIR)/include $(QTCXXFLAGS) +CC_COMPILER_SWITCHES := $(subst \,\\,$(CFLAGS)) +CC_COMPILER_SWITCHES := $(subst ",\",$(CC_COMPILER_SWITCHES)) +CXX_COMPILER_SWITCHES := $(subst \,\\,$(CXXFLAGS)) +CXX_COMPILER_SWITCHES := $(subst ",\",$(CXX_COMPILER_SWITCHES)) +CC_RESPONSEFILE=../src/nethack_cc.rsp +CXX_RESPONSEFILE=../src/nethack_cxx.rsp +CFLAGS=@$(CC_RESPONSEFILE) +CXXFLAGS=@$(CXX_RESPONSEFILE) +CLEAN_CC_RESPONSEFILE=rm -f $(CC_RESPONSEFILE); +CLEAN_CXX_RESPONSEFILE=rm -f $(CXX_RESPONSEFILE); +endif + +#end of response.370 +#------------------------------------------------------------------------------ diff --git a/sys/unix/hints/linux.370 b/sys/unix/hints/linux.370 index 3ce7d47d4..4f49e9921 100755 --- a/sys/unix/hints/linux.370 +++ b/sys/unix/hints/linux.370 @@ -448,6 +448,10 @@ MANDIR=/usr/share/man/man6 #-INCLUDE multisnd2-pre.370 # +# +#-INCLUDE response.370 +# + #-POST # diff --git a/sys/unix/hints/macOS.370 b/sys/unix/hints/macOS.370 index ad522bb2f..13cb3afe1 100755 --- a/sys/unix/hints/macOS.370 +++ b/sys/unix/hints/macOS.370 @@ -461,6 +461,9 @@ SYSCONFENSURE = (if ! test -f $(INSTDIR)/sysconf ; then \ # #-INCLUDE multisnd2-pre.370 # +# +#-INCLUDE response.370 +# ifdef MAKEFILE_TOP .PHONY: bundle From e219a6046506fd2741ac195489d0b7c4eb2094ee Mon Sep 17 00:00:00 2001 From: nhw_cron Date: Sun, 7 Sep 2025 13:24:06 -0400 Subject: [PATCH 016/442] This is cron-daily v1-Apr-1-2024. 000files updated: Files --- Files | 1 + 1 file changed, 1 insertion(+) diff --git a/Files b/Files index e4f4e096b..1ed552cf1 100644 --- a/Files +++ b/Files @@ -418,6 +418,7 @@ sys/unix/hints/include: compiler.370 cross-post.370 cross-pre1.370 cross-pre2.370 gbdates-post.370 gbdates-pre.370 misc.370 multisnd-post.370 multisnd1-pre.370 multisnd2-pre.370 multiw-1.370 multiw-2.370 +response.370 sys/vms: (files for VMS version) From 9b7b2421ac75c8fa268b6a19693a1f429c4ebd2b Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 17 Sep 2025 12:39:32 -0400 Subject: [PATCH 017/442] update tested versions of Visual Studio 2025-09-17 --- sys/windows/Makefile.nmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index 2d92eb582..772b00e33 100644 --- a/sys/windows/Makefile.nmake +++ b/sys/windows/Makefile.nmake @@ -9,7 +9,7 @@ # # Visual Studio Compilers Tested: # - Microsoft Visual Studio 2019 Community Edition v 16.11.42 -# - Microsoft Visual Studio 2022 Community Edition v 17.14.13 +# - Microsoft Visual Studio 2022 Community Edition v 17.14.15 # #============================================================================== # This is used for building two distinct executables of NetHack: @@ -1179,7 +1179,7 @@ rc=Rc.exe # # Recently tested versions: TESTEDVS2019 = 14.29.30157.0 -TESTEDVS2022 = 14.44.35215.0 +TESTEDVS2022 = 14.44.35217.0 VS2019CUR = $(TESTEDVS2019:.=) VS2022CUR = $(TESTEDVS2022:.=) From d0b79912ed6c0441ee96eaedca2fc064661c1633 Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 22 Sep 2025 14:47:54 -0700 Subject: [PATCH 018/442] support 'nethack --nethackrc=~/File' Substitute $HOME/File if command line specifies --nethackrc=~/File to avoid "Access to ~/File denied (2)". Only implemented for opening run-time config file on Unix. Works for NETHACKOPTIONS=@~/File too; the normally optional at-sign is required since the tilde won't match a slash to distinguish file versus options. Only supports "~/" file path prefix, not "~user/". --- doc/fixes3-7-0.txt | 2 ++ src/cfgfiles.c | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 70718735b..98d309738 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -2479,6 +2479,8 @@ Unix: add ../include/nhlua.h to the alloc.o dependencies in Makefile.utl to Unix: implement SELF_RECOVER compile-time option, on by default on linux Unix: allow build to succeed with musl (instead of glibc), by specifying musl=1 on the make command line +Unix: support tilde expansion for home directory path, "~/relative-path", in + command line --nethackrc=path and environment NETHACKOPTIONS=@path user_sounds: move the message hook from inside individual window display ports to the core where it allows MSGTYP_NOSHOW msgtyp's to still trigger sounds to correct a reported github issue; also fixes a past reported diff --git a/src/cfgfiles.c b/src/cfgfiles.c index 3a3c0ff06..ca05d35cd 100644 --- a/src/cfgfiles.c +++ b/src/cfgfiles.c @@ -242,6 +242,13 @@ fopen_config_file(const char *filename, int src) if (filename && *filename) { set_configfile_name(filename); #ifdef UNIX + if (!strncmp(configfile, "~/", 2) && (envp = nh_getenv("HOME")) != 0) { + /* support for command line '--nethackrc=~/path' (or for + NETHACKOPTIONS='@~/path'; we don't support ~user/path) */ + Snprintf(tmp_config, sizeof tmp_config, "%s/%s", + envp, configfile + 2); /* insert $HOME/ and remove ~/ */ + set_configfile_name(tmp_config); + } if (access(configfile, 4) == -1) { /* 4 is R_OK on newer systems */ /* nasty sneaky attempt to read file through * NetHack's setuid permissions -- this is the only From edd11009e96e92948d79b75278b21cdee73a929f Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 25 Sep 2025 09:59:15 -0700 Subject: [PATCH 019/442] issue #1447 - physical damage when polymorphed Isssue reported by Tomsod: hero-as-target section of mhitm_ad_phys() was not handling hero's Half_physical_damage attribute. The issue was about cloning a pet from hero who is poly'd into a pudding but it was more general than that. Half_physical_damage was ignored for any hit by a monster-wielded weapon against poly'd hero. It took a while to convince myself that Half_physical_damage wasn't aleady being applied elsewhere but it doesn't seem to be. Fixes #1447 --- doc/fixes3-7-0.txt | 1 + src/mhitu.c | 3 ++- src/uhitm.c | 12 ++++++++---- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 98d309738..15447eac8 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -2132,6 +2132,7 @@ don't try to split a pudding that got jousted into a hole and is off the map mounted hero was able to deliver joust hits when trapped timer sanity check for melting ice gave false complaint about non-ice for frozen moat under open drawbridge +mhitm_ad_phys() was not applying Half_physical_damage when hero was target Fixes to 3.7.0-x Platform and/or Interface Problems Exposed Via git Repository diff --git a/src/mhitu.c b/src/mhitu.c index d6332c18d..480d616fe 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -1199,7 +1199,8 @@ hitmu(struct monst *mtmp, struct attack *mattk) mhm.damage = 1; } - if (mhm.damage) { + if (mhm.damage > 0) { + /* [Half_physical_damage isn't applied to mhm.permdmg] */ if (Half_physical_damage /* Mitre of Holiness, even if not currently blessed */ || (Role_if(PM_CLERIC) && uarmh && is_quest_artifact(uarmh) diff --git a/src/uhitm.c b/src/uhitm.c index cca56b435..26f3cfeac 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -4008,7 +4008,8 @@ mhitm_ad_phys( if (mattk->aatyp == AT_WEAP && otmp) { struct obj *marmg; int tmp; - boolean was_poisoned = (otmp->opoisoned || permapoisoned(otmp)); + boolean was_poisoned = (otmp->opoisoned + || permapoisoned(otmp)); if (otmp->otyp == CORPSE && touch_petrifies(&mons[otmp->corpsenm])) { @@ -4051,6 +4052,9 @@ mhitm_ad_phys( tmp -= rnd(-u.uac); if (tmp < 1) tmp = 1; + if (Half_physical_damage) + tmp = (tmp + 1) / 2; + if (u.mh - tmp > 1 && (objects[otmp->otyp].oc_material == IRON /* relevant 'metal' objects are scalpel and tsurugi */ @@ -4071,13 +4075,13 @@ mhitm_ad_phys( char buf[BUFSZ]; /* similar to mhitm_really_poison, but we don't use the - * exact same values, nor do we want the same 1/8 chance of - * the poison taking (use 1/4, same as in the mhitm case). */ + * exact same values, nor do we want same 1/8 chance of + * poison taking (use 1/4, same as in the mhitm case). */ Sprintf(buf, "%s %s", s_suffix(Monnam(magr)), mpoisons_subj(magr, mattk)); /* arbitrary, but most poison sources in the game are * strength-based. With hpdamchance = 10, HP damage occurs - * 1/2 of the time and it will hit Str the rest of the time. + * 1/2 of the time and it will hit Str rest of the time. * (This is the same as poisoned ammo.) */ poisoned(buf, A_STR, pmname(magr->data, Mgender(magr)), 10, FALSE); From b857899afc4f50ac3a450e3427e6aff02a0d5747 Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 25 Sep 2025 11:39:08 -0700 Subject: [PATCH 020/442] issue #1449 - fake explorer corpses on land mines Issue reported by AndrioCelos: the corpse of an early-level fake explorer supposedly killed by a trap would leave land mines intact. Change land mines with a fake explorer's corpse into discovered pits. Fixes #1449 --- src/mklev.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/mklev.c b/src/mklev.c index 8c75cc1df..7a3de31ad 100644 --- a/src/mklev.c +++ b/src/mklev.c @@ -1812,8 +1812,7 @@ staticfn void mktrap_victim(struct trap *ttmp) { /* Object generated by the trap; initially NULL, stays NULL if - we fail to generate an object or if the trap doesn't - generate objects. */ + the trap doesn't generate objects. */ struct obj *otmp = NULL; int victim_mnum; /* race of the victim */ unsigned lvl = level_difficulty(); @@ -1867,14 +1866,18 @@ mktrap_victim(struct trap *ttmp) break; } - otmp = mkobj(poss_class, FALSE); /* these items are always cursed, both for flavour (owned by a dead adventurer, bones-pile-style) and for balance (less useful to use, and encourage pets to avoid the trap) */ - if (otmp) { - otmp->blessed = 0; - otmp->cursed = 1; - otmp->owt = weight(otmp); + otmp = mkobj(poss_class, FALSE); + curse(otmp); + /* for mktrap_victim(), PIT is actually an exploded LANDMINE */ + if (ttmp->ttyp == PIT && breaktest(otmp)) { + /* landmine: if fragile object has been created, destroy it; + don't worry about non-empty containers--they aren't + breakable--nor about breakable contents of such */ + dealloc_obj(otmp); + } else { place_object(otmp, x, y); } @@ -2054,7 +2057,7 @@ mktrap( m.x = m.y = 0; /* no traps in pools */ - if (tm && is_pool(tm->x, tm->y)) + if (tm && is_pool_or_lava(tm->x, tm->y)) return; if (num > NO_TRAP && num < TRAPNUM) { @@ -2126,7 +2129,8 @@ mktrap( immediately lethal). Finally, pits are excluded because it's weird to see an item in a pit and yet not be able to identify that the pit is there. */ - if (kind != NO_TRAP && !(mktrapflags & MKTRAP_NOVICTIM) + if (gi.in_mklev + && kind != NO_TRAP && !(mktrapflags & MKTRAP_NOVICTIM) && lvl <= (unsigned) rnd(4) && kind != SQKY_BOARD && kind != RUST_TRAP /* rolling boulder trap might not have a boulder if there was no @@ -2135,8 +2139,15 @@ mktrap( && !(kind == ROLLING_BOULDER_TRAP && t->launch.x == t->tx && t->launch.y == t->ty) && !is_pit(kind) && (kind < HOLE || kind == MAGIC_TRAP)) { + if (kind == LANDMINE) { + /* if victim was killed by a land mine, we won't scatter objects; + treat it as exploded, converting it into an unconcealed pit */ + t->ttyp = PIT; + t->tseen = 1; + } mktrap_victim(t); } + return; } /* Create stairs up or down at x,y. From 32873218cd2fc079aeee3b5c9e1cf45c95bb2a12 Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 26 Sep 2025 08:57:31 -0400 Subject: [PATCH 021/442] update tested versions of Visual Studio 2025-09-26 --- sys/windows/Makefile.nmake | 49 ++++++++------------------ sys/windows/vs/hacklib/hacklib.vcxproj | 8 ++--- sys/windows/vs/lualib/lualib.vcxproj | 6 ++-- 3 files changed, 22 insertions(+), 41 deletions(-) diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index 772b00e33..afb9108cc 100644 --- a/sys/windows/Makefile.nmake +++ b/sys/windows/Makefile.nmake @@ -8,7 +8,6 @@ # MS Visual Studio Visual C++ compiler # # Visual Studio Compilers Tested: -# - Microsoft Visual Studio 2019 Community Edition v 16.11.42 # - Microsoft Visual Studio 2022 Community Edition v 17.14.15 # #============================================================================== @@ -1178,15 +1177,14 @@ rc=Rc.exe # is too old or untested. # # Recently tested versions: -TESTEDVS2019 = 14.29.30157.0 TESTEDVS2022 = 14.44.35217.0 +# Other versions: +TESTEDVS2026 = 14.50.35503.0 -VS2019CUR = $(TESTEDVS2019:.=) -VS2022CUR = $(TESTEDVS2022:.=) -VS2019UP1 = $(VS2019CUR) + 1 -VS2022UP1 = $(VS2022CUR) + 1 -VS20191ST = 1419300000 -VS20221ST = $(VS2019UP1) +VS20261ST = 1450000000 +VS2026CUR = $(TESTEDVS2026:.=) +VS2026DN1 = $(VS20261ST) - 1 +VS2026UP1 = $(VS2026CUR) + 1 #!MESSAGE $(MAKEFLAGS) #!MESSAGE $(MAKEDIR) @@ -1198,28 +1196,25 @@ MAKEVERSION=$(MAKEVERSION: =) #!MESSAGE $(MAKEVERSION) VSSPECIAL= -VSNEWEST=2022 +VSNEWEST=2026 !IF ($(MAKEVERSION) < 1411000000) VSVER=0000 #untested ancient version -!ELSEIF ($(MAKEVERSION) < $(VS20191ST)) -VSVER=2017 -!ELSEIF ($(MAKEVERSION) > $(VS20191ST)) && ($(MAKEVERSION) < $(VS2019UP1)) -VSVER=2019 -!ELSEIF ($(MAKEVERSION) > $(VS20221ST)) && ($(MAKEVERSION) < $(VS2022UP1)) +!ELSEIF ($(MAKEVERSION) < $(VS20261ST)) VSVER=2022 -!ELSEIF ($(MAKEVERSION) > $(VS2022CUR)) +!ELSEIF ($(MAKEVERSION) > $(VS20261ST)) && ($(MAKEVERSION) < $(VS2026UP1)) +VSVER=2026 +!ELSEIF ($(MAKEVERSION) > $(VS2026CUR)) VSVER=2999 #untested future version !ENDIF -!IF ($(VSVER) >= 2012) !IF ($(VSVER) <= $(VSNEWEST)) -!IF ($(VSVER) == 2017) -!MESSAGE Autodetected Visual Studio $(VSVER) which we stopped testing with in March 2024 +!IF ($(MAKEVERSION) < $(VS20261ST)) +!MESSAGE Autodetected Visual Studio $(VSVER) or earlier !ELSE !MESSAGE Autodetected Visual Studio $(VSVER) $(VSSPECIAL) !ENDIF !ENDIF -!ENDIF + !IF ($(VSVER) == 2999) !MESSAGE The NMAKE version of this Visual Studio $(_NMAKE_VER) is newer than the !MESSAGE most recent at the time this Makefile was crafted (Visual Studio $(VSNEWEST)). @@ -1227,25 +1222,11 @@ VSVER=2999 #untested future version !MESSAGE will still work. !ELSEIF ($(VSVER) == 0000) !MESSAGE The version of Visual Studio appears to be quite old, older -!MESSAGE than VS2010 which is the oldest supported version by this +!MESSAGE than VS2010 which is the oldest version confirmed to work with this !MESSAGE Makefile, so we'll stop now. !ERROR Untested old Visual Studio version with NMAKE $(_NMAKE_VER). !ENDIF -!IF ($(VSVER) == 2010) -# For VS2010 use "setenv /x86" or "setenv /x64" before invoking make process -# DO NOT DELETE THE FOLLOWING LINE -!include -! ENDIF - -!IF ($(VSVER) == 2010) -CL_RECENT= -!ELSE -! IF ($(VSVER) > 2010) -CL_RECENT=-sdl -! ENDIF -!ENDIF - !IF ($(VSVER) >= 2019) && ("$(WANT_ASAN)"=="Y") ASAN=/fsanitize=address !ELSE diff --git a/sys/windows/vs/hacklib/hacklib.vcxproj b/sys/windows/vs/hacklib/hacklib.vcxproj index 23b673271..9243ceb8c 100644 --- a/sys/windows/vs/hacklib/hacklib.vcxproj +++ b/sys/windows/vs/hacklib/hacklib.vcxproj @@ -67,13 +67,13 @@ StaticLibrary true - v143 + v145 Unicode StaticLibrary false - v143 + v145 true Unicode @@ -104,7 +104,7 @@ $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) stdclatest /w45262 %(AdditionalOptions) - + @@ -179,4 +179,4 @@ - + \ No newline at end of file diff --git a/sys/windows/vs/lualib/lualib.vcxproj b/sys/windows/vs/lualib/lualib.vcxproj index 48cbb9f35..e8a5efe0a 100644 --- a/sys/windows/vs/lualib/lualib.vcxproj +++ b/sys/windows/vs/lualib/lualib.vcxproj @@ -93,13 +93,13 @@ StaticLibrary true - v143 + v145 Unicode StaticLibrary false - v143 + v145 true Unicode @@ -201,4 +201,4 @@ - + \ No newline at end of file From 976aca6530e945fec3dbd16655d91525eaf3991d Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 26 Sep 2025 08:59:49 -0400 Subject: [PATCH 022/442] follow-up bit for visual studio --- sys/windows/build-vs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/windows/build-vs.txt b/sys/windows/build-vs.txt index a144470df..2585c0308 100644 --- a/sys/windows/build-vs.txt +++ b/sys/windows/build-vs.txt @@ -54,7 +54,7 @@ versions: /-----------------------------------------------------------\ -| Building And Running Using Visual Studio 2019, 2022 | +| Building And Running Using Visual Studio 2022 or greater | \-----------------------------------------------------------/ When using Visual Studio Community Edition, load the provided solution From ea4ffba1ce6d84ede3d933f3cdfda2d836c08163 Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 26 Sep 2025 09:13:38 -0400 Subject: [PATCH 023/442] another visual studio follow-up --- sys/windows/.gitattributes | 2 +- sys/windows/vs/.gitattributes | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sys/windows/.gitattributes b/sys/windows/.gitattributes index 823c775c1..86544ef62 100644 --- a/sys/windows/.gitattributes +++ b/sys/windows/.gitattributes @@ -5,6 +5,6 @@ sysconf NHSUBST *.rc NHSUBST *.bat NHSUBST *.def NH_header=no -* NH_filestag=(file%s_for_Windows_7/8.x/10/11_version) +* NH_filestag=(file%s_for_Windows_10/11_version) ..files NH_filegenerated=nethack.ico nethack.ico NH_filesgentag=(file%s_generated_by_uudecode_at_compile_time) diff --git a/sys/windows/vs/.gitattributes b/sys/windows/vs/.gitattributes index 852f61477..047ae4d7a 100644 --- a/sys/windows/vs/.gitattributes +++ b/sys/windows/vs/.gitattributes @@ -1,2 +1,2 @@ -* NH_filestag=(file%s_for_Visual_Studio_2019_or_2022_Community_Edition_builds) +* NH_filestag=(file%s_for_Visual_Studio_2022_Community_Edition_builds) ..files !NH_filegenerated From c96297c052696cd99026966c1fb8057af2c40de3 Mon Sep 17 00:00:00 2001 From: nhw_cron Date: Fri, 26 Sep 2025 09:24:06 -0400 Subject: [PATCH 024/442] This is cron-daily v1-Apr-1-2024. 000files updated: Files --- Files | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/Files b/Files index 1ed552cf1..40aa65597 100644 --- a/Files +++ b/Files @@ -431,7 +431,7 @@ vmsmain.c vmsmisc.c vmssetup.com vmstty.c vmsunix.c sys/windows: -(files for Windows 7/8.x/10/11 version) +(files for Windows 10/11 version) GNUmakefile GNUmakefile.depend Install.windows Makefile.nmake build-msys2.txt build-nmake.txt build-vs.txt console.rc consoletty.c @@ -442,7 +442,7 @@ win10.c win10.h win32api.h windmain.c windsys.c winos.h sys/windows/vs: -(files for Visual Studio 2019 or 2022 Community Edition builds) +(files for Visual Studio 2022 Community Edition builds) NetHack.sln NetHackPackage.appxmanifest NetHackPackage.wapproj NetHackProperties.props Package.StoreAssociation.xml ScreenShot.PNG @@ -454,11 +454,11 @@ dirs.props dll.props files.props sfctool.sln sys/windows/vs/FetchPrereq: -(files for Visual Studio 2019 or 2022 Community Edition builds) +(files for Visual Studio 2022 Community Edition builds) fetchprereq.nmake fetchprereq.vcxproj sys/windows/vs/Images: -(files for Visual Studio 2019 or 2022 Community Edition builds) +(files for Visual Studio 2022 Community Edition builds) BadgeLogo.scale-100.png BadgeLogo.scale-125.png BadgeLogo.scale-150.png @@ -513,71 +513,71 @@ Wide310x150Logo.scale-200.png Wide310x150Logo.scale-400.png sys/windows/vs/NetHack: -(files for Visual Studio 2019 or 2022 Community Edition builds) +(files for Visual Studio 2022 Community Edition builds) NetHack.vcxproj afternethack.proj sys/windows/vs/NetHackW: -(file for Visual Studio 2019 or 2022 Community Edition builds) +(file for Visual Studio 2022 Community Edition builds) NetHackW.vcxproj sys/windows/vs/PDCurses: -(file for Visual Studio 2019 or 2022 Community Edition builds) +(file for Visual Studio 2022 Community Edition builds) PDCurses.vcxproj sys/windows/vs/PDCursesGui: -(file for Visual Studio 2019 or 2022 Community Edition builds) +(file for Visual Studio 2022 Community Edition builds) pdcursesgui.vcxproj sys/windows/vs/dlb: -(files for Visual Studio 2019 or 2022 Community Edition builds) +(files for Visual Studio 2022 Community Edition builds) afterdlb.proj dlb.vcxproj sys/windows/vs/fetchctags: -(files for Visual Studio 2019 or 2022 Community Edition builds) +(files for Visual Studio 2022 Community Edition builds) fetchctags.nmake fetchctags.vcxproj sys/windows/vs/hacklib: -(file for Visual Studio 2019 or 2022 Community Edition builds) +(file for Visual Studio 2022 Community Edition builds) hacklib.vcxproj sys/windows/vs/lualib: -(file for Visual Studio 2019 or 2022 Community Edition builds) +(file for Visual Studio 2022 Community Edition builds) lualib.vcxproj sys/windows/vs/makedefs: -(files for Visual Studio 2019 or 2022 Community Edition builds) +(files for Visual Studio 2022 Community Edition builds) aftermakedefs.proj makedefs.vcxproj sys/windows/vs/package: -(files for Visual Studio 2019 or 2022 Community Edition builds) +(files for Visual Studio 2022 Community Edition builds) package.nmake package.vcxproj sys/windows/vs/recover: -(files for Visual Studio 2019 or 2022 Community Edition builds) +(files for Visual Studio 2022 Community Edition builds) afterrecover.proj recover.vcxproj sys/windows/vs/sfctool: -(file for Visual Studio 2019 or 2022 Community Edition builds) +(file for Visual Studio 2022 Community Edition builds) sfctool.vcxproj sys/windows/vs/sftags: -(files for Visual Studio 2019 or 2022 Community Edition builds) +(files for Visual Studio 2022 Community Edition builds) aftersftags.proj sftags.vcxproj sys/windows/vs/tile2bmp: -(files for Visual Studio 2019 or 2022 Community Edition builds) +(files for Visual Studio 2022 Community Edition builds) aftertile2bmp.proj tile2bmp.vcxproj sys/windows/vs/tilemap: -(files for Visual Studio 2019 or 2022 Community Edition builds) +(files for Visual Studio 2022 Community Edition builds) aftertilemap.proj tilemap.vcxproj sys/windows/vs/tiles: -(file for Visual Studio 2019 or 2022 Community Edition builds) +(file for Visual Studio 2022 Community Edition builds) tiles.vcxproj sys/windows/vs/uudecode: -(files for Visual Studio 2019 or 2022 Community Edition builds) +(files for Visual Studio 2022 Community Edition builds) afteruudecode.proj uudecode.vcxproj test: From ef1174ed1bbdaa4d3dc5de131b8dd8363ee28146 Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 30 Sep 2025 12:06:48 -0400 Subject: [PATCH 025/442] clean up a warning about missing function def --- util/makedefs.c | 1 - 1 file changed, 1 deletion(-) diff --git a/util/makedefs.c b/util/makedefs.c index bc86614bd..53c90c151 100644 --- a/util/makedefs.c +++ b/util/makedefs.c @@ -152,7 +152,6 @@ void do_oracles(void); void do_date(void); void do_dungeon(void); void do_options(void); -void do_monstr(void); void do_objs(void); void do_permonst(void); void do_questtxt(void); From bbc85783755d095a7be98d5ec507a8275a418ebc Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 30 Sep 2025 12:35:18 -0400 Subject: [PATCH 026/442] get rid of a visual studio build warning lnt-arithmetic-overflow A sub-expression may overflow before being assigned to a wider type --- src/hack.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hack.c b/src/hack.c index 7c30870c7..1f0873a79 100644 --- a/src/hack.c +++ b/src/hack.c @@ -4327,9 +4327,10 @@ dump_weights(void) { int i, cnt = 0, nmwidth = 49, mcount = NUMMONS, ocount = NUM_OBJECTS; char nmbuf[BUFSZ], nmbufbase[BUFSZ]; + size_t num_entries = (size_t) (mcount + ocount); weightlist = (struct weight_table_entry *) - alloc(sizeof (struct weight_table_entry) * (mcount + ocount)); + alloc(sizeof (struct weight_table_entry) * num_entries); decl_globals_init(); init_objects(); for (i = 0; i < mcount; ++i) { From c0686d1795bbd93af82ba7f56c058b13c5035521 Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 30 Sep 2025 12:41:55 -0400 Subject: [PATCH 027/442] clear pair of warnings re: not initialized --- src/hack.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hack.c b/src/hack.c index 1f0873a79..89c153746 100644 --- a/src/hack.c +++ b/src/hack.c @@ -104,7 +104,7 @@ obj_to_any(struct obj *obj) boolean revive_nasty(coordxy x, coordxy y, const char *msg) { - struct obj *otmp, *otmp2; + struct obj *otmp = 0, *otmp2 = 0; struct monst *mtmp; coord cc; boolean revived = FALSE; @@ -3379,7 +3379,7 @@ char * in_rooms(coordxy x, coordxy y, int typewanted) { static char buf[5]; - char rno, *ptr = &buf[4]; + char rno = 0, *ptr = &buf[4]; int typefound, min_x, min_y, max_x, max_y_offset, step; struct rm *lev; From bdffed390c5527d6a4d694dfcd21d157a758e8d2 Mon Sep 17 00:00:00 2001 From: Aaron Lebahn Date: Thu, 2 Oct 2025 11:36:49 -0500 Subject: [PATCH 028/442] Fix typo in encyclopedia --- dat/data.base | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dat/data.base b/dat/data.base index ed42fca25..d43307f33 100644 --- a/dat/data.base +++ b/dat/data.base @@ -3025,7 +3025,7 @@ knight And was a bugbear in men's eyes; But had the fortune in his age To live a fool and die a sage. - [ Don Quixote of La Mancha, by Miquel de Cervantes Saavedra ] + [ Don Quixote of La Mancha, by Miguel de Cervantes Saavedra ] ~kobold ??m* *kobold* The race of kobolds are reputed to be an artificial creation @@ -3144,7 +3144,7 @@ lance his lance into shivers, carrying him and his horse after it, and finally tumbled him a good way off from it on the field in evil plight. - [ Don Quixote of La Mancha, by Miquel de Cervantes Saavedra ] + [ Don Quixote of La Mancha, by Miguel de Cervantes Saavedra ] land mine Your heart is intact, your brain is not badly damaged, but the rest of your injuries are comparable to stepping on a land mine. You'd From a3b60ba36f8b25deb34efd27824ff91b598b861f Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 3 Oct 2025 10:00:48 -0400 Subject: [PATCH 029/442] build bit for CROSS_TO_WASM=1 ../win/shim/winshim.c:194:23: error: unused parameter 'wri' [-Werror,-Wunused-parameter] 194 | win_request_info *wri) { | ^ 1 error generated. --- win/shim/winshim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/win/shim/winshim.c b/win/shim/winshim.c index ae4caed12..fdbe18b9e 100644 --- a/win/shim/winshim.c +++ b/win/shim/winshim.c @@ -191,7 +191,7 @@ win_request_info * shim_ctrl_nhwindow( winid window UNUSED, int request UNUSED, - win_request_info *wri) { + win_request_info *wri UNUSED) { return (win_request_info *) 0; } #else /* !__EMSCRIPTEN__ */ From 9b375ea488068f121a02f4f1c882e1b79dbdf4b2 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 5 Oct 2025 10:18:24 -0400 Subject: [PATCH 030/442] revert a couple of unintended vcxproj changes --- sys/windows/vs/hacklib/hacklib.vcxproj | 4 ++-- sys/windows/vs/lualib/lualib.vcxproj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sys/windows/vs/hacklib/hacklib.vcxproj b/sys/windows/vs/hacklib/hacklib.vcxproj index 9243ceb8c..9ac94ffb1 100644 --- a/sys/windows/vs/hacklib/hacklib.vcxproj +++ b/sys/windows/vs/hacklib/hacklib.vcxproj @@ -67,13 +67,13 @@ StaticLibrary true - v145 + v143 Unicode StaticLibrary false - v145 + v143 true Unicode diff --git a/sys/windows/vs/lualib/lualib.vcxproj b/sys/windows/vs/lualib/lualib.vcxproj index e8a5efe0a..185f94b9d 100644 --- a/sys/windows/vs/lualib/lualib.vcxproj +++ b/sys/windows/vs/lualib/lualib.vcxproj @@ -93,13 +93,13 @@ StaticLibrary true - v145 + v143 Unicode StaticLibrary false - v145 + v143 true Unicode From 60e2598d81e53b1cdaa187f61d92e455d1a145cb Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 5 Oct 2025 22:10:34 -0400 Subject: [PATCH 031/442] SYMBOLS=S_ghost: Resolves #1448 --- doc/Guidebook.mn | 5 +++-- doc/Guidebook.tex | 5 +++-- src/glyphs.c | 4 ++-- win/share/tilemap.c | 8 ++++---- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index dae3b6713..1139806e5 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -3664,7 +3664,7 @@ See the \(lqModifying NetHack Symbols\(rq section. Example: .sd \f(CR# replace small punctuation (tick marks) with digits\fP -\f(CRSYMBOLS=S_boulder:0,S_golem:7\fP +\f(CRSYMBOLS=S_golem:7\fP .ed .lp WIZKIT Debug mode only: extra items to add to initial inventory. @@ -3699,7 +3699,8 @@ OPTIONS=color # Display things in color if possible OPTIONS=lit_corridor # Show lit corridors differently OPTIONS=hilite_pet,hilite_pile # Replace small punctuation (tick marks) with digits -SYMBOLS=S_boulder:0,S_golem:7 +OPTIONS=boulder:0 +SYMBOLS=S_golem:7 # # No startup splash screen. Windows GUI only. OPTIONS=!splash_screen diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index a54d02b68..f3d3cfc38 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -4015,7 +4015,7 @@ Example: %.sd \begin{verbatim} # replace small punctuation (tick marks) with digits - SYMBOLS=S_boulder:0,S_golem:7 + SYMBOLS=S_golem:7 \end{verbatim} %.ed @@ -4051,7 +4051,8 @@ Here is an example of configuration file contents: OPTIONS=lit_corridor # Show lit corridors differently OPTIONS=hilite_pet,hilite_pile # Replace small punctuation (tick marks) with digits - SYMBOLS=S_boulder:0,S_golem:7 + OPTIONS=boulder:0 + SYMBOLS=S_golem:7 # No startup splash screen. Windows GUI only. OPTIONS=!splash_screen diff --git a/src/glyphs.c b/src/glyphs.c index 8b6a77854..c584e212f 100644 --- a/src/glyphs.c +++ b/src/glyphs.c @@ -251,7 +251,7 @@ glyph_find_core( break; case find_pm: if (glyph_is_monster(glyph) - && monsym(&mons[glyph_to_mon(glyph)]) + && mons[glyph_to_mon(glyph)].mlet == findwhat->val) do_callback = TRUE; break; @@ -819,7 +819,7 @@ parse_id( int i = 0, j, mnum, glyph, pm_offset = 0, oc_offset = 0, cmap_offset = 0, pm_count = 0, oc_count = 0, cmap_count = 0; - boolean skip_base = FALSE, skip_this_one, dump_ids = FALSE, + boolean skip_base = FALSE, skip_this_one = FALSE, dump_ids = FALSE, filling_cache = FALSE, is_S = FALSE, is_G = FALSE; char buf[4][QBUFSZ]; diff --git a/win/share/tilemap.c b/win/share/tilemap.c index 832bae769..41583dfa0 100644 --- a/win/share/tilemap.c +++ b/win/share/tilemap.c @@ -1235,11 +1235,11 @@ init_tilemap(void) Snprintf(tilemap[GLYPH_STATUE_MALE_OFF + i].name, sizeof tilemap[0].name, "statue of male %s (mnum=%d)", - tilename(MON_GLYPH, file_entry, 0), file_entry); + tilename(MON_GLYPH, file_entry, 0), i); Snprintf(tilemap[GLYPH_STATUE_MALE_PILETOP_OFF + i].name, sizeof tilemap[0].name, "piletop statue of male %s (mnum=%d)", - tilename(MON_GLYPH, file_entry, 0), file_entry); + tilename(MON_GLYPH, file_entry, 0), i); add_tileref(tilenum, GLYPH_STATUE_MALE_OFF + i, generated, file_entry, tilemap[GLYPH_STATUE_MALE_OFF + i].name, ""); @@ -1258,10 +1258,10 @@ init_tilemap(void) Snprintf(tilemap[GLYPH_STATUE_FEM_OFF + i].name, sizeof tilemap[0].name, "statue of female %s (mnum=%d)", - tilename(MON_GLYPH, file_entry, 0), file_entry); + tilename(MON_GLYPH, file_entry, 0), i); Sprintf(tilemap[GLYPH_STATUE_FEM_PILETOP_OFF + i].name, "piletop statue of female %s (mnum=%d)", - tilename(MON_GLYPH, file_entry, 0), file_entry); + tilename(MON_GLYPH, file_entry, 0), i); add_tileref(tilenum, GLYPH_STATUE_FEM_OFF + i, generated, file_entry, tilemap[GLYPH_STATUE_FEM_OFF + i].name, ""); add_tileref(tilenum, GLYPH_STATUE_FEM_PILETOP_OFF + i, generated, From 9f6d1d17ddbf842472e4acc59c6d375ffe22997b Mon Sep 17 00:00:00 2001 From: nhw_cron Date: Sun, 5 Oct 2025 22:24:09 -0400 Subject: [PATCH 032/442] This is cron-daily v1-Apr-1-2024. 005guidebook updated: doc/Guidebook.txt --- doc/Guidebook.txt | 576 +++++++++++++++++++++++----------------------- 1 file changed, 288 insertions(+), 288 deletions(-) diff --git a/doc/Guidebook.txt b/doc/Guidebook.txt index e36569992..573fccdef 100644 --- a/doc/Guidebook.txt +++ b/doc/Guidebook.txt @@ -4058,7 +4058,7 @@ Example: # replace small punctuation (tick marks) with digits - SYMBOLS=S_boulder:0,S_golem:7 + SYMBOLS=S_golem:7 WIZKIT Debug mode only: extra items to add to initial inventory. Value is @@ -4107,7 +4107,8 @@ OPTIONS=lit_corridor # Show lit corridors differently OPTIONS=hilite_pet,hilite_pile # Replace small punctuation (tick marks) with digits - SYMBOLS=S_boulder:0,S_golem:7 + OPTIONS=boulder:0 + SYMBOLS=S_golem:7 # # No startup splash screen. Windows GUI only. OPTIONS=!splash_screen @@ -4149,7 +4150,6 @@ be set to the full name of a configuration file you want to use. If that full name doesn't start with a slash, precede it with `@' (at- sign) to let NetHack know that the rest is intended as a file name. - If it does start with `/', the at-sign is optional. NetHack 3.7.0 May 1, 2025 @@ -4162,6 +4162,8 @@ + If it does start with `/', the at-sign is optional. + 9.4. Customization options Here are explanations of what the various options do. Character @@ -4214,8 +4216,6 @@ autoquiver This option controls what happens when you attempt the `f' (fire) - command when nothing is quivered or readied (default false). When - true, the computer will fill your quiver or quiver sack or make NetHack 3.7.0 May 1, 2025 @@ -4228,6 +4228,8 @@ + command when nothing is quivered or readied (default false). When + true, the computer will fill your quiver or quiver sack or make ready some suitable weapon. Note that it will not take into account the blessed/cursed status, enchantment, damage, or quality of the weapon; you are free to manually fill your quiver or quiver sack or @@ -4280,8 +4282,6 @@ catname Name your starting cat (for example "catname:Morris"). Cannot be - set with the `O' command. - NetHack 3.7.0 May 1, 2025 @@ -4294,6 +4294,8 @@ + set with the `O' command. + character Synonym for "role" to pick the type of your character (for example "character:Monk"). See role for more details. @@ -4346,8 +4348,6 @@ The listings of vanquished monsters and of genocided types can be sorted, so there are two additional choices for `v' and `g': - ? - prompt you and default to ask on the prompt; - # - disclose it without prompting, ask for sort order. NetHack 3.7.0 May 1, 2025 @@ -4360,10 +4360,13 @@ - Asking refers to picking one of the orderings from a menu. The - `+' disclose without prompting choice, or being prompted and - answering `y' rather than `a', will default to showing monsters - in the order specified by the sortvanquished option. + ? - prompt you and default to ask on the prompt; + # - disclose it without prompting, ask for sort order. + + Asking refers to picking one of the orderings from a menu. The `+' + disclose without prompting choice, or being prompted and answering + `y' rather than `a', will default to showing monsters in the order + specified by the sortvanquished option. Omitted categories are implicitly added with `n' prefix. Specified categories with omitted prefix implicitly use `+' prefix. Order of @@ -4411,9 +4414,6 @@ on). If this is off, dropping an object shifts all the remaining inventory letters. Persistent. - force_invmenu - Commands asking for an inventory item show a menu instead of a text - query with possible menu letters. Default is off. NetHack 3.7.0 May 1, 2025 @@ -4426,6 +4426,10 @@ + force_invmenu + Commands asking for an inventory item show a menu instead of a text + query with possible menu letters. Default is off. + fruit Name a fruit after something you enjoy eating (for example "fruit:mango") (default "slime mold"). Basically a nostalgic whimsy @@ -4475,11 +4479,7 @@ highlight pets and setting it will turn the hilite_pet option on or off as warranted. - hilite_pile - Visually distinguish piles of objects from individual objects - (default off). The behavior of this option depends on the type of - windowing you use. In text windowing, text highlighting or inverse - video is often used; with tiles, generally displays a small plus- + NetHack 3.7.0 May 1, 2025 @@ -4492,6 +4492,11 @@ + hilite_pile + Visually distinguish piles of objects from individual objects + (default off). The behavior of this option depends on the type of + windowing you use. In text windowing, text highlighting or inverse + video is often used; with tiles, generally displays a small plus- symbol beside the object on the top of the pile. hitpointbar @@ -4541,11 +4546,6 @@ lootabc When using a menu to interact with a container, use the old `a', `b', and `c' keyboard shortcuts rather than the mnemonics `o', `i', - and `b' (default off). Persistent. - - mail - Enable mail delivery during the game (default on). Persistent. - NetHack 3.7.0 May 1, 2025 @@ -4558,6 +4558,11 @@ + and `b' (default off). Persistent. + + mail + Enable mail delivery during the game (default on). Persistent. + male An obsolete synonym for "gender:male". Cannot be set with the `O' command. @@ -4607,11 +4612,6 @@ menu_first_page Key to jump to the first page in a menu. Default `^'. - menu_headings - Controls how the headings in a menu are highlighted. Takes a text - attribute, or text color and attribute separated by ampersand. For - allowed attributes and colors, see "Configuring Menu Colors". Not - all ports can actually display all types. NetHack 3.7.0 May 1, 2025 @@ -4624,6 +4624,12 @@ + menu_headings + Controls how the headings in a menu are highlighted. Takes a text + attribute, or text color and attribute separated by ampersand. For + allowed attributes and colors, see "Configuring Menu Colors". Not + all ports can actually display all types. + menu_invert_all Key to invert all items in a menu. Default `@'. @@ -4672,12 +4678,6 @@ Key to search for some text and toggle selection state of matching menu items. Default `:'. - menu_select_all - Key to select all items in a menu. Default `.'. - - menu_select_page - Key to select all items on this page of a menu. Default `,'. - NetHack 3.7.0 May 1, 2025 @@ -4690,6 +4690,12 @@ + menu_select_all + Key to select all items in a menu. Default `.'. + + menu_select_page + Key to select all items on this page of a menu. Default `,'. + menu_shift_left Key to scroll a menu--one which has been scrolled right--back to the left. Implemented for perm_invent only by curses and X11. Default @@ -4738,12 +4744,6 @@ For backward compatibility, no value needs to be specified (which defaults to "full"), or it can be negated (which defaults to "sin- - gle"). - - name - Set your character's name (defaults to your user name). You can - also set your character's role by appending a dash and one or more - letters of the role (that is, by suffixing one of -A -B -C -H -K -M NetHack 3.7.0 May 1, 2025 @@ -4756,6 +4756,12 @@ + gle"). + + name + Set your character's name (defaults to your user name). You can + also set your character's role by appending a dash and one or more + letters of the role (that is, by suffixing one of -A -B -C -H -K -M -P -Ra -Ro -S -T -V -W). If -@ is used for the role, then a random one will be automatically chosen. @@ -4804,12 +4810,6 @@ paranoid_confirmation A space separated list of specific situations where alternate - prompting is desired. The default is "paranoid_confirmation:pray - swim trap". - - Confirm - for any prompts which are set to require "yes" rather - than `y', also require "no" to reject instead of - accepting any non-yes response as no; changes pray and NetHack 3.7.0 May 1, 2025 @@ -4822,6 +4822,12 @@ + prompting is desired. The default is "paranoid_confirmation:pray + swim trap". + + Confirm - for any prompts which are set to require "yes" rather + than `y', also require "no" to reject instead of + accepting any non-yes response as no; changes pray and AutoAll to require "yes" or `no' too; quit - require "yes" rather than `y' to confirm quitting the game or switching into non-scoring explore mode; @@ -4870,12 +4876,6 @@ new entries and remove some old ones, you can use multiple para- noid_confirmation option settings, or you can use the `+' form and list entries to be added by their name and entries to be removed by - `!' and name. The positive (no `!') and negative (with `!') entries - can be intermixed. - - pauper - Start the character with no possessions (default false). Persis- - tent. NetHack 3.7.0 May 1, 2025 @@ -4888,6 +4888,13 @@ + `!' and name. The positive (no `!') and negative (with `!') entries + can be intermixed. + + pauper + Start the character with no possessions (default false). Persis- + tent. + perm_invent If true, always display your current inventory in a window (default false). @@ -4934,14 +4941,7 @@ Loaded), you will be asked if you want to continue. (Default `S'). Persistent. - pickup_stolen - If this option is on and autopickup is also on, try to pick up - things that a monster stole from you, even if they aren't in - pickup_types or match an autopickup exception. Default is on. Per- - sistent. - pickup_thrown - If this option is on and autopickup is also on, try to pick up NetHack 3.7.0 May 1, 2025 @@ -4954,6 +4954,14 @@ + pickup_stolen + If this option is on and autopickup is also on, try to pick up + things that a monster stole from you, even if they aren't in + pickup_types or match an autopickup exception. Default is on. Per- + sistent. + + pickup_thrown + If this option is on and autopickup is also on, try to pick up things that you threw, even if they aren't in pickup_types or match an autopickup exception. Default is on. Persistent. @@ -5000,14 +5008,6 @@ query_menu Use a menu when asked specific yes/no queries, instead of a prompt. - quick_farsight - When set, usually prevents the "you sense your surroundings" message - where play pauses to allow you to browse the map whenever clairvoy- - ance randomly activates. Some situations, such as being underwater - or engulfed, ignore this option. It does not affect the clairvoy- - ance spell where pausing to examine revealed objects or monsters is - less intrusive. Default is off. Persistent. - NetHack 3.7.0 May 1, 2025 @@ -5020,6 +5020,14 @@ + quick_farsight + When set, usually prevents the "you sense your surroundings" message + where play pauses to allow you to browse the map whenever clairvoy- + ance randomly activates. Some situations, such as being underwater + or engulfed, ignore this option. It does not affect the clairvoy- + ance spell where pausing to examine revealed objects or monsters is + less intrusive. Default is off. Persistent. + race Selects your race (for example, race:human). Choices are human, dwarf, elf, gnome, and orc but most roles restrict which of the non- @@ -5065,15 +5073,7 @@ map. Not all ports support run length compression. It has no effect on reading an existing save file. - runmode - Controls the amount of screen updating for the map window when - engaged in multi-turn movement (running via shift+direction or con- - trol+direction and so forth, or via the travel command or mouse - click). The possible values are: - teleport - update the map after movement has finished; - run - update the map after every seven or so steps; - walk - update the map after each step; NetHack 3.7.0 May 1, 2025 @@ -5086,6 +5086,15 @@ + runmode + Controls the amount of screen updating for the map window when + engaged in multi-turn movement (running via shift+direction or con- + trol+direction and so forth, or via the travel command or mouse + click). The possible values are: + + teleport - update the map after movement has finished; + run - update the map after every seven or so steps; + walk - update the map after each step; crawl - like walk, but pause briefly after each step. This option only affects the game's screen display, not the actual @@ -5131,15 +5140,6 @@ off). By default, this feature is suppressed when building the pro- gram. Persistent. - showvers - Include the game's version number on the status lines (default off). - Potentially useful if you switch between different versions or vari- - ants, or you are making screenshots or streaming video. Using the - statuslines:3 option is recommended so that there will be more room - available for status information, unless you're using nethack's Qt - interface or your terminal emulator window displays fewer than 25 - lines. Persistent. - NetHack 3.7.0 May 1, 2025 @@ -5152,6 +5152,15 @@ + showvers + Include the game's version number on the status lines (default off). + Potentially useful if you switch between different versions or vari- + ants, or you are making screenshots or streaming video. Using the + statuslines:3 option is recommended so that there will be more room + available for status information, unless you're using nethack's Qt + interface or your terminal emulator window displays fewer than 25 + lines. Persistent. + silent Suppress terminal beeps (default on). Persistent. @@ -5197,15 +5206,6 @@ t - traditional--order by monster level; ties are broken by internal monster index; default; - d - order by monster difficulty rating; ties broken by internal - index; - a - order alphabetically, first any unique monsters then all the - others; - c - order by monster class, by low to high level within each class; - n - order by count, high to low; ties are broken by internal monster - index; - z - order by count, low to high; ties broken by internal index. - NetHack 3.7.0 May 1, 2025 @@ -5218,6 +5218,15 @@ + d - order by monster difficulty rating; ties broken by internal + index; + a - order alphabetically, first any unique monsters then all the + others; + c - order by monster class, by low to high level within each class; + n - order by count, high to low; ties are broken by internal monster + index; + z - order by count, low to high; ties broken by internal index. + Can be interactively set via the `m O' command or via using the `m' prefix before either the #vanquished command or the #genocided com- mand. @@ -5263,15 +5272,6 @@ When pausing momentarily for display effect, such as with explosions and moving objects, use a timer rather than sending extra characters to the screen. (Applies to "tty" and "curses" interfaces only; - "X11" interface always uses a timer-based delay. The default is on - if configured into the program.) Persistent. - - tips - Show some helpful tips during gameplay (default on). Persistent. - - tombstone - Draw a tombstone graphic upon your death (default on). Persistent. - NetHack 3.7.0 May 1, 2025 @@ -5284,6 +5284,15 @@ + "X11" interface always uses a timer-based delay. The default is on + if configured into the program.) Persistent. + + tips + Show some helpful tips during gameplay (default on). Persistent. + + tombstone + Draw a tombstone graphic upon your death (default on). Persistent. + toptenwin Put the ending display in a NetHack window instead of on stdout (default off). Setting this option makes the score list visible @@ -5330,15 +5339,6 @@ v - in view only a - in same area only - The area-filter tries to be slightly predictive--if you're standing - on a doorway, it will consider the area on the side of the door you - were last moving towards. - - Filtering can also be changed when getting a location with the "get- - pos.filter" key. - - - NetHack 3.7.0 May 1, 2025 @@ -5350,6 +5350,13 @@ + The area-filter tries to be slightly predictive--if you're + standing on a doorway, it will consider the area on the side of + the door you were last moving towards. + + Filtering can also be changed when getting a location with the "get- + pos.filter" key. + whatis_menu When getting a location on the map, and using a key to cycle through next and previous targets, use a menu instead to pick a target. @@ -5396,13 +5403,6 @@ Some options are dynamic and can be specified during the game with the `O' command. - align_message - Where to align or place the message window (top, bottom, left, or - right) - - align_status - Where to align or place the status window (top, bottom, left, or - right). @@ -5416,6 +5416,14 @@ + align_message + Where to align or place the message window (top, bottom, left, or + right) + + align_status + Where to align or place the status window (top, bottom, left, or + right). + ascii_map If NetHack can, it should display the map using simple characters (letters and punctuation) rather than tiles graphics. In some @@ -5462,14 +5470,6 @@ font_size_message If NetHack can, it should use this size font for the message window. - font_size_status - If NetHack can, it should use this size font for the status window. - - font_size_text - If NetHack can, it should use this size font for text windows. - - fullscreen - If NetHack can, it should try to display on the entire screen rather NetHack 3.7.0 May 1, 2025 @@ -5482,6 +5482,14 @@ + font_size_status + If NetHack can, it should use this size font for the status window. + + font_size_text + If NetHack can, it should use this size font for text windows. + + fullscreen + If NetHack can, it should try to display on the entire screen rather than in a window. guicolor @@ -5528,14 +5536,6 @@ splash_screen If NetHack can, it should display an opening splash screen when it - starts up (default yes). - - statuslines - Number of lines for traditional below-the-map status display. - Acceptable values are 2 and 3 (default is 2). - - When set to 3, the tty interface moves some fields around and mainly - shows status conditions on their own line. A display capable of NetHack 3.7.0 May 1, 2025 @@ -5548,6 +5548,14 @@ + starts up (default yes). + + statuslines + Number of lines for traditional below-the-map status display. + Acceptable values are 2 and 3 (default is 2). + + When set to 3, the tty interface moves some fields around and mainly + shows status conditions on their own line. A display capable of showing at least 25 lines is recommended. The value can be toggled back and forth during the game with the `O' command. @@ -5595,14 +5603,6 @@ use_darkgray Use bold black instead of blue for black glyphs (TTY only). - use_inverse - If NetHack can, it should display inverse when the game specifies - it. - - vary_msgcount - If NetHack can, it should display this number of messages at a time - in the message window. - NetHack 3.7.0 May 1, 2025 @@ -5614,6 +5614,14 @@ + use_inverse + If NetHack can, it should display inverse when the game specifies + it. + + vary_msgcount + If NetHack can, it should display this number of messages at a time + in the message window. + windowborders Whether to draw boxes around the map, status area, message area, and persistent inventory window if enabled. Curses interface only. @@ -5660,14 +5668,6 @@ If NetHack can, it should wrap long lines of text if they don't fit in the visible area of the window. - 9.6. Crash Report Options - - Please note that NetHack does not send any information off your - computer unless you manually click submit on a form. - - OPTION=crash_email:email_address - - NetHack 3.7.0 May 1, 2025 @@ -5680,6 +5680,13 @@ + 9.6. Crash Report Options + + Please note that NetHack does not send any information off your + computer unless you manually click submit on a form. + + OPTION=crash_email:email_address + OPTION=crash_name:your_name These options are used only to save you some typing on the crash report and #bugreport forms. @@ -5727,13 +5734,6 @@ subkeyvalue (Win32 tty NetHack only). May be used to alter the value of key- strokes that the operating system returns to NetHack to help compen- - sate for international keyboard issues. OPTIONS=subkeyvalue:171/92 - will return 92 to NetHack, if 171 was originally going to be - returned. You can use multiple subkeyvalue assignments in the con- - figuration file if needed. Cannot be set with the `O' command. - - video - Set the video mode used (PC NetHack only). Values are "autodetect", NetHack 3.7.0 May 1, 2025 @@ -5746,6 +5746,13 @@ + sate for international keyboard issues. OPTIONS=subkeyvalue:171/92 + will return 92 to NetHack, if 171 was originally going to be + returned. You can use multiple subkeyvalue assignments in the con- + figuration file if needed. Cannot be set with the `O' command. + + video + Set the video mode used (PC NetHack only). Values are "autodetect", "default", "vga", or "vesa". Setting "vesa" will cause the game to display tiles, using the full capability of the VGA hardware. Set- ting "vga" will cause the game to display tiles, fixed at 640x480 in @@ -5792,14 +5799,7 @@ you can define patterns to be checked when the game is about to autopickup something. - autopickup_exception - Sets an exception to the pickup_types option. The autopickup_excep- - tion option should be followed by a regular expression to be used as - a pattern to match against the singular form of the description of - an object at your location. - In addition, some characters are treated specially if they occur as - the first character in the pattern, specifically: NetHack 3.7.0 May 1, 2025 @@ -5812,6 +5812,15 @@ + autopickup_exception + Sets an exception to the pickup_types option. The autopickup_excep- + tion option should be followed by a regular expression to be used as + a pattern to match against the singular form of the description of + an object at your location. + + In addition, some characters are treated specially if they occur as + the first character in the pattern, specifically: + < - always pickup an object that matches rest of pattern; > - never pickup an object that matches rest of pattern. @@ -5857,15 +5866,6 @@ Menu accelerator keys The menu control or accelerator keys can also be rebound via OPTIONS - lines in the configuration file. You cannot bind object symbols or - selection letters into menu accelerators. Some interfaces only sup- - port some of the menu accelerators. - - Mouse buttons - You can bind "mouse1" or "mouse2" to "nothing", "therecmdmenu", - "clicklook", or "mouseaction". - - NetHack 3.7.0 May 1, 2025 @@ -5878,6 +5878,14 @@ + lines in the configuration file. You cannot bind object symbols or + selection letters into menu accelerators. Some interfaces only sup- + port some of the menu accelerators. + + Mouse buttons + You can bind "mouse1" or "mouse2" to "nothing", "therecmdmenu", + "clicklook", or "mouseaction". + Special command keys Below are the special commands you can rebind. Some of them can be bound to same keys with no problems, others are in the same "con- @@ -5924,14 +5932,6 @@ When asked for a location, the key to go to next closest door or doorway. Default is `d'. - getpos.door.prev - When asked for a location, the key to go to previous closest door or - doorway. Default is `D'. - - getpos.help - When asked for a location, the key to show help. Default is `?'. - - NetHack 3.7.0 May 1, 2025 @@ -5944,6 +5944,13 @@ + getpos.door.prev + When asked for a location, the key to go to previous closest door or + doorway. Default is `D'. + + getpos.help + When asked for a location, the key to show help. Default is `?'. + getpos.mon.next When asked for a location, the key to go to next closest monster. Default is `m'. @@ -5991,13 +5998,6 @@ getpos.pick.quick When asked for a location, the key to choose the location, skip ask- ing for more info, and exit the location asking loop. Default is - `;'. - - getpos.pick.verbose - When asked for a location, the key to choose the location, and show - more info without asking. Default is `:'. - - NetHack 3.7.0 May 1, 2025 @@ -6010,6 +6010,12 @@ + `;'. + + getpos.pick.verbose + When asked for a location, the key to choose the location, and show + more info without asking. Default is `:'. + getpos.self When asked for a location, the key to go to your location. Default is `@'. @@ -6058,12 +6064,6 @@ Here's an example of message types using NetHack's internal pattern matching facility: - MSGTYPE=stop "You feel hungry." - MSGTYPE=hide "You displaced *." - - specifies that whenever a message "You feel hungry" is shown, the - user is prompted with more-prompt, and a message matching "You dis- - placed ." is not shown at all. NetHack 3.7.0 May 1, 2025 @@ -6076,6 +6076,13 @@ + MSGTYPE=stop "You feel hungry." + MSGTYPE=hide "You displaced *." + + specifies that whenever a message "You feel hungry" is shown, the + user is prompted with more-prompt, and a message matching "You dis- + placed ." is not shown at all. + The order of the defined MSGTYPE lines is important; the last match- ing rule is used. Put the general case first, exceptions below them. @@ -6122,13 +6129,6 @@ ple MENUCOLOR entries in your configuration file, and the last MENU- COLOR line that matches a menu line will be used for the line. - Note that if you intend to have one or more color specifications - match " uncursed ", you will probably want to turn the - implicit_uncursed option off so that all items known to be uncursed - are actually displayed with the "uncursed" description. - - - @@ -6142,6 +6142,11 @@ + Note that if you intend to have one or more color specifications + match " uncursed ", you will probably want to turn the + implicit_uncursed option off so that all items known to be uncursed + are actually displayed with the "uncursed" description. + 9.13. Configuring User Sounds Some platforms allow you to define sound files to be played when @@ -6191,11 +6196,6 @@ For example, the following line in your configuration file will cause the hitpoints field to display in the color red if your hit- - points drop to or below a threshold of 30%: - - OPTION=hilite_status:hitpoints/<=30%/red/normal - - (That example is actually specifying red&normal for <=30% and no- NetHack 3.7.0 May 1, 2025 @@ -6208,6 +6208,11 @@ + points drop to or below a threshold of 30%: + + OPTION=hilite_status:hitpoints/<=30%/red/normal + + (That example is actually specifying red&normal for <=30% and no- color&normal for >30%.) For another example, the following line in your configuration @@ -6256,12 +6261,7 @@ "experience", "time", and "score" are conditionally displayed depending upon your other option settings. - Instead of a behavior, "condition" takes the following condition - flags: stone, slime, strngl, foodpois, termill, blind, deaf, stun, - conf, hallu, lev, fly, and ride. You can use "major_troubles" as an - alias for stone through termill, "minor_troubles" for blind through - hallu, "movement" for lev, fly, and ride, and "all" for every condi- - tion. + NetHack 3.7.0 May 1, 2025 @@ -6274,6 +6274,13 @@ + Instead of a behavior, "condition" takes the following condition + flags: stone, slime, strngl, foodpois, termill, blind, deaf, stun, + conf, hallu, lev, fly, and ride. You can use "major_troubles" as an + alias for stone through termill, "minor_troubles" for blind through + hallu, "movement" for lev, fly, and ride, and "all" for every condi- + tion. + Allowed behaviors are "always", "up", "down", "changed", a percent- age or absolute number threshold, or text to match against. For the hitpoints field, the additional behavior "criticalhp" is available. @@ -6321,13 +6328,6 @@ instead, it also matches when value is below or above. If the prefix is `<' or `>', only match when strictly above or below. - * criticalhp only applies to the hitpoints field and only when - current hit points are below a threshold (which varies by maxi- - mum hit points and experience level). When the threshold is - met, a criticalhp rule takes precedence over all other hit- - points rules. - - NetHack 3.7.0 May 1, 2025 @@ -6340,6 +6340,12 @@ + * criticalhp only applies to the hitpoints field and only when + current hit points are below a threshold (which varies by maxi- + mum hit points and experience level). When the threshold is + met, a criticalhp rule takes precedence over all other hit- + points rules. + * text match sets the attribute when the field value matches the text. Text matches can only be used for "alignment", "carry- ing-capacity", "hunger", "dungeon-level", and "title". For @@ -6388,12 +6394,6 @@ following character literally. Thus \ needs to be represented as \\. The special prefix form \m switches on the meta bit in the symbol value, and the ^ prefix causes the following character to be treated - as a control character. - - NetHack Symbols - Symbol Name Description - ----------------------------------------------------------------- - NetHack 3.7.0 May 1, 2025 @@ -6406,6 +6406,11 @@ + as a control character. + + NetHack Symbols + Symbol Name Description + ----------------------------------------------------------------- S_air (air) _ S_altar (altar) " S_amulet (amulet) @@ -6454,11 +6459,6 @@ \ S_expl_tr (explosion top right) | S_expl_ml (explosion middle left) S_expl_mc (explosion middle center) - | S_expl_mr (explosion middle right) - \ S_expl_bl (explosion bottom left) - - S_expl_bc (explosion bottom center) - / S_expl_br (explosion bottom right) - e S_eye (eye or sphere) @@ -6472,6 +6472,11 @@ + | S_expl_mr (explosion middle right) + \ S_expl_bl (explosion bottom left) + - S_expl_bc (explosion bottom center) + / S_expl_br (explosion bottom right) + e S_eye (eye or sphere) ^ S_falling_rock_trap (falling rock trap) f S_feline (cat or other feline) ^ S_fire_trap (fire trap) @@ -6520,11 +6525,6 @@ N S_naga (naga) . S_ndoor (doorway without door) n S_nymph (nymph) - O S_ogre (ogre) - o S_orc (orc) - p S_piercer (piercer) - ^ S_pit (pit) - # S_poisoncloud (poison cloud) @@ -6538,6 +6538,11 @@ + O S_ogre (ogre) + o S_orc (orc) + p S_piercer (piercer) + ^ S_pit (pit) + # S_poisoncloud (poison cloud) ^ S_polymorph_trap (polymorph trap) } S_pool (water) ! S_potion (potion) @@ -6586,11 +6591,6 @@ # S_tree (tree) T S_troll (troll) | S_trwall (wall) - - S_tuwall (wall) - U S_umber (umber hulk) - S_unexplored (unexplored terrain) - u S_unicorn (unicorn or horse) - < S_upladder (ladder up) @@ -6604,6 +6604,11 @@ + - S_tuwall (wall) + U S_umber (umber hulk) + S_unexplored (unexplored terrain) + u S_unicorn (unicorn or horse) + < S_upladder (ladder up) < S_upstair (staircase up) V S_vampire (vampire) | S_vbeam (vertical beam [zap animation]) @@ -6652,12 +6657,7 @@ has a UTF8 handler within the symbols file such as the enhanced1 set, or individually within your nethack.rc file. - The format for defining a glyph representation is: - OPTIONS=glyph:glyphid/U+nnnn/R-G-B - - The window port that is active needs to provide support for dis- - playing UTF-8 character sequences and explicit red-green-blue colors NetHack 3.7.0 May 1, 2025 @@ -6670,6 +6670,12 @@ + The format for defining a glyph representation is: + + OPTIONS=glyph:glyphid/U+nnnn/R-G-B + + The window port that is active needs to provide support for dis- + playing UTF-8 character sequences and explicit red-green-blue colors in order for the glyph representation to be visible. For example, the following line in your configuration file will cause the glyph repre- sentation for glyphid G_pool to use Unicode codepoint U+224B and the @@ -6717,12 +6723,6 @@ The most crucial settings to make the game more accessible are: - symset:plain - Load a symbol set appropriate for use by blind players. - - menustyle:traditional - This will assist in the interface to speech synthesizers. - @@ -6736,6 +6736,12 @@ + symset:plain + Load a symbol set appropriate for use by blind players. + + menustyle:traditional + This will assist in the interface to speech synthesizers. + nomenu_overlay Show menus on a cleared screen and aligned to the left edge. @@ -6785,12 +6791,6 @@ your screen-reader reads those lines. The same information can be seen via the "#attributes" command. - showdamage - Give a message of damage taken and how many hit points are left. - - - - NetHack 3.7.0 May 1, 2025 @@ -6802,6 +6802,9 @@ + showdamage + Give a message of damage taken and how many hit points are left. + 9.18. Global Configuration for System Administrators If NetHack is compiled with the SYSCF option, a system adminis- @@ -6853,9 +6856,6 @@ ENTRYMAX = Maximum number of entries in the score file. - POINTSMIN = Minimum number of points to get an entry in the score - file. - NetHack 3.7.0 May 1, 2025 @@ -6868,6 +6868,9 @@ + POINTSMIN = Minimum number of points to get an entry in the score + file. + PERS_IS_UID = 0 or 1 to use user names or numeric userids, respec- tively, to identify unique people for the score file. @@ -6918,10 +6921,7 @@ internally inconsistent state, or if the #bugreport command is invoked. - 10. Scoring - NetHack maintains a list of the top scores or scorers on your - machine, depending on how it is set up. In the latter case, each NetHack 3.7.0 May 1, 2025 @@ -6934,6 +6934,10 @@ + 10. Scoring + + NetHack maintains a list of the top scores or scorers on your + machine, depending on how it is set up. In the latter case, each account on the machine can post only one non-winning score on this list. If you score higher than someone else on this list, or better your previous score, you will be inserted in the proper place under @@ -6984,10 +6988,6 @@ user name to be allowed to use debug mode; for others, the hero must be given a particular character name (but may be any role; there's no connection between "wizard mode" and the Wizard role). Attempting to - start a game in debug mode when not allowed or not available will - result in falling back to explore mode instead. - - NetHack 3.7.0 May 1, 2025 @@ -7000,6 +7000,9 @@ + start a game in debug mode when not allowed or not available will + result in falling back to explore mode instead. + 12. Credits The original hack game was modeled on the Berkeley UNIX rogue @@ -7051,9 +7054,6 @@ Kevin Darcy later joined the main NetHack Development Team to produce subsequent revisions of 3.0. - Olaf Seibert ported NetHack 2.3 and 3.0 to the Amiga. Norm - Meluch, Stephen Spackman and Pierre Martineau designed overlay code - for PC NetHack 3.0. Johnny Lee ported NetHack 3.0 to the Macintosh. NetHack 3.7.0 May 1, 2025 @@ -7066,6 +7066,9 @@ + Olaf Seibert ported NetHack 2.3 and 3.0 to the Amiga. Norm + Meluch, Stephen Spackman and Pierre Martineau designed overlay code + for PC NetHack 3.0. Johnny Lee ported NetHack 3.0 to the Macintosh. Along with various other Dungeoneers, they continued to enhance the PC, Macintosh, and Amiga ports through the later revisions of 3.0. @@ -7117,9 +7120,6 @@ Warwick Allison wrote a graphically displayed version of NetHack for the Atari where the tiny pictures were described as "icons" and were distinct for specific types of monsters and objects rather than - just their classes. He contributed them to the NetHack Development - Team which rechristened them "tiles", original usage which has subse- - quently been picked up by various other games. NetHack's tiles sup- NetHack 3.7.0 May 1, 2025 @@ -7132,6 +7132,9 @@ + just their classes. He contributed them to the NetHack Development + Team which rechristened them "tiles", original usage which has subse- + quently been picked up by various other games. NetHack's tiles sup- port was then implemented on other platforms (initially MS-DOS but eventually Windows, Qt, and X11 too). @@ -7183,9 +7186,6 @@ 1999's year 99 was followed by 2000's year 100. That got written out successfully but it unintentionally introduced an extra column in the file layout which prevented score entries from being read back in cor- - rectly, interfering with insertion of new high scores and with - retrieval of old character names to use for random ghost and statue - names in the current game.) NetHack 3.7.0 May 1, 2025 @@ -7198,6 +7198,10 @@ + rectly, interfering with insertion of new high scores and with + retrieval of old character names to use for random ghost and statue + names in the current game.) + The 3.3 NetHack Development Team, consisting of Michael Allison, Ken Arromdee, David Cohrs, Jessie Collet, Steve Creps, Kevin Darcy, Timo Hakulinen, Kevin Hugo, Steve Linhart, Ken Lorber, Dean Luick, Pat @@ -7248,10 +7252,6 @@ Janne Salmijarvi and Teemu Suikki maintained and enhanced the Amiga port of 3.4 after Janne Salmijarvi resurrected it for 3.3.1. - Christian "Marvin" Bressler maintained 3.4 for the Atari after he - resurrected it for 3.3.1. - - NetHack 3.7.0 May 1, 2025 @@ -7264,6 +7264,9 @@ + Christian "Marvin" Bressler maintained 3.4 for the Atari after he + resurrected it for 3.3.1. + The release of NetHack 3.4.3 in December 2003 marked the begin- ning of a long release hiatus. 3.4.3 proved to be a remarkably stable version that provided continued enjoyment by the community for more @@ -7315,9 +7318,6 @@ Michael Allison, David Cohrs, Bart House, Pasi Kallinen, Alex Kompel, Dion Nicolaas, Derek S. Ray and Yitzhak Sapir maintained the - port of NetHack 3.6 for Microsoft Windows. - - NetHack 3.7.0 May 1, 2025 @@ -7330,6 +7330,8 @@ + port of NetHack 3.6 for Microsoft Windows. + Pat Rankin attempted to keep the VMS port running for NetHack 3.6, hindered by limited access. Kevin Smolkowski has updated and tested it for the most recent version of OpenVMS (V8.4 as of this @@ -7384,8 +7386,6 @@ - - NetHack 3.7.0 May 1, 2025 From 38161f3e4a90d4ba771ef4b00e89f44aaad26b50 Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 6 Oct 2025 11:49:31 -0400 Subject: [PATCH 033/442] relocate customization application Resolves #1450 --- include/extern.h | 1 + include/flag.h | 1 + src/allmain.c | 2 ++ src/glyphs.c | 13 ++++++++++--- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/include/extern.h b/include/extern.h index 670fb77a8..d533e0d49 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1155,6 +1155,7 @@ extern void dump_glyphids(void); extern void clear_all_glyphmap_colors(void); extern void reset_customcolors(void); extern int glyph_to_cmap(int); +extern void maybe_shuffle_customizations(void); /* ### hack.c ### */ diff --git a/include/flag.h b/include/flag.h index bd6a5dc58..a566c04de 100644 --- a/include/flag.h +++ b/include/flag.h @@ -254,6 +254,7 @@ struct instance_flags { boolean remember_getpos; /* save getpos() positioning in do-again queue */ boolean sad_feeling; /* unseen pet is dying */ boolean showdamage; /* extra message reporting damage hero has taken */ + boolean pending_customizations; /* at least one custom. was specified */ xint8 debug_fuzzer; /* fuzz testing */ int at_midnight; /* only valid during end of game disclosure */ int at_night; /* also only valid during end of game disclosure */ diff --git a/src/allmain.c b/src/allmain.c index a1fad95c9..f6d1ee503 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -178,6 +178,8 @@ moveloop_core(void) #ifdef POSITIONBAR do_positionbar(); #endif + if (iflags.pending_customizations) + maybe_shuffle_customizations(); dobjsfree(); diff --git a/src/glyphs.c b/src/glyphs.c index c584e212f..d92bef9fc 100644 --- a/src/glyphs.c +++ b/src/glyphs.c @@ -568,15 +568,22 @@ apply_customizations( } } } - if (at_least_one) { - shuffle_customizations(); - } + iflags.pending_customizations = at_least_one; } /* Shuffle the customizations to match shuffled object descriptions, * so a red potion isn't displayed with a blue customization, and so on. */ +void +maybe_shuffle_customizations(void) +{ + if (iflags.pending_customizations) { + shuffle_customizations(); + iflags.pending_customizations = 0; + } +} + #if 0 staticfn void shuffle_customizations(void) From d75ffe4b3460fc2ccebf0d1bf8161d8fb355ff10 Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 9 Oct 2025 09:52:30 -0400 Subject: [PATCH 034/442] distinguish magical and auditory scares Github issue #1326 states: "https://github.com/NetHack/NetHack/commit/dc9fe0d8bc96e006c119fa183c9c9a7c4cde2f53 aims to nerf scrolls of scare monster a bit, making humans and uniques immune. But in actuality this change also affects all elves, and also makes them immune to musical instruments, including the objectively scary drums of earthquake. This is possibly unintentional (I don't see why elves would be immune to bugles but dwarves wouldn't), and in my experience (playing EvilHack which ports this commit) it makes elves really annoying, and seems to contradict the commit's message about getting "most of the effect in the early game when you're usually dealing with normal monsters" (elves are fairly common starting from around Sokoban). [...] Also the commit has a comment saying "humans aren't monsters" presumably referring to the scroll of scare monster, but read scrolls can still scare most @, or uniques for that matter." Resolves #1326 --- src/monmove.c | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/src/monmove.c b/src/monmove.c index 8bf665099..0b0b27dfb 100644 --- a/src/monmove.c +++ b/src/monmove.c @@ -241,22 +241,33 @@ boolean onscary(coordxy x, coordxy y, struct monst *mtmp) { struct engr *ep; + /* <0,0> is used by musical scaring; + * it doesn't care about scrolls or engravings or dungeon branch */ + boolean auditory_scare = (x == 0 && y == 0), + magical_scare = !auditory_scare; - /* creatures who are directly resistant to magical scaring: - * humans aren't monsters - * uniques have ascended their base monster instincts - * Rodney, lawful minions, Angels, the Riders, shopkeepers - * inside their own shop, priests inside their own temple */ + /* creatures who are directly resistant to any type of scaring: + * Rodney, lawful minions, Angels, the Riders */ if (mtmp->iswiz || is_lminion(mtmp) || mtmp->data == &mons[PM_ANGEL] - || is_rider(mtmp->data) - || mtmp->data->mlet == S_HUMAN || unique_corpstat(mtmp->data) - || (mtmp->isshk && inhishop(mtmp)) + || is_rider(mtmp->data)) + return FALSE; + + /* creatures who are directly resistant to magical scaring + * based on the mere presence of something at a location: + * humans etc. + * uniques have ascended their base monster instincts */ + if (magical_scare + && (mtmp->data->mlet == S_HUMAN || unique_corpstat(mtmp->data))) + return FALSE; + + /* creatues who resist scaring under particular circumstances: + * shopkeepers inside their own shop + * priests inside their own temple */ + if ((mtmp->isshk && inhishop(mtmp)) || (mtmp->ispriest && inhistemple(mtmp))) return FALSE; - /* <0,0> is used by musical scaring to check for the above; - * it doesn't care about scrolls or engravings or dungeon branch */ - if (x == 0 && y == 0) + if (auditory_scare) return TRUE; /* should this still be true for defiled/molochian altars? */ @@ -286,7 +297,7 @@ onscary(coordxy x, coordxy y, struct monst *mtmp) || (Displaced && mtmp->mux == x && mtmp->muy == y) || (ep->guardobjects && vobj_at(x, y))) && !(mtmp->isshk || mtmp->isgd || !mtmp->mcansee - || mtmp->mpeaceful || mtmp->data->mlet == S_HUMAN + || mtmp->mpeaceful || mtmp->data == &mons[PM_MINOTAUR] || Inhell || In_endgame(&u.uz))); } From 34d0e956eda0a94d11927769e77a1313d321e47b Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 13 Oct 2025 23:02:56 -0700 Subject: [PATCH 035/442] fix issue #1453 - throwing crackable object upward Issue reported by NullCGT: throwing a crackable item against the ceiling would cause it to vanish if it became [more] cracked but not yet fully shattered. Fixes #1453 --- doc/fixes3-7-0.txt | 2 ++ src/dothrow.c | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 15447eac8..901a756d4 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -2133,6 +2133,8 @@ mounted hero was able to deliver joust hits when trapped timer sanity check for melting ice gave false complaint about non-ice for frozen moat under open drawbridge mhitm_ad_phys() was not applying Half_physical_damage when hero was target +throwing crystal plate mail or helm of brilliance up against the ceiling could + result in the item being cracked and then vanishing Fixes to 3.7.0-x Platform and/or Interface Problems Exposed Via git Repository diff --git a/src/dothrow.c b/src/dothrow.c index df019f6e4..a7d4ffd68 100644 --- a/src/dothrow.c +++ b/src/dothrow.c @@ -1268,7 +1268,14 @@ toss_up(struct obj *obj, boolean hitsroof) if (breaktest(obj)) { pline("%s hits the %s.", Doname2(obj), ceiling(u.ux, u.uy)); breakmsg(obj, !Blind); - return breakobj(obj, u.ux, u.uy, TRUE, TRUE) ? FALSE : TRUE; + /* crackable armor will return True for breaktest() but will + usually return False for breakobj() */ + if (!breakobj(obj, u.ux, u.uy, TRUE, TRUE)) { + hitfloor(obj, FALSE); + gt.thrownobj = 0; + return TRUE; + } + return FALSE; } action = "hits"; } else { From 6f8c1127edb07f81f727647fbd88e69e0b72610a Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 13 Oct 2025 23:13:15 -0700 Subject: [PATCH 036/442] item action 'W' for armor In the context-sensitive menu when picking an item of armor from an inventory listing, distinguish between wear-this-armor from could- wear-this-armor-if-something-else-wasn't-already-worn-in-its-slot. --- include/extern.h | 2 ++ src/invent.c | 18 ++++++++++++-- src/worn.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 2 deletions(-) diff --git a/include/extern.h b/include/extern.h index d533e0d49..34d20129c 100644 --- a/include/extern.h +++ b/include/extern.h @@ -3866,6 +3866,8 @@ extern void setworn(struct obj *, long) NO_NNARGS; /* has tests for obj */ extern void setnotworn(struct obj *) NO_NNARGS; /* has tests for obj */ extern void allunworn(void); extern struct obj *wearmask_to_obj(long); +extern int wornmask_to_armcat(long); +extern long armcat_to_wornmask(int); extern long wearslot(struct obj *) NONNULLARG1; extern void check_wornmask_slots(void); extern void mon_set_minvis(struct monst *) NONNULLARG1; diff --git a/src/invent.c b/src/invent.c index ef5a78dea..12c3a109a 100644 --- a/src/invent.c +++ b/src/invent.c @@ -3444,8 +3444,22 @@ itemactions(struct obj *otmp) /* W: wear armor */ if (!already_worn) { - if (otmp->oclass == ARMOR_CLASS) - ia_addmenu(win, IA_WEAR_OBJ, 'W', "Wear this armor"); + if (otmp->oclass == ARMOR_CLASS) { + /* if 'otmp' is worn we skip 'W' (and show 'T' above instead); + if it isn't, we either show "W - wear this" if otmp's slot + isn't populated, or "W - [already wearing ]"; + for the latter, picking 'W' will fail but we don't want to + omit 'W' in this situation */ + long Wmask = armcat_to_wornmask(objects[otmp->otyp].oc_armcat); + struct obj *o = wearmask_to_obj(Wmask); + + if (!o) + Strcpy(buf, "Wear this armor"); + else + Sprintf(buf, "[already wearing %s]", an(armor_simple_name(o))); + + ia_addmenu(win, IA_WEAR_OBJ, 'W', buf); + } } /* x: Swap main and readied weapon */ diff --git a/src/worn.c b/src/worn.c index b7db2d94a..e3464d04a 100644 --- a/src/worn.c +++ b/src/worn.c @@ -205,6 +205,70 @@ wearmask_to_obj(long wornmask) return (struct obj *) 0; } +/* convert an armor wornmask to corresponding category */ +int +wornmask_to_armcat(long mask) +{ + int cat = 0; + + switch (mask & W_ARMOR) { + case W_ARM: + cat = ARM_SUIT; + break; + case W_ARMC: + cat = ARM_CLOAK; + break; + case W_ARMH: + cat = ARM_HELM; + break; + case W_ARMS: + cat = ARM_SHIELD; + break; + case W_ARMG: + cat = ARM_GLOVES; + break; + case W_ARMF: + cat = ARM_BOOTS; + break; + case W_ARMU: + cat = ARM_SHIRT; + break; + } + return cat; +} + +/* convert an armor category to corresponding wornmask */ +long +armcat_to_wornmask(int cat) +{ + long mask = 0L; + + switch (cat) { + case ARM_SUIT: + mask = W_ARM; + break; + case ARM_CLOAK: + mask = W_ARMC; + break; + case ARM_HELM: + mask = W_ARMH; + break; + case ARM_SHIELD: + mask = W_ARMS; + break; + case ARM_GLOVES: + mask = W_ARMG; + break; + case ARM_BOOTS: + mask = W_ARMF; + break; + case ARM_SHIRT: + mask = W_ARMU; + break; + } + return mask; +} + /* return a bitmask of the equipment slot(s) a given item might be worn in */ long wearslot(struct obj *obj) From 98b526eb152490fdbc78a11226335bb5663ff078 Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 14 Oct 2025 11:52:46 -0400 Subject: [PATCH 037/442] sync NetHack-3.6/doc/fixes36.7 -> NetHack-3.7/doc/fixes3-6-7.txt --- doc/fixes3-6-7.txt | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/doc/fixes3-6-7.txt b/doc/fixes3-6-7.txt index a00aab090..e34d9f1b4 100644 --- a/doc/fixes3-6-7.txt +++ b/doc/fixes3-6-7.txt @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.0 $ $NHDT-Date: 1676619775 2023/02/17 07:42:55 $ +$NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.21 $ $NHDT-Date: 1683746783 2023/05/10 19:26:23 $ fixes36.7 contains a summary of changes made to 3.6.6 in order to produce 3.6.7 as well as any post-release fixes in binaries. @@ -40,6 +40,32 @@ none Fixes to 3.6.7 Post-release Problems and other Post-release changes ------------------------------------------------------------------- -none - - +extend the fix for build failure w/ newer C library headers to macOS (pr #988) +Windows: fix range error detected by address sanitizer in plselInitDialog() +Windows: nethackw.exe was vertically spacing out menu items based on + the height of the tileset in use, even though the tiles in the + menu were drawn at the default height of 16 anyway; get rid of + the extraneous vertical spacing between the menu rows +hilite_pile can remain on the map after eating food off the floor +vms: update winprocs.h to ensure that CLR_MAX is defined; necessary for + compiling vmsmail.c which uses winprocs.h but not hack.h +curses: when #quitting, just before the high scores are about to be shown + status_window was NULL and dereferenced, so add checks (issue #1090) +unix: update Makefile.dat so that if parallel make is used, it will build + the data files "engrave", "epitaph", and "bogusmon" sequentially; + 'makedefs -s' builds all three at once and doing parallel instances + of that can produce seemingly mysterious problems by stomping on + each other's results +fix the mingw32 build of NetHack 3.6.7 by updating sys/winnt/Makefile.gcc, + sys/winnt/winnt.c and sys/winnt/stubs.c +correct the Ixoth tile +makedefs can produce individual bogusmon, engrave and epitaph files +drinkfountain() fate 24 to curse objects could end up cursing objects + on the floor chain instead of the intended inventory object chain +proceed with showpaths option even if the sysconf file is missing +avoid memory leak in mk_artifact(); cherry-pick of 3cca4f27 from 3.7 WIP +avoid using col in Guidebook build, since some distros no longer include + it, particularly some that use musl libc +back-port some nroff macro updates for Guidebook +prevent a crash when using 'O' to interactively set a text match highlight + for hunger From da20b839b5706a2751e6c433eab6a17b38af5f47 Mon Sep 17 00:00:00 2001 From: PatR Date: Wed, 15 Oct 2025 23:49:52 -0700 Subject: [PATCH 038/442] item actions for accessories Update item actions for rings, amulets, and eyewear. Make 'P' for an accessory that isn't worn behave similarly to recently modified 'W', and make 'R' for an accessory that is worn be more specific. --- src/invent.c | 42 +++++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/src/invent.c b/src/invent.c index 12c3a109a..ecca44250 100644 --- a/src/invent.c +++ b/src/invent.c @@ -3333,15 +3333,29 @@ itemactions(struct obj *otmp) /* P: put on accessory */ if (!already_worn) { - if (otmp->oclass == RING_CLASS || otmp->otyp == MEAT_RING) - ia_addmenu(win, IA_WEAR_OBJ, 'P', "Put this ring on"); - else if (otmp->oclass == AMULET_CLASS) - ia_addmenu(win, IA_WEAR_OBJ, 'P', "Put this amulet on"); - else if (otmp->otyp == TOWEL || otmp->otyp == BLINDFOLD) - ia_addmenu(win, IA_WEAR_OBJ, 'P', - "Use this to blindfold yourself"); - else if (otmp->otyp == LENSES) - ia_addmenu(win, IA_WEAR_OBJ, 'P', "Put these lenses on"); + /* if 'otmp' is worn, we'll skip 'P' and show 'R' below; + if not worn, we show 'P - Put on this ' if + the slot is available, or 'P - '; for the latter, + 'P' will fail but we don't want to omit the choice because + item actions can be used to learn commands */ + *buf = '\0'; + if (otmp->oclass == RING_CLASS || otmp->otyp == MEAT_RING) { + Strcpy(buf, (!uleft || !uright) ? "Put this ring on" + : "[both ring fingers in use]"); + } else if (otmp->oclass == AMULET_CLASS) { + Strcpy(buf, !uamul ? "Put this amulet on" + : "[already wearing an amulet]"); + } else if (otmp->otyp == TOWEL || otmp->otyp == BLINDFOLD + || otmp->otyp == LENSES) { + if (ublindf) + Strcpy(buf, "[already wearing eyewear]"); + else if (otmp->otyp == LENSES) + Strcpy(buf, "Put these lenses on"); + else + Strcpy(buf, "Use this to blindfold yourself"); + } + if (*buf) + ia_addmenu(win, IA_WEAR_OBJ, 'P', buf); } /* q: drink item */ @@ -3365,8 +3379,14 @@ itemactions(struct obj *otmp) ia_addmenu(win, IA_READ_OBJ, 'r', buf); /* R: remove accessory or rub item */ - if (otmp->owornmask & W_ACCESSORY) - ia_addmenu(win, IA_TAKEOFF_OBJ, 'R', "Remove this accessory"); + if (otmp->owornmask & W_ACCESSORY) { + Sprintf(buf, "Remove this %s", + (otmp->owornmask & W_RING) ? "ring" + : (otmp->owornmask & W_AMUL) ? "amulet" + : (otmp->owornmask & W_TOOL) ? "eyewear" + : "accessory"); /* catchall */ + ia_addmenu(win, IA_TAKEOFF_OBJ, 'R', buf); + } if (otmp->otyp == OIL_LAMP || otmp->otyp == MAGIC_LAMP || otmp->otyp == BRASS_LANTERN) { Sprintf(buf, "Rub this %s", simpleonames(otmp)); From a9f84bfe9af9a26cb0ef903f5a631c880c2bcab7 Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 18 Oct 2025 10:20:13 -0700 Subject: [PATCH 039/442] item action for towel Change the menu entry for putting on a towel to "Put this on to blindfold yourself" since "Use this ..." seems ambiguous. Also, for the 'P' and 'R' item actions, list amulets before rings like most other routines that can deal with both. --- src/invent.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/invent.c b/src/invent.c index ecca44250..507cd1c49 100644 --- a/src/invent.c +++ b/src/invent.c @@ -3339,20 +3339,21 @@ itemactions(struct obj *otmp) 'P' will fail but we don't want to omit the choice because item actions can be used to learn commands */ *buf = '\0'; - if (otmp->oclass == RING_CLASS || otmp->otyp == MEAT_RING) { - Strcpy(buf, (!uleft || !uright) ? "Put this ring on" - : "[both ring fingers in use]"); - } else if (otmp->oclass == AMULET_CLASS) { + if (otmp->oclass == AMULET_CLASS) { Strcpy(buf, !uamul ? "Put this amulet on" : "[already wearing an amulet]"); - } else if (otmp->otyp == TOWEL || otmp->otyp == BLINDFOLD + } else if (otmp->oclass == RING_CLASS || otmp->otyp == MEAT_RING) { + Strcpy(buf, (!uleft || !uright) ? "Put this ring on" + : "[both ring fingers in use]"); + } else if (otmp->otyp == BLINDFOLD || otmp->otyp == TOWEL || otmp->otyp == LENSES) { if (ublindf) Strcpy(buf, "[already wearing eyewear]"); else if (otmp->otyp == LENSES) Strcpy(buf, "Put these lenses on"); else - Strcpy(buf, "Use this to blindfold yourself"); + Sprintf(buf, "Put this on%s", + (otmp->otyp == TOWEL) ? " to blindfold yourself" : ""); } if (*buf) ia_addmenu(win, IA_WEAR_OBJ, 'P', buf); @@ -3381,10 +3382,10 @@ itemactions(struct obj *otmp) /* R: remove accessory or rub item */ if (otmp->owornmask & W_ACCESSORY) { Sprintf(buf, "Remove this %s", - (otmp->owornmask & W_RING) ? "ring" - : (otmp->owornmask & W_AMUL) ? "amulet" + (otmp->owornmask & W_AMUL) ? "amulet" + : (otmp->owornmask & W_RING) ? "ring" : (otmp->owornmask & W_TOOL) ? "eyewear" - : "accessory"); /* catchall */ + : "accessory"); /* catchall -- can't happen */ ia_addmenu(win, IA_TAKEOFF_OBJ, 'R', buf); } if (otmp->otyp == OIL_LAMP || otmp->otyp == MAGIC_LAMP From 966145a61d167d8cac0624e3a5b42026f02af07a Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 20 Oct 2025 13:29:42 -0700 Subject: [PATCH 040/442] item action 'T' against covered armor Using 'i'+menu choice for suit+'T' to try to take off a suit that is covered by a cloak (or shirt covered by suit and/or cloak) wouldn't do anything. It should report that you need to take off the outer garment first and then not take the chosen item off. There is probably a simpler fix. It took me a long time to figure where things were going wrong and them cobble this together. A big chunk of the diff for invent.c is just identation, surrounding a one-line change there. --- include/decl.h | 1 + include/extern.h | 1 + src/cmd.c | 3 +- src/decl.c | 1 + src/do_wear.c | 41 ++++++-- src/invent.c | 266 +++++++++++++++++++++++------------------------ 6 files changed, 168 insertions(+), 145 deletions(-) diff --git a/include/decl.h b/include/decl.h index 72f85eb90..8487ae6fc 100644 --- a/include/decl.h +++ b/include/decl.h @@ -475,6 +475,7 @@ struct instance_globals_i { /* invent.c */ char *invbuf; unsigned invbufsiz; + boolean item_action_in_progress; int in_sync_perminvent; /* mon.c */ diff --git a/include/extern.h b/include/extern.h index 34d20129c..247cc8e15 100644 --- a/include/extern.h +++ b/include/extern.h @@ -746,6 +746,7 @@ extern void Ring_gone(struct obj *) NONNULLARG1; extern void Blindf_on(struct obj *) NONNULLARG1; extern void Blindf_off(struct obj *); extern int dotakeoff(void); +extern int ia_dotakeoff(void); extern int doremring(void); extern int cursed(struct obj *); extern int armoroff(struct obj *); diff --git a/src/cmd.c b/src/cmd.c index 3eb8a634e..cd82e234d 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -2032,8 +2032,9 @@ struct ext_func_tab extcmdlist[] = { /* internal commands: only used by game core, not available for user */ { '\0', "clicklook", NULL, doclicklook, INTERNALCMD | MOUSECMD, NULL }, { '\0', "mouseaction", NULL, domouseaction, INTERNALCMD | MOUSECMD, NULL }, - { '\0', "altdip", NULL, dip_into, INTERNALCMD, NULL }, { '\0', "altadjust", NULL, adjust_split, INTERNALCMD, NULL }, + { '\0', "altdip", NULL, dip_into, INTERNALCMD, NULL }, + { '\0', "alttakeoff", NULL, ia_dotakeoff, INTERNALCMD, NULL }, { '\0', "altunwield", NULL, remarm_swapwep, INTERNALCMD, NULL }, { '\0', (char *) 0, (char *) 0, donull, 0, (char *) 0 } /* sentinel */ }; diff --git a/src/decl.c b/src/decl.c index a7e52dd80..04f55eb99 100644 --- a/src/decl.c +++ b/src/decl.c @@ -413,6 +413,7 @@ static const struct instance_globals_i g_init_i = { /* invent.c */ NULL, /* invbuf */ 0U, /* invbufsize */ + FALSE, /* item_action_in_progress */ 0, /* in_sync_perminvent */ /* mon.c */ NULL, /* itermonarr */ diff --git a/src/do_wear.c b/src/do_wear.c index 058d903d7..2cb86fa6d 100644 --- a/src/do_wear.c +++ b/src/do_wear.c @@ -1783,7 +1783,7 @@ armor_or_accessory_off(struct obj *obj) Strcat(what, " and "); Strcat(what, suit_simple_name(uarm)); } - Snprintf(why, sizeof(why), " without taking off your %s first", + Snprintf(why, sizeof why, " without taking off your %s first", what); } else { Strcpy(why, "; it's embedded"); @@ -1841,7 +1841,7 @@ dotakeoff(void) pline("Not wearing any armor or accessories."); return ECMD_OK; } - if (Narmorpieces != 1 || ParanoidRemove || cmdq_peek(CQ_CANNED)) + if (Narmorpieces != 1 || ParanoidRemove || gi.item_action_in_progress) otmp = getobj("take off", takeoff_ok, GETOBJ_NOFLAGS); if (!otmp) return ECMD_CANCEL; @@ -1849,6 +1849,21 @@ dotakeoff(void) return armor_or_accessory_off(otmp); } +/* 'i' or 'I[' followed by and then 'T'; + plain dotakeoff() would not give any feedback when picking suit + covered by cloak or shirt covered by suit and/or cloak due to the + default behavior of equip_ok() (skipping inaccessible items) */ +int +ia_dotakeoff(void) +{ + int res; + + gi.item_action_in_progress = TRUE; + res = dotakeoff(); + gi.item_action_in_progress = FALSE; + return res; +} + /* the #remove command - take off ring or other accessory */ int doremring(void) @@ -3256,14 +3271,14 @@ adj_abon(struct obj *otmp, schar delta) } /* decide whether a worn item is covered up by some other worn item, - used for dipping into liquid and applying grease; + used for dipping into liquid and applying grease and takeoff_ok(); some criteria are different than select_off()'s */ boolean -inaccessible_equipment(struct obj *obj, - const char *verb, /* "dip" or "grease", or null to - avoid messages */ - boolean only_if_known_cursed) /* ignore covering unless - known to be cursed */ +inaccessible_equipment( + struct obj *obj, + const char *verb, /* "dip" or "grease", or null to avoid messages */ + boolean only_if_known_cursed) /* ignore covering unless it is known to + * be cursed */ { static NEARDATA const char need_to_take_off_outer_armor[] = "need to take off %s to %s %s."; @@ -3315,6 +3330,8 @@ inaccessible_equipment(struct obj *obj, } /* item is not inaccessible */ return FALSE; + +#undef BLOCKSACCESS } /* not a getobj callback - unifies code among the other 4 getobj callbacks */ @@ -3354,9 +3371,11 @@ equip_ok(struct obj *obj, boolean removing, boolean accessory) * can't be worn because the slot is filled with something else. */ /* removing inaccessible equipment */ - if (removing && inaccessible_equipment(obj, (const char *) 0, - (obj->oclass == RING_CLASS))) - return GETOBJ_EXCLUDE_INACCESS; + if (removing && !gi.item_action_in_progress) { + if (inaccessible_equipment(obj, (const char *) 0, + (obj->oclass == RING_CLASS))) + return GETOBJ_EXCLUDE_INACCESS; + } /* all good to go */ return GETOBJ_SUGGEST; diff --git a/src/invent.c b/src/invent.c index 507cd1c49..913bc353f 100644 --- a/src/invent.c +++ b/src/invent.c @@ -2980,142 +2980,142 @@ ia_addmenu(winid win, int act, char let, const char *txt) ATR_NONE, clr, txt, MENU_ITEMFLAGS_NONE); } +/* set up a command to execute on a specific item next */ staticfn void itemactions_pushkeys(struct obj *otmp, int act) { - switch (act) { - default: - impossible("Unknown item action"); - break; - case IA_NONE: - break; - case IA_UNWIELD: - cmdq_add_ec(CQ_CANNED, (otmp == uwep) ? dowield - : (otmp == uswapwep) ? remarm_swapwep - : (otmp == uquiver) ? dowieldquiver - : donull); /* can't happen */ - cmdq_add_key(CQ_CANNED, '-'); - break; - case IA_APPLY_OBJ: - cmdq_add_ec(CQ_CANNED, doapply); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_DIP_OBJ: - /* #altdip instead of normal #dip - takes potion to dip into - first (the inventory item instigating this) and item to - be dipped second, also ignores floor features such as - fountain/sink so we don't need to force m-prefix here */ - cmdq_add_ec(CQ_CANNED, dip_into); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_NAME_OBJ: - case IA_NAME_OTYP: - cmdq_add_ec(CQ_CANNED, docallcmd); - cmdq_add_key(CQ_CANNED, (act == IA_NAME_OBJ) ? 'i' : 'o'); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_DROP_OBJ: - cmdq_add_ec(CQ_CANNED, dodrop); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_EAT_OBJ: - /* start with m-prefix; for #eat, it means ignore floor food - if present and eat food from invent */ - cmdq_add_ec(CQ_CANNED, do_reqmenu); - cmdq_add_ec(CQ_CANNED, doeat); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_ENGRAVE_OBJ: - cmdq_add_ec(CQ_CANNED, doengrave); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_FIRE_OBJ: - cmdq_add_ec(CQ_CANNED, dofire); - break; - case IA_ADJUST_OBJ: - cmdq_add_ec(CQ_CANNED, doorganize); /* #adjust */ - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_ADJUST_STACK: - cmdq_add_ec(CQ_CANNED, adjust_split); /* #altadjust */ - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_SACRIFICE: - cmdq_add_ec(CQ_CANNED, dosacrifice); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_BUY_OBJ: - cmdq_add_ec(CQ_CANNED, dopay); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_QUAFF_OBJ: - /* start with m-prefix; for #quaff, it means ignore fountain - or sink if present and drink a potion from invent */ - cmdq_add_ec(CQ_CANNED, do_reqmenu); - cmdq_add_ec(CQ_CANNED, dodrink); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_QUIVER_OBJ: - cmdq_add_ec(CQ_CANNED, dowieldquiver); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_READ_OBJ: - cmdq_add_ec(CQ_CANNED, doread); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_RUB_OBJ: - cmdq_add_ec(CQ_CANNED, dorub); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_THROW_OBJ: - cmdq_add_ec(CQ_CANNED, dothrow); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_TAKEOFF_OBJ: - cmdq_add_ec(CQ_CANNED, dotakeoff); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_TIP_CONTAINER: - /* start with m-prefix to skip floor containers; - for menustyle:Traditional when more than one floor - container is present, player will get a #tip menu and - have to pick the "tip something being carried" choice, - then this item will be already chosen from inventory; - suboptimal but possibly an acceptable tradeoff since - combining item actions with use of traditional ggetobj() - is an unlikely scenario */ - cmdq_add_ec(CQ_CANNED, do_reqmenu); - cmdq_add_ec(CQ_CANNED, dotip); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_INVOKE_OBJ: - cmdq_add_ec(CQ_CANNED, doinvoke); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_WIELD_OBJ: - cmdq_add_ec(CQ_CANNED, dowield); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_WEAR_OBJ: - cmdq_add_ec(CQ_CANNED, dowear); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_SWAPWEAPON: - cmdq_add_ec(CQ_CANNED, doswapweapon); - break; - case IA_TWOWEAPON: - cmdq_add_ec(CQ_CANNED, dotwoweapon); - break; - case IA_ZAP_OBJ: - cmdq_add_ec(CQ_CANNED, dozap); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_WHATIS_OBJ: - cmdq_add_ec(CQ_CANNED, dowhatis); /* "/" command */ - cmdq_add_key(CQ_CANNED, 'i'); /* "i" == item from inventory */ - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - } + switch (act) { + default: + impossible("Unknown item action %d", act); + break; + case IA_NONE: + break; + case IA_UNWIELD: + cmdq_add_ec(CQ_CANNED, (otmp == uwep) ? dowield + : (otmp == uswapwep) ? remarm_swapwep + : (otmp == uquiver) ? dowieldquiver + : donull); /* can't happen */ + cmdq_add_key(CQ_CANNED, HANDS_SYM); + break; + case IA_APPLY_OBJ: + cmdq_add_ec(CQ_CANNED, doapply); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_DIP_OBJ: + /* #altdip instead of normal #dip - takes potion to dip into + first (the inventory item instigating this) and item to + be dipped second, also ignores floor features such as + fountain/sink so we don't need to force m-prefix here */ + cmdq_add_ec(CQ_CANNED, dip_into); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_NAME_OBJ: + case IA_NAME_OTYP: + cmdq_add_ec(CQ_CANNED, docallcmd); + cmdq_add_key(CQ_CANNED, (act == IA_NAME_OBJ) ? 'i' : 'o'); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_DROP_OBJ: + cmdq_add_ec(CQ_CANNED, dodrop); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_EAT_OBJ: + /* start with m-prefix; for #eat, it means ignore floor food + if present and eat food from invent */ + cmdq_add_ec(CQ_CANNED, do_reqmenu); + cmdq_add_ec(CQ_CANNED, doeat); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_ENGRAVE_OBJ: + cmdq_add_ec(CQ_CANNED, doengrave); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_FIRE_OBJ: + cmdq_add_ec(CQ_CANNED, dofire); + break; + case IA_ADJUST_OBJ: + cmdq_add_ec(CQ_CANNED, doorganize); /* #adjust */ + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_ADJUST_STACK: + cmdq_add_ec(CQ_CANNED, adjust_split); /* #altadjust */ + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_SACRIFICE: + cmdq_add_ec(CQ_CANNED, dosacrifice); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_BUY_OBJ: + cmdq_add_ec(CQ_CANNED, dopay); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_QUAFF_OBJ: + /* start with m-prefix; for #quaff, it means ignore fountain + or sink if present and drink a potion from invent */ + cmdq_add_ec(CQ_CANNED, do_reqmenu); + cmdq_add_ec(CQ_CANNED, dodrink); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_QUIVER_OBJ: + cmdq_add_ec(CQ_CANNED, dowieldquiver); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_READ_OBJ: + cmdq_add_ec(CQ_CANNED, doread); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_RUB_OBJ: + cmdq_add_ec(CQ_CANNED, dorub); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_THROW_OBJ: + cmdq_add_ec(CQ_CANNED, dothrow); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_TAKEOFF_OBJ: + cmdq_add_ec(CQ_CANNED, ia_dotakeoff); /* #altdotakeoff */ + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_TIP_CONTAINER: + /* start with m-prefix to skip floor containers; + for menustyle:Traditional when more than one floor container + is present, player will get a #tip menu and have to pick + the "tip something being carried" choice, then this item + will be already chosen from inventory; suboptimal but + possibly an acceptable tradeoff since combining item actions + with use of traditional ggetobj() is an unlikely scenario */ + cmdq_add_ec(CQ_CANNED, do_reqmenu); + cmdq_add_ec(CQ_CANNED, dotip); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_INVOKE_OBJ: + cmdq_add_ec(CQ_CANNED, doinvoke); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_WIELD_OBJ: + cmdq_add_ec(CQ_CANNED, dowield); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_WEAR_OBJ: + cmdq_add_ec(CQ_CANNED, dowear); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_SWAPWEAPON: + cmdq_add_ec(CQ_CANNED, doswapweapon); + break; + case IA_TWOWEAPON: + cmdq_add_ec(CQ_CANNED, dotwoweapon); + break; + case IA_ZAP_OBJ: + cmdq_add_ec(CQ_CANNED, dozap); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_WHATIS_OBJ: + cmdq_add_ec(CQ_CANNED, dowhatis); /* "/" command */ + cmdq_add_key(CQ_CANNED, 'i'); /* "i" == item from inventory */ + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + } } /* Show menu of possible actions hero could do with item otmp */ From 7e3586acaddb92c70ea55c10d788375395c9aa7e Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 21 Oct 2025 14:06:16 -0700 Subject: [PATCH 041/442] ring item action bit for 'P' If the hero is in a form without fingers but is wearing two rings (put on before shape change), examining inventory and selecting a third ring shows an item action menu entry of "P - [both ring fingers in use]" (as of a couple of days ago). Change that to plug in appropriate body part for finger. --- src/invent.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/invent.c b/src/invent.c index 913bc353f..5d62567f2 100644 --- a/src/invent.c +++ b/src/invent.c @@ -3343,8 +3343,11 @@ itemactions(struct obj *otmp) Strcpy(buf, !uamul ? "Put this amulet on" : "[already wearing an amulet]"); } else if (otmp->oclass == RING_CLASS || otmp->otyp == MEAT_RING) { - Strcpy(buf, (!uleft || !uright) ? "Put this ring on" - : "[both ring fingers in use]"); + if (!uleft || !uright) + Strcpy(buf, "Put this ring on"); + else + Sprintf(buf, "[both ring %s in use]", + makeplural(body_part(FINGER))); } else if (otmp->otyp == BLINDFOLD || otmp->otyp == TOWEL || otmp->otyp == LENSES) { if (ublindf) From f636749ea2324c246dc652ac61de9e1975bbbbc9 Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 22 Oct 2025 12:52:10 -0400 Subject: [PATCH 042/442] suppress dozens of new compiler warnings with clang-21 Turn off default -Wdeprecated-octal-literals warning with clang-21 One example of many: In file included from objects.c:22: ../include/objects.h:137:1: warning: octal literals without a '0o' prefix are deprecated [-Wdeprecated-octal-literals] 137 | PROJECTILE("arrow", NoDes, | ^ ../include/objects.h:119:48: note: expanded from macro 'PROJECTILE' 119 | BITS(kn, 1, 1, 0, 0, 1, 0, 0, 0, 0, PIERCE, sub, metal), \ | ^ ../include/objclass.h:79:18: note: expanded from macro 'PIERCE' 79 | #define PIERCE 01 /* pointed weapon punctures target */ | ^ In file included from objects.c:22: ../include/objects.h:140:1: warning: octal literals without a '0o' prefix are deprecated [-Wdeprecated-octal-literals] 140 | PROJECTILE("elven arrow", "runed arrow", | ^ ../include/objects.h:119:48: note: expanded from macro 'PROJECTILE' 119 | BITS(kn, 1, 1, 0, 0, 1, 0, 0, 0, 0, PIERCE, sub, metal), \ | ^ ../include/objclass.h:79:18: note: expanded from macro 'PIERCE' 79 | #define PIERCE 01 /* pointed weapon punctures target */ | ^ In file included from objects.c:22: ../include/objects.h:143:1: warning: octal literals without a '0o' prefix are deprecated [-Wdeprecated-octal-literals] 143 | PROJECTILE("orcish arrow", "crude arrow", | ^ ../include/objects.h:119:48: note: expanded from macro 'PROJECTILE' 119 | BITS(kn, 1, 1, 0, 0, 1, 0, 0, 0, 0, PIERCE, sub, metal), \ | ^ ../include/objclass.h:79:18: note: expanded from macro 'PIERCE' 79 | #define PIERCE 01 /* pointed weapon punctures target */ | ^ --- sys/unix/hints/include/compiler.370 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sys/unix/hints/include/compiler.370 b/sys/unix/hints/include/compiler.370 index 5ef6f801e..b686636a2 100755 --- a/sys/unix/hints/include/compiler.370 +++ b/sys/unix/hints/include/compiler.370 @@ -98,6 +98,7 @@ CXX=clang++ -std=gnu++11 # clang-specific follows CLANGGTEQ12 := $(shell expr `$(CC) -dumpversion | cut -f1 -d.` \>= 12) CLANGGTEQ14 := $(shell expr `$(CC) -dumpversion | cut -f1 -d.` \>= 14) +CLANGGTEQ21 := $(shell expr `$(CC) -dumpversion | cut -f1 -d.` \>= 21) ifeq "$(CLANGGTEQ12)" "1" CFLAGS+=-Wimplicit-fallthrough endif @@ -109,6 +110,9 @@ else # older versions complain about things newer ones don't without this CFLAGS+=-Wno-missing-field-initializers endif +ifeq "$(CLANGGTEQ21)" "1" +CFLAGS+=-Wno-deprecated-octal-literals +endif # none endif # clang-specific ends here From 8b69a5aabb86484cac0a7e527953165a6dda4642 Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 23 Oct 2025 11:18:42 -0400 Subject: [PATCH 043/442] a few constants that don't need to be octal at all --- include/objclass.h | 6 +++--- src/restore.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/objclass.h b/include/objclass.h index 23c3a86f2..3faf84a16 100644 --- a/include/objclass.h +++ b/include/objclass.h @@ -76,9 +76,9 @@ struct objclass { #define IMMEDIATE 2 /* directional beam that doesn't ricochet */ #define RAY 3 /* beam that does bounce off walls */ /* overloaded oc_dir: strike mode bit mask for weapons and weptools */ -#define PIERCE 01 /* pointed weapon punctures target */ -#define SLASH 02 /* sharp weapon cuts target */ -#define WHACK 04 /* blunt weapon bashes target */ +#define PIERCE 1 /* pointed weapon punctures target */ +#define SLASH 2 /* sharp weapon cuts target */ +#define WHACK 4 /* blunt weapon bashes target */ Bitfield(oc_material, 5); /* one of obj_material_types */ schar oc_subtyp; diff --git a/src/restore.c b/src/restore.c index 59bc231ad..91ade76f2 100644 --- a/src/restore.c +++ b/src/restore.c @@ -1208,7 +1208,7 @@ getlev(NHFILE *nhfp, int pid, xint8 lev) them is different now than when the level was saved */ restore_cham(mtmp); /* give hiders a chance to hide before their next move */ - if (ghostly || (elapsed > 00 && elapsed > (long) rnd(10))) + if (ghostly || (elapsed > 0L && elapsed > (long) rnd(10))) hide_monst(mtmp); } #endif /* !SFCTOOL */ From 42a08f1a864e4a9d797abfd9be63197d135cb059 Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 24 Oct 2025 11:24:48 -0400 Subject: [PATCH 044/442] typo fixes in fixes3-7-0.txt doc/fixes3-7-0.txt:218:11 - Unknown word (redundate) fix: (redundant) doc/fixes3-7-0.txt:533:23 - Unknown word (adjustmens) fix: (adjustments) doc/fixes3-7-0.txt:718:2 - Unknown word (uniforn) fix: (uniform) doc/fixes3-7-0.txt:1240:57 - Unknown word (attibute) fix: (attribute) doc/fixes3-7-0.txt:1858:43 - Unknown word (foced) fix: (forced) doc/fixes3-7-0.txt:1948:7 - Unknown word (thorougnly) fix: (thoroughly) doc/fixes3-7-0.txt:2072:8 - Unknown word (interferring) fix:(interfering) --- doc/fixes3-7-0.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 901a756d4..c69b99421 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -215,7 +215,7 @@ if riding or levitating, hero could apply bullwhip downward to pull up things some monster code was checking whether pets or engulfers were eating green slime by checking for green slime corpse instead of glob change light radius of stack of candles to square root -could get redundate "mon hits other-mon" messages when mon wields an artifact +could get redundant "mon hits other-mon" messages when mon wields an artifact failed untrap while mounted that moved hero onto the trap would leave steed with stale coordinates, triggering warnings if 'sanity_check' is On when digging a pit results in it being filled by adjacent pool or lava, any @@ -530,7 +530,7 @@ innocuous items like scrolls or eucalyptus leaves did harm when falling on hero's head after being thrown upward fighter types who start out knowing all non-magic armor should not know cornuthaum and dunce cap -prediscovered weapons adjustmens: only knights and samurai know polearms; +prediscovered weapons adjustments: only knights and samurai know polearms; rangers know launchers (bows), ammo (arrows), and spears regardless of their race/species; likewise, rogues know all daggers if the move counter ever reaches 1000000000, end the game @@ -715,7 +715,7 @@ selection of random engravings, epitaphs, and hallucinatory monster names had follow longer than average lines are most likely to be chosen and ones which follow shorter than average lines are least likely; use same workaround as for rumors: pad the shortest lines; result isn't - uniforn distribution but is better (tradeoff vs size; see makedefs) + uniform distribution but is better (tradeoff vs size; see makedefs) make selection of random rumors, engravings, epitaphs, and hallucinatory monst names have uniform distribution by handling long lines specially when filling a special room with monsters, if one that can come in groups got @@ -1237,7 +1237,7 @@ restore the ability for trap creation via magic which creates pits to destroy allow #sit while flying over a squeaky board trap to trigger it weight of statues of wraiths and of monsters which never leave a corpse was 0 when a werecreature in human form attacked hero, it could transform to critter - despite hero having the Protection_from_shape_changers_attibute + despite hero having the Protection_from_shape_changers_attribute status highlighting for hit points didn't work as intended for up or down HP changes; 'up' rule was used for both, 'down' rule was ignored unhide an unseen water monster using a polymorph trap on land @@ -1855,7 +1855,7 @@ after the fix for zombie reviving near hero (which now interrupts hero's occupation), could get "revive panic" when eating a troll corpse if it revived on the same turn that eating the corpse would finish a wizard's starting equipment was supposed to include a random spellbook of - spell level 1 through 3 but it was being foced to be level 1 + spell level 1 through 3 but it was being forced to be level 1 earlier fix for prices of unpaid objects going away in persistent inventory display when hero bought something during itemized billing didn't work if paying for a used-up shop item--prices of any unpaid items vanished @@ -1945,7 +1945,7 @@ don't blame/credit hero for stinking cloud if dying quest nemesis releases one Lua selection operations 'subtract' and 'xor' didn't always work as intended accept 'true' or 'false' as value for des.object field 'trapped' special level loader didn't check for bad coordinates supplied by .lua - file thorougnly enough + file thoroughly enough hero might not be credited with "entered Mine Town" achievement for the town variations which treat the whole level as the town if that level gets entered via falling or level teleport @@ -2069,7 +2069,7 @@ restoring a save file could trigger impossible "rnd(0) attempted" if it tried a released version, it would crash due to divide by 0 error instead fix regression of a post-3.6 fix: if 2 Wizards of Yendor were in play and 1 escaped the dungeon, bookkeeping for current number of Wizards stayed - at 2, interferring with future Wizard behavior + at 2, interfering with future Wizard behavior sometimes a repeat count from the preceding command carried over to most recent one when using do-again (^A); if the most recent one was an extended command, the spurious repeat was for '#' From dbe5ebfa94858113b13368477be0d019280a23b3 Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 24 Oct 2025 11:46:10 -0400 Subject: [PATCH 045/442] Guidebook typo doc/Guidebook.mn:2928:54 - Unknown word (situtaion) fix: (situation) --- doc/Guidebook.mn | 2 +- doc/Guidebook.tex | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 1139806e5..73fa5d454 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -2925,7 +2925,7 @@ Some objects of subtle enchantment are difficult to identify without these. .pg A scroll whose label is known can be read even when the hero is blind. If a scroll has been discovered, it will be listed in inventory by type -rather than by label, but the label is known in that situtaion even though +rather than by label, but the label is known in that situation even though it isn't shown. .pg Many scrolls produce a different effect from usual if they are blessed or diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index f3d3cfc38..d24078ba8 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -3164,7 +3164,7 @@ Some objects of subtle enchantment are difficult to identify without these. %.pg A scroll whose label is known can be read even when the hero is blind. If a scroll has been discovered, it will be listed in inventory by type -rather than by label, but the label is known in that situtaion even though +rather than by label, but the label is known in that situation even though it isn't shown. %.pg From 3eed021a397b00a47a61c9d6100d4f6fd96770dd Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 24 Oct 2025 12:01:37 -0400 Subject: [PATCH 046/442] date stamp update --- README | 4 ++-- doc/Guidebook.mn | 2 +- doc/Guidebook.tex | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README b/README index 9a4923d03..e6bf764f8 100644 --- a/README +++ b/README @@ -110,8 +110,8 @@ Please read items (1), (2) and (3) BEFORE doing anything with your new code. Intel Pentium or better running Linux, BSDI Intel Pentium or better running Windows 10 or 11 - Intel-based, or Apple M1, M2, M3 Macs running - macOS 10.11 (El Capitan) to macOS 14 (Sonoma) + Intel-based, or Apple M1, M2, M3, M4, M5 Macs running + macOS 10.11 (El Capitan) to macOS 26 (Tahoe) (follow the instructions in sys/unix/NewInstall.unx) Intel 80386 or greater running MS-DOS with DPMI built via djgpp compiler (native or Linux-hosted cross-compiler) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 73fa5d454..95bd7be35 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -47,7 +47,7 @@ .ds f0 \*(vr .ds f1 \" empty .\"DO NOT REMOVE NH_DATESUB .ds f2 Date(%B %-d, %Y) -.ds f2 May 1, 2025 +.ds f2 October 24, 2025 . .\" A note on some special characters: .\" \(lq = left double quote diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index d24078ba8..4bd01c70c 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -48,7 +48,7 @@ \author{Original version - Eric S. Raymond\\ (Edited and expanded for 3.7.0 by Mike Stephenson and others)} %DO NOT REMOVE NH_DATESUB \date{Date(%B %-d, %Y)} -\date{May 1, 2025} +\date{October 24, 2025} \maketitle From 3c8628f5496fe710fadf74eeb221ac660aa93efd Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 24 Oct 2025 12:27:05 -0400 Subject: [PATCH 047/442] correct a few typos --- doc/options.txt | 2 +- doc/sound.txt | 4 ++-- sys/vms/Install.vms | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/options.txt b/doc/options.txt index a07b316cc..8cc665b7e 100644 --- a/doc/options.txt +++ b/doc/options.txt @@ -1,4 +1,4 @@ -The definitition for each OPTIONS= option resides in include/optlist.h as of +The definition for each OPTIONS= option resides in include/optlist.h as of February 2020 in 3.7 WIP. Boolean and compound options are combined into a single allopt[] array. diff --git a/doc/sound.txt b/doc/sound.txt index 2f90d3334..66ff2d0d0 100644 --- a/doc/sound.txt +++ b/doc/sound.txt @@ -87,7 +87,7 @@ There are 4 distinct types of sound sound_triggers used by NetHack. at the outset (ambience_begin), at the termination (ambience_end), and periodically in-between as needed - (ambience_upate), likely with a different + (ambience_update), likely with a different hero proximity value. SOUND_TRIGGER_VERBAL Invoked by the core when someone (or something) @@ -233,7 +233,7 @@ sound_play_usersound(char *filename, int32_t volume, int32_t usidx); sound_ambience(int32_t ambienceid, int32_t ambience_action, int32_t hero_proximity); -- NetHack will call this function when it wants a particular - ambience related sound played in order to provide atomosphere + ambience related sound played in order to provide atmosphere or flavor. -- The ambienceid is used to identify the particular ambience sound being sought. The abienceid identifiers are from the diff --git a/sys/vms/Install.vms b/sys/vms/Install.vms index 730eb47f3..5b0b7e184 100644 --- a/sys/vms/Install.vms +++ b/sys/vms/Install.vms @@ -76,7 +76,7 @@ used if symbols are set up to run them. Or if you have them but do not have symbols set up, you may edit spec_lev.com to have it run them. If neither of those situations applies, spec_lev.com will default to - copying pre-genearated versions of the appropriate files (dgn_lex.c, + copying pre-generated versions of the appropriate files (dgn_lex.c, lev_lex.c, dgn_yacc.c, lev_yacc.c, dgn_comp.h, and lev_comp.h) from [.sys.share] into [.util]*.c and [.include]*.h. From 62e4a0bc91afeb8d6a5632a89d24113b382f0f80 Mon Sep 17 00:00:00 2001 From: nhw_cron Date: Fri, 24 Oct 2025 12:24:08 -0400 Subject: [PATCH 048/442] This is cron-daily v1-Apr-1-2024. 005guidebook updated: doc/Guidebook.txt --- doc/Guidebook.txt | 236 +++++++++++++++++++++++----------------------- 1 file changed, 118 insertions(+), 118 deletions(-) diff --git a/doc/Guidebook.txt b/doc/Guidebook.txt index 573fccdef..be96fb67d 100644 --- a/doc/Guidebook.txt +++ b/doc/Guidebook.txt @@ -15,7 +15,7 @@ Original version - Eric S. Raymond (Edited and expanded for NetHack 3.7.0 by Mike Stephenson and others) - May 1, 2025 + October 24, 2025 @@ -126,7 +126,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -192,7 +192,7 @@ NetHack continues this fine tradition. Unlike text adventure games - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -258,7 +258,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -324,7 +324,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -390,7 +390,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -456,7 +456,7 @@ The number of turns elapsed so far, displayed if you have the - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -522,7 +522,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -588,7 +588,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -654,7 +654,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -720,7 +720,7 @@ instead. Only these one-step movement commands cause you to - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -786,7 +786,7 @@ ing . ^ is used as shorthand elsewhere in the - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -852,7 +852,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -918,7 +918,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -984,7 +984,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -1050,7 +1050,7 @@ at an adjacent "remembered, unseen monster" marker. - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -1116,7 +1116,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -1182,7 +1182,7 @@ (R)UNIX is a registered trademark of The Open Group. - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -1248,7 +1248,7 @@ menu_next_page, and menu_last_page keys (`^', `<', `>', `|' by - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -1314,7 +1314,7 @@ doesn't and give that name to the result, while splitting (count - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -1380,7 +1380,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -1446,7 +1446,7 @@ extinct. - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -1512,7 +1512,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -1578,7 +1578,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -1644,7 +1644,7 @@ right away.) Since using this command by accident can cause - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -1710,7 +1710,7 @@ Default key is `M-R'. - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -1776,7 +1776,7 @@ (worn blindfold or towel or lenses, lit lamp(s) and/or candle(s), - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -1842,7 +1842,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -1908,7 +1908,7 @@ line will show "(no travel path)" if your character does not know - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -1974,7 +1974,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -2040,7 +2040,7 @@ Set one or more intrinsic attributes. Autocompletes. Debug mode - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -2106,7 +2106,7 @@ "high"] bit), you can invoke many extended commands by meta-ing the - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -2172,7 +2172,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -2238,7 +2238,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -2304,7 +2304,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -2370,7 +2370,7 @@ them). Some monsters who can open doors can also use unlocking tools. - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -2436,7 +2436,7 @@ been nullified, giving access to whatever is beyond them. In the - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -2502,7 +2502,7 @@ play. - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -2568,7 +2568,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -2634,7 +2634,7 @@ attack, when guessing where an unseen monster is or when deliberately - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -2700,7 +2700,7 @@ noid_confirmation:attack option to require a response of "yes" - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -2766,7 +2766,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -2832,7 +2832,7 @@ are encumbered, one of the conditions Burdened, Stressed, Strained, - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -2898,7 +2898,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -2964,7 +2964,7 @@ `X' command to engage or disengage that. Only some types of charac- - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -3030,7 +3030,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -3096,7 +3096,7 @@ you'll be told that you feel more confident in your skills. At that - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -3162,7 +3162,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -3228,7 +3228,7 @@ what you eat." - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -3261,8 +3261,8 @@ A scroll whose label is known can be read even when the hero is blind. If a scroll has been discovered, it will be listed in inven- - tory by type rather than by label, but the label is known in that - situtaion even though it isn't shown. + tory by type rather than by label, but the label is known in that sit- + uation even though it isn't shown. Many scrolls produce a different effect from usual if they are blessed or cursed, or read while the hero is confused. @@ -3294,7 +3294,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -3360,7 +3360,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -3426,7 +3426,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -3492,7 +3492,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -3558,7 +3558,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -3624,7 +3624,7 @@ either. - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -3690,7 +3690,7 @@ with your hands and feet. - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -3756,7 +3756,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -3822,7 +3822,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -3888,7 +3888,7 @@ ning NetHack for the first time, you should find a default template - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -3954,7 +3954,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -4020,7 +4020,7 @@ begins; whatever follows will be common to all sections. Otherwise - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -4086,7 +4086,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -4152,7 +4152,7 @@ sign) to let NetHack know that the rest is intended as a file name. - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -4218,7 +4218,7 @@ This option controls what happens when you attempt the `f' (fire) - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -4284,7 +4284,7 @@ Name your starting cat (for example "catname:Morris"). Cannot be - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -4350,7 +4350,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -4416,7 +4416,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -4482,7 +4482,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -4548,7 +4548,7 @@ `b', and `c' keyboard shortcuts rather than the mnemonics `o', `i', - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -4614,7 +4614,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -4680,7 +4680,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -4746,7 +4746,7 @@ defaults to "full"), or it can be negated (which defaults to "sin- - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -4812,7 +4812,7 @@ A space separated list of specific situations where alternate - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -4878,7 +4878,7 @@ list entries to be added by their name and entries to be removed by - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -4944,7 +4944,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -5010,7 +5010,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -5076,7 +5076,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -5142,7 +5142,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -5208,7 +5208,7 @@ monster index; default; - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -5274,7 +5274,7 @@ to the screen. (Applies to "tty" and "curses" interfaces only; - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -5340,7 +5340,7 @@ a - in same area only - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -5406,7 +5406,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -5472,7 +5472,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -5538,7 +5538,7 @@ If NetHack can, it should display an opening splash screen when it - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -5604,7 +5604,7 @@ Use bold black instead of blue for black glyphs (TTY only). - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -5670,7 +5670,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -5736,7 +5736,7 @@ strokes that the operating system returns to NetHack to help compen- - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -5802,7 +5802,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -5868,7 +5868,7 @@ The menu control or accelerator keys can also be rebound via OPTIONS - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -5934,7 +5934,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -6000,7 +6000,7 @@ ing for more info, and exit the location asking loop. Default is - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -6066,7 +6066,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -6132,7 +6132,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -6198,7 +6198,7 @@ cause the hitpoints field to display in the color red if your hit- - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -6264,7 +6264,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -6330,7 +6330,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -6396,7 +6396,7 @@ value, and the ^ prefix causes the following character to be treated - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -6462,7 +6462,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -6528,7 +6528,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -6594,7 +6594,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -6660,7 +6660,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -6726,7 +6726,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -6792,7 +6792,7 @@ seen via the "#attributes" command. - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -6858,7 +6858,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -6924,7 +6924,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -6990,7 +6990,7 @@ connection between "wizard mode" and the Wizard role). Attempting to - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -7056,7 +7056,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -7122,7 +7122,7 @@ were distinct for specific types of monsters and objects rather than - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -7188,7 +7188,7 @@ file layout which prevented score entries from being read back in cor- - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -7254,7 +7254,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -7320,7 +7320,7 @@ Kompel, Dion Nicolaas, Derek S. Ray and Yitzhak Sapir maintained the - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -7386,7 +7386,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -7452,7 +7452,7 @@ sends a particularly intriguing modification to help out with the - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -7518,7 +7518,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -7584,7 +7584,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 @@ -7650,7 +7650,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 October 24, 2025 From 58beb25d1c5c4bbb9180642e350bb2d4b0dc0996 Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 24 Oct 2025 12:32:34 -0400 Subject: [PATCH 049/442] Cross-compiling doc typos --- Cross-compiling | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cross-compiling b/Cross-compiling index 38998a937..0eb9488a7 100644 --- a/Cross-compiling +++ b/Cross-compiling @@ -550,7 +550,7 @@ Cross-compiler url: https://github.com/bebbo/amiga-gcc brew install bash wget make lhasa gmp mpfr libmpc flex gettext \ texinfo gcc make autoconf - After installing the prerequite packages and the cross-compiler + After installing the prerequisite packages and the cross-compiler it was a straightforward build: git clone https://github.com/bebbo/amiga-gcc.git @@ -646,7 +646,7 @@ Cross-compiler url: https://emscripten.org/docs/getting_started/downloads.html For macOS, you will need to install Xcode, git, cmake, Python 3.5 or new (at time of this writing). - After installing the prerequite packages above, obtain the cross-compiler + After installing the prerequisite packages above, obtain the cross-compiler via git and build it from the directory of your choice using steps similar to these: From 987e3d93e02f2b99e2ea7a37f4f78e96f4c4bb59 Mon Sep 17 00:00:00 2001 From: nhkeni Date: Fri, 24 Oct 2025 16:34:35 -0400 Subject: [PATCH 050/442] Fix a typo going back to B news 2.11; 1986 (or earlier) according to tmac.n --- doc/mn.7 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/mn.7 b/doc/mn.7 index 1d40edc2c..827ed82a8 100644 --- a/doc/mn.7 +++ b/doc/mn.7 @@ -136,7 +136,7 @@ b num \- i used to embolden italics _bi \*Z mac \- \- print \*x in emboldened font 2, \*y after, \*z before bm num 1i,1i+1v \- height of bottom margin -_bt mac \- \- print pottom title +_bt mac \- \- print bottom title bt num .5i+1v \- bottom of footer to bottom of page _cf \*Z mac \- \- print contents of header line (double quotes around \*x, \*y before, \*z after) From 7ab563ffc9efd772b862a943e20060d69c67bf07 Mon Sep 17 00:00:00 2001 From: nhw_cron Date: Fri, 24 Oct 2025 17:24:08 -0400 Subject: [PATCH 051/442] This is cron-daily v1-Apr-1-2024. 005manpages updated: mn.txt --- doc/mn.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/mn.txt b/doc/mn.txt index 1c42c2a04..7623777fd 100644 --- a/doc/mn.txt +++ b/doc/mn.txt @@ -75,7 +75,7 @@ b num - i used to embolden italics .bi x y z mac - - print x in emboldened font 2, y after, z before bm num 1i,1i+1v - height of bottom margin -.bt mac - - print pottom title +.bt mac - - print bottom title bt num .5i+1v - bottom of footer to bottom of page .cf x y z mac - - print contents of header line (double quotes around x, y before, z after) From a03b2c763b0baab471405799c6027455f756a869 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 25 Oct 2025 09:57:04 -0400 Subject: [PATCH 052/442] documentation URL fix --- DEVEL/Developer.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEVEL/Developer.txt b/DEVEL/Developer.txt index 37fd69c59..2622ffbd6 100644 --- a/DEVEL/Developer.txt +++ b/DEVEL/Developer.txt @@ -74,7 +74,7 @@ A. If you have never set up git on this machine before: git config --global credential.helper osxkeychain Linux: (This will vary by distribution.) - cd /usr/share/doc/git/contrib/credentail/libsecret + cd /usr/share/doc/git/contrib/credential/libsecret sudo apt-get install libglib-2.0-dev libsecret-1-dev sudo make git config --global credential.helper `pwd`/git-credential-libsecret From b72d4af837c830aae3fe899383f67fcf8d981a36 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 25 Oct 2025 10:14:28 -0400 Subject: [PATCH 053/442] correct a few minor typos in sys --- sys/unix/unixmain.c | 2 +- sys/windows/build-nmake.txt | 2 +- sys/windows/windsys.c | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sys/unix/unixmain.c b/sys/unix/unixmain.c index e74cfc8c5..6fb14c417 100644 --- a/sys/unix/unixmain.c +++ b/sys/unix/unixmain.c @@ -623,7 +623,7 @@ early_options(int *argc_p, char ***argv_p, char **hackdir_p) /* * Both *argc_p and *argv_p account for the program name as (*argv_p)[0]; - * local argc and argv impicitly discard that (by starting 'ndx' at 1). + * local argc and argv implicitly discard that (by starting 'ndx' at 1). * argcheck() doesn't mind, prscore() (via scores_only()) does (for the * number of args it gets passed, not for the value of argv[0]). */ diff --git a/sys/windows/build-nmake.txt b/sys/windows/build-nmake.txt index 4735b480c..16c5fce52 100644 --- a/sys/windows/build-nmake.txt +++ b/sys/windows/build-nmake.txt @@ -62,7 +62,7 @@ Building Two different versions of NetHack will be built for Windows from the command line using the Makefile approach: A tty port utilizing the Win32 Console I/O subsystem Console, - and a curses interface in an executabled called NetHack.exe. + and a curses interface in an executable called NetHack.exe. NetHack A Win32 native port built on the Windows API Graphical NetHack, and graphical curses in an executable called NetHackW.exe. diff --git a/sys/windows/windsys.c b/sys/windows/windsys.c index 6be9b6407..3209d774f 100644 --- a/sys/windows/windsys.c +++ b/sys/windows/windsys.c @@ -827,9 +827,9 @@ get_executable_path(void) path_buffer[length] = '\0'; #endif - char *seperator = strrchr(path_buffer, PATH_SEPARATOR); - if (seperator) - *seperator = '\0'; + char *separator = strrchr(path_buffer, PATH_SEPARATOR); + if (separator) + *separator = '\0'; path_buffer_set = TRUE; return path_buffer; From 5c0137a6a54c13f1fc6557474fe1a445516d50de Mon Sep 17 00:00:00 2001 From: nhkeni Date: Sun, 26 Oct 2025 12:26:11 -0400 Subject: [PATCH 054/442] comment/documentation typos --- DEVEL/hooksdir/nhsub | 6 +++--- DEVEL/nhgitset.pl | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DEVEL/hooksdir/nhsub b/DEVEL/hooksdir/nhsub index 7aa183b65..b5b90704b 100644 --- a/DEVEL/hooksdir/nhsub +++ b/DEVEL/hooksdir/nhsub @@ -397,7 +397,7 @@ sub cmdparse { } if($opt{cmd} eq 'commit' && $opt{a} && $#in == -1){ # "git commit -a" does multiple things; we only care - # about modigied files. + # about modified files. #XXX this assumes $RS is set properly for Windows - need to check that my @x = split(/$::RS/,`git ls-files -m`); chomp(@x); @@ -661,7 +661,7 @@ is actually a directory, the program recurses into that directory tree. Not all files found are re-written; only those with the attribute NHSUBST (for inline substitutions) or NH_DATESUB (for template substitution) are considered; finally those with no substitution -variables or no changes due to subtitution variables are not +variables or no changes due to substitution variables are not re-written. Unless changed by the options, files that have not changed are not affected. @@ -711,7 +711,7 @@ or the changes will be overwritten with the current date.) =back -=head1 SUBSTITUTION VALRIABLES +=head1 SUBSTITUTION VARIABLES =over diff --git a/DEVEL/nhgitset.pl b/DEVEL/nhgitset.pl index b20b5c57c..2c4977895 100755 --- a/DEVEL/nhgitset.pl +++ b/DEVEL/nhgitset.pl @@ -43,7 +43,7 @@ BEGIN { # Set $is_sourcerepo while we're at it - same logic. { # Special case for running nhgitset against a different repo. - # Must preceed the normal case! + # Must precede the normal case! # NB: we use $DEVhooksdir later in the program $DEVhooksdir = ($0 =~ m!^(.*)$DS!)[0]; chomp($DEVhooksdir); From efe0b1197dc87c5cdf0d0ede142992f09524b419 Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 27 Oct 2025 11:55:20 -0400 Subject: [PATCH 055/442] update tested versions of Visual Studio 2025-10-27 --- sys/windows/Makefile.nmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index afb9108cc..2cd87415b 100644 --- a/sys/windows/Makefile.nmake +++ b/sys/windows/Makefile.nmake @@ -8,7 +8,7 @@ # MS Visual Studio Visual C++ compiler # # Visual Studio Compilers Tested: -# - Microsoft Visual Studio 2022 Community Edition v 17.14.15 +# - Microsoft Visual Studio 2022 Community Edition v 17.14.18 # #============================================================================== # This is used for building two distinct executables of NetHack: @@ -1179,7 +1179,7 @@ rc=Rc.exe # Recently tested versions: TESTEDVS2022 = 14.44.35217.0 # Other versions: -TESTEDVS2026 = 14.50.35503.0 +TESTEDVS2026 = 14.50.35717.0 VS20261ST = 1450000000 VS2026CUR = $(TESTEDVS2026:.=) From 177a5bcaf7a4008e7b842d0503cc022a22326716 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Sat, 1 Nov 2025 10:09:30 +0000 Subject: [PATCH 056/442] Wielded vegetables do no damage It makes sense that a vegetable would do less damage than a hard object would, as they're generally fairly soft, so it seems like a likely thing for players to try if they're *intentionally* trying to hit for zero damage (which could be useful in certain niche cases, e.g. to wake up a sleeping monster without damaging it). --- src/uhitm.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/uhitm.c b/src/uhitm.c index 26f3cfeac..8d2feb1fa 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -1316,6 +1316,13 @@ hmon_hitmon_misc_obj( hmd->get_dmg_bonus = FALSE; break; default: + if (objects[obj->otyp].oc_material == VEGGY) { + /* vegetables (and similar) do no damage, because they + aren't rigid enough */ + hmd->dmg = 0; + hmd->get_dmg_bonus = FALSE; + break; + } /* non-weapons can damage because of their weight */ /* (but not too much) */ hmd->dmg = (obj->owt + 99) / 100; From 317282c469a9762698caf0c32e80f49d98e471fa Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Sat, 1 Nov 2025 16:09:25 +0000 Subject: [PATCH 057/442] Paper objects (except books) also do no damage These are even flimsier than vegetables are. --- src/uhitm.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/uhitm.c b/src/uhitm.c index 8d2feb1fa..160d2e0a2 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -1316,9 +1316,12 @@ hmon_hitmon_misc_obj( hmd->get_dmg_bonus = FALSE; break; default: - if (objects[obj->otyp].oc_material == VEGGY) { + if ((objects[obj->otyp].oc_material == VEGGY || + objects[obj->otyp].oc_material == PAPER) && + obj->oclass != SPBOOK_CLASS) { /* vegetables (and similar) do no damage, because they - aren't rigid enough */ + aren't rigid enough; paper objects also do no damage, + except for books */ hmd->dmg = 0; hmd->get_dmg_bonus = FALSE; break; From db8e76ffd18f97e3570d1e3d3c73d0187a343989 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Sat, 1 Nov 2025 16:11:22 +0000 Subject: [PATCH 058/442] Changelog entry for 0-damage nonweapon changes --- doc/fixes3-7-0.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index c69b99421..cf035bf33 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1528,6 +1528,7 @@ a gas spore that was killed when an engulfer swallowed it produced an explosion hallucination can display objects on the map that have a description (for shuffling into play at game start) but no name; examining those might trigger a crash +some food and paper items do no damage when bashing Fixes to 3.7.0-x General Problems Exposed Via git Repository From 4e29db079b6ae75609c9580bae14dc283ee9f0e6 Mon Sep 17 00:00:00 2001 From: Hector Denis <14800242+horlogeislux@users.noreply.github.com> Date: Wed, 5 Nov 2025 18:20:55 +0100 Subject: [PATCH 059/442] Fix QTCXXFLAGS and WINLIB when compiling with WANT_WINT_QT6. The previous code made incorrect assumptions about Qt6's file hierarchy. Reuse automatic configuration (with pkg-config) for Qt5, and replace '5' by '6'. Also fix a typo in multiw-2.370. --- sys/unix/hints/include/multiw-2.370 | 2 +- sys/unix/hints/macOS.370 | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sys/unix/hints/include/multiw-2.370 b/sys/unix/hints/include/multiw-2.370 index c580063b6..e1cf1633d 100644 --- a/sys/unix/hints/include/multiw-2.370 +++ b/sys/unix/hints/include/multiw-2.370 @@ -15,7 +15,7 @@ # - WINOBJ0 #--- # User selections could be specified as combinations of any of the following: -# WIN_WANT_TTY=1, WIN_WANT_CURSES=1, WIN_WANT_QT=1, WIN_WANT_X11=1 +# WANT_WIN_TTY=1, WANT_WIN_CURSES=1, WANT_WIN_QT=1, WANT_WIN_X11=1 # The selections will all be linked into the same binary. # # Assuming you have the prerequisite packages mentioned above, you can diff --git a/sys/unix/hints/macOS.370 b/sys/unix/hints/macOS.370 index 13cb3afe1..31279a3cd 100755 --- a/sys/unix/hints/macOS.370 +++ b/sys/unix/hints/macOS.370 @@ -281,12 +281,12 @@ WINLIB += $(shell PKG_CONFIG_PATH=$(QTDIR)/lib/pkgconfig \ pkg-config Qt5Gui Qt5Widgets Qt5Multimedia --libs) endif # WANT_WIN_QT5 ifdef WANT_WIN_QT6 -QTCXXFLAGS += -std=c++17 -I $(QTDIR)/include -I $(QTDIR)/include/QtCore \ - -I $(QTDIR)/include/QtMultimedia +QTCXXFLAGS += $(sort $(shell PKG_CONFIG_PATH=$(QTDIR)/lib/pkgconfig \ + pkg-config Qt6Gui Qt6Widgets Qt6Multimedia --cflags)) MOC = moc MOCPATH = $(QTDIR)/share/qt/libexec/moc -WINLIB += -F $(QTDIR)/Frameworks -framework QtCore -framework QtGui \ - -framework QtWidgets -framework QtMultimedia +WINLIB += $(shell PKG_CONFIG_PATH=$(QTDIR)/lib/pkgconfig \ + pkg-config Qt6Gui Qt6Widgets Qt6Multimedia --libs) endif # WANT_WIN_QT6 VARDATND0 += nhtiles.bmp rip.xpm nhsplash.xpm # XXX if /Developer/qt exists and QTDIR not set, use that From 58c7d2fb3e3cda89f2ff162bb735af665ad594d1 Mon Sep 17 00:00:00 2001 From: Michael Allison Date: Wed, 5 Nov 2025 13:20:45 -0500 Subject: [PATCH 060/442] macOS QT6 doc tidbit --- sys/unix/NewInstall.unx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sys/unix/NewInstall.unx b/sys/unix/NewInstall.unx index 97e066df9..831b1b870 100644 --- a/sys/unix/NewInstall.unx +++ b/sys/unix/NewInstall.unx @@ -104,7 +104,8 @@ additional interfaces. See below. | | | | support in your build. | | | | | | | | | | One possible way: | -| | | | brew install Qt | +| | | | brew install pkg-config | +| | | | brew install Qt6 | |----------+-------+-----------------+--------------------------------------| | Linux | tty | WANT_WIN_TTY | | | (Ubuntu) | | | | From 7763f4c5b04e6d300f21277ca549008453038337 Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 6 Nov 2025 01:34:52 -0500 Subject: [PATCH 061/442] blank perm_invent after repeating spell cast add mechanics to ensure display_inventory() refreshes perm_invent as expected when update_inventory() is called from a repeat command. Resolves #1454 --- include/hack.h | 1 + src/invent.c | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/include/hack.h b/include/hack.h index 361b95cd3..12da18c62 100644 --- a/include/hack.h +++ b/include/hack.h @@ -799,6 +799,7 @@ struct sinfo { int in_role_selection; /* role/race/&c selection menus in progress */ int in_getlin; /* inside interface getlin routine */ int in_sanity_check; /* for impossible() during sanity checking */ + int in_update_inventory; /* inside update_inventory() function */ int config_error_ready; /* config_error_add is ready, available */ int beyond_savefile_load; /* set when past savefile loading */ int savefile_completed; /* savefile has completed writing */ diff --git a/src/invent.c b/src/invent.c index 5d62567f2..fbbbe9ab9 100644 --- a/src/invent.c +++ b/src/invent.c @@ -2703,10 +2703,12 @@ update_inventory(void) * attempt in the shop code handled it for unpaid items but not for * paying for used-up shop items; that follows a different code path.) */ + program_state.in_update_inventory = TRUE; save_suppress_price = iflags.suppress_price; iflags.suppress_price = 0; (*windowprocs.win_update_inventory)(0); iflags.suppress_price = save_suppress_price; + program_state.in_update_inventory = FALSE; } /* the #perminv command - call interface's persistent inventory routine */ @@ -4024,7 +4026,7 @@ display_inventory(const char *lets, boolean want_reply) { struct _cmd_queue *cmdq = cmdq_pop(); - if (cmdq) { + if (cmdq && !program_state.in_update_inventory) { if (cmdq->typ == CMDQ_KEY) { struct obj *otmp; From 6af5a906bc2896fb1a31da8da4f539f0615840a8 Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 6 Nov 2025 01:38:06 -0500 Subject: [PATCH 062/442] follow-up: program_state field isn't boolean --- src/invent.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/invent.c b/src/invent.c index fbbbe9ab9..86ce36880 100644 --- a/src/invent.c +++ b/src/invent.c @@ -2703,12 +2703,12 @@ update_inventory(void) * attempt in the shop code handled it for unpaid items but not for * paying for used-up shop items; that follows a different code path.) */ - program_state.in_update_inventory = TRUE; + program_state.in_update_inventory = 1; save_suppress_price = iflags.suppress_price; iflags.suppress_price = 0; (*windowprocs.win_update_inventory)(0); iflags.suppress_price = save_suppress_price; - program_state.in_update_inventory = FALSE; + program_state.in_update_inventory = 0; } /* the #perminv command - call interface's persistent inventory routine */ From 4574738c48c34af2aa9f05df8e28227ee82bef59 Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 6 Nov 2025 18:43:48 -0500 Subject: [PATCH 063/442] Revert "follow-up: program_state field isn't boolean" This reverts commit 6af5a906bc2896fb1a31da8da4f539f0615840a8. --- src/invent.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/invent.c b/src/invent.c index 86ce36880..fbbbe9ab9 100644 --- a/src/invent.c +++ b/src/invent.c @@ -2703,12 +2703,12 @@ update_inventory(void) * attempt in the shop code handled it for unpaid items but not for * paying for used-up shop items; that follows a different code path.) */ - program_state.in_update_inventory = 1; + program_state.in_update_inventory = TRUE; save_suppress_price = iflags.suppress_price; iflags.suppress_price = 0; (*windowprocs.win_update_inventory)(0); iflags.suppress_price = save_suppress_price; - program_state.in_update_inventory = 0; + program_state.in_update_inventory = FALSE; } /* the #perminv command - call interface's persistent inventory routine */ From 0f01260605c9393068e07a308840a9b6f8864aa9 Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 6 Nov 2025 18:44:12 -0500 Subject: [PATCH 064/442] Revert "blank perm_invent after repeating spell cast" This reverts commit 7763f4c5b04e6d300f21277ca549008453038337. --- include/hack.h | 1 - src/invent.c | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/include/hack.h b/include/hack.h index 12da18c62..361b95cd3 100644 --- a/include/hack.h +++ b/include/hack.h @@ -799,7 +799,6 @@ struct sinfo { int in_role_selection; /* role/race/&c selection menus in progress */ int in_getlin; /* inside interface getlin routine */ int in_sanity_check; /* for impossible() during sanity checking */ - int in_update_inventory; /* inside update_inventory() function */ int config_error_ready; /* config_error_add is ready, available */ int beyond_savefile_load; /* set when past savefile loading */ int savefile_completed; /* savefile has completed writing */ diff --git a/src/invent.c b/src/invent.c index fbbbe9ab9..5d62567f2 100644 --- a/src/invent.c +++ b/src/invent.c @@ -2703,12 +2703,10 @@ update_inventory(void) * attempt in the shop code handled it for unpaid items but not for * paying for used-up shop items; that follows a different code path.) */ - program_state.in_update_inventory = TRUE; save_suppress_price = iflags.suppress_price; iflags.suppress_price = 0; (*windowprocs.win_update_inventory)(0); iflags.suppress_price = save_suppress_price; - program_state.in_update_inventory = FALSE; } /* the #perminv command - call interface's persistent inventory routine */ @@ -4026,7 +4024,7 @@ display_inventory(const char *lets, boolean want_reply) { struct _cmd_queue *cmdq = cmdq_pop(); - if (cmdq && !program_state.in_update_inventory) { + if (cmdq) { if (cmdq->typ == CMDQ_KEY) { struct obj *otmp; From 82edcca4021a35226393a07b18b34d2d0cd77088 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Fri, 7 Nov 2025 18:21:21 +0000 Subject: [PATCH 065/442] Improve messages for oilskin sack protection versus water The existing messages made sense for brief dips into water, but didn't make sense when using an oilskin sack for an extended period underwater (and also assumed that the player was able to see the sack). This commit changes the message to make sense (and to be less spamy) if the hero enters water and remains there, and prevents oilskin sacks self-IDing if the hero is blind and thus can't see the water. --- doc/fixes3-7-0.txt | 1 + src/trap.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index cf035bf33..040600e43 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1529,6 +1529,7 @@ hallucination can display objects on the map that have a description (for shuffling into play at game start) but no name; examining those might trigger a crash some food and paper items do no damage when bashing +improved messages for oilskin sacks protecting from water damage Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/trap.c b/src/trap.c index 21adb2314..46c0e1266 100644 --- a/src/trap.c +++ b/src/trap.c @@ -4663,8 +4663,8 @@ water_damage( water_damage_chain(obj->cobj, FALSE); return ER_DAMAGED; /* contents were damaged */ } else if (Waterproof_container(obj)) { - if (in_invent) { - pline_The("%s slides right off your %s.", hliquid("water"), ostr); + if (in_invent && !Blind && !Underwater) { + pline_The("%s cannot get into your %s.", hliquid("water"), ostr); gm.mentioned_water = !Hallucination; makeknown(obj->otyp); /* if an oilskin sack, discover it; doesn't * matter for chest, large box, ice box */ From 3113387371a98e085782bfbba21dfc6a8d23767d Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 7 Nov 2025 13:03:46 -0800 Subject: [PATCH 066/442] fix #S14667 - livelog of genocide when hallucinating Uncursed genocide while hallucinating deliberately reports hero's role to the player as the affected target, but it was also showing that for livelog and #chronicle. Making the true target be visible for #chronicle gives away a little information but that should be inconsequential in this siutation since the player specifies the target. Not sure why this report got misclassified as spam. --- doc/fixes3-7-0.txt | 4 +++- src/read.c | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 040600e43..f8aa94516 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1,4 +1,4 @@ -NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1535 $ $NHDT-Date: 1740629713 2025/02/26 20:15:13 $ +NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1572 $ $NHDT-Date: 1762577372 2025/11/07 20:49:32 $ General Fixes and Modified Features ----------------------------------- @@ -2825,6 +2825,8 @@ archeologists' fedora is lucky telepathic hero can discern which particular monster just read a scroll add "make fetch-docs" to download pre-formatted documentation monsters will use throw-and-return weapons such as an aklys +uncursed genocide while hallucinating deliberately mis-reports hero's role as + target but was also inappropriately showing it to livelog/#chronicle Platform- and/or Interface-Specific New Features diff --git a/src/read.c b/src/read.c index 5ebe8ab64..2fd2c98ba 100644 --- a/src/read.c +++ b/src/read.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 read.c $NHDT-Date: 1715889745 2024/05/16 20:02:25 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.308 $ */ +/* NetHack 3.7 read.c $NHDT-Date: 1762577372 2025/11/07 20:49:32 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.323 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2755,7 +2755,7 @@ do_genocide( * 3 = forced genocide of player * 5 (4 | 1) = normal genocide from throne */ { - char buf[BUFSZ], promptbuf[QBUFSZ]; + char buf[BUFSZ], realbuf[BUFSZ], promptbuf[QBUFSZ]; int i, killplayer = 0; int mndx; struct permonst *ptr; @@ -2860,27 +2860,27 @@ do_genocide( } which = "all "; + Strcpy(buf, ptr->pmnames[NEUTRAL]); /* standard singular */ + if ((ptr->geno & G_UNIQ) && ptr != &mons[PM_HIGH_CLERIC]) + which = !type_is_pname(ptr) ? "the " : ""; + Strcpy(realbuf, buf); /* save true name, to avoid Hallu for livelog */ if (Hallucination) { - if (Upolyd) + if (Upolyd) { Strcpy(buf, pmname(gy.youmonst.data, flags.female ? FEMALE : MALE)); - else { + } else { Strcpy(buf, (flags.female && gu.urole.name.f) ? gu.urole.name.f : gu.urole.name.m); buf[0] = lowc(buf[0]); } - } else { - Strcpy(buf, ptr->pmnames[NEUTRAL]); /* standard singular */ - if ((ptr->geno & G_UNIQ) && ptr != &mons[PM_HIGH_CLERIC]) - which = !type_is_pname(ptr) ? "the " : ""; } if (how & REALLY) { if (!num_genocides()) livelog_printf(LL_CONDUCT | LL_GENOCIDE, "performed %s first genocide (%s)", - uhis(), makeplural(buf)); + uhis(), makeplural(realbuf)); else - livelog_printf(LL_GENOCIDE, "genocided %s", makeplural(buf)); + livelog_printf(LL_GENOCIDE, "genocided %s", makeplural(realbuf)); /* setting no-corpse affects wishing and random tin generation */ svm.mvitals[mndx].mvflags |= (G_GENOD | G_NOCORPSE); From 6fa324d52a4149b5dccbe4defa072feb6537190a Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 8 Nov 2025 01:06:13 -0800 Subject: [PATCH 067/442] more hallucinatory genocide Reorganize the recent livelog/#chronicle fix. --- src/read.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/read.c b/src/read.c index 2fd2c98ba..c98a8a725 100644 --- a/src/read.c +++ b/src/read.c @@ -2860,20 +2860,24 @@ do_genocide( } which = "all "; - Strcpy(buf, ptr->pmnames[NEUTRAL]); /* standard singular */ - if ((ptr->geno & G_UNIQ) && ptr != &mons[PM_HIGH_CLERIC]) - which = !type_is_pname(ptr) ? "the " : ""; - Strcpy(realbuf, buf); /* save true name, to avoid Hallu for livelog */ + Strcpy(realbuf, ptr->pmnames[NEUTRAL]); /* standard singular */ if (Hallucination) { + /* hallucinate hero's type */ if (Upolyd) { Strcpy(buf, pmname(gy.youmonst.data, flags.female ? FEMALE : MALE)); } else { Strcpy(buf, (flags.female && gu.urole.name.f) ? gu.urole.name.f - : gu.urole.name.m); + : gu.urole.name.m); buf[0] = lowc(buf[0]); } + } else { + /* use actual type */ + Strcpy(buf, realbuf); + if ((ptr->geno & G_UNIQ) && ptr != &mons[PM_HIGH_CLERIC]) + which = !type_is_pname(ptr) ? "the " : ""; } + if (how & REALLY) { if (!num_genocides()) livelog_printf(LL_CONDUCT | LL_GENOCIDE, @@ -2902,13 +2906,13 @@ do_genocide( } /* Polymorphed characters will die as soon as they're rehumanized. - */ - /* KMH -- Unchanging prevents rehumanization */ + KMH -- Unchanging prevents rehumanization. */ if (Upolyd && ptr != gy.youmonst.data) { delayed_killer(POLYMORPH, svk.killer.format, svk.killer.name); You_feel("%s inside.", udeadinside()); - } else + } else { done(GENOCIDED); + } } else if (ptr == gy.youmonst.data) { rehumanize(); } From d5658018ac8806c2a00c96f89d878c4834b07fa3 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 8 Nov 2025 14:26:07 -0500 Subject: [PATCH 068/442] alternative to display_inventory for window-port Several window ports that support perm_invent were using a call back to the core display_inventory() function. While calling from the window port back to core functions is arguably not ideal in the first place, it was recently brought to light that code NetHack-3.7 code changes to display_inventory() actually caused it to stop repopulating the perm_invent window as intended under certain circumstances. For now, provide an alternative function, repopulate_perminvent(), that hopefullshould still work the way it did previously. There will likely be some additional changes after this to further improve things, at some point. For now though, this Resolves #1454 --- doc/window.txt | 6 +++++- include/extern.h | 1 + src/invent.c | 7 +++++++ win/Qt/qt_bind.cpp | 2 +- win/X11/winX.c | 2 +- win/curses/cursinvt.c | 2 +- win/shim/winshim.c | 6 +++--- win/win32/mswproc.c | 4 ++-- 8 files changed, 21 insertions(+), 9 deletions(-) diff --git a/doc/window.txt b/doc/window.txt index 45593eb88..d2f55793a 100644 --- a/doc/window.txt +++ b/doc/window.txt @@ -284,7 +284,7 @@ update_inventory(arg) -- For an argument of 0: -- Indicate to the window port that the inventory has been changed. - -- Merely calls display_inventory() for window-ports + -- Merely calls repopulate_perminvent() for window-ports that leave the window up, otherwise empty. -- or for a non-zero argument: -- Prompts the user for a menu scrolling action and @@ -1001,6 +1001,10 @@ char display_inventory(lets, want_reply) -- Calls a start_menu()/add_menu()/select_menu() sequence. It returns the item selected, or '\0' if none is selected. Returns '\033' if the menu was canceled. +void repopulate_perminvent(void) + -- a minimal alternative to display_inventory() that + focuses only on repopulating the permanent inventory + window. raw_printf(str, ...) -- Like raw_print(), but accepts arguments like printf(). This routine processes the arguments and then calls raw_print(). diff --git a/include/extern.h b/include/extern.h index 247cc8e15..6fe005940 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1358,6 +1358,7 @@ extern void sync_perminvent(void); extern void perm_invent_toggled(boolean negated); extern void prepare_perminvent(winid window); extern struct obj *carrying_stoning_corpse(void); +extern void repopulate_perminvent(void); /* ### ioctl.c ### */ diff --git a/src/invent.c b/src/invent.c index 5d62567f2..37b23a87f 100644 --- a/src/invent.c +++ b/src/invent.c @@ -4047,6 +4047,13 @@ display_inventory(const char *lets, boolean want_reply) FALSE, want_reply, (long *) 0); } +void +repopulate_perminvent(void) +{ + (void) display_pickinv(NULL, (char *) 0, (char *) 0, + FALSE, FALSE, (long *) 0); +} + /* * Show what is current using inventory letters. * diff --git a/win/Qt/qt_bind.cpp b/win/Qt/qt_bind.cpp index 58dc96476..fd55061e2 100644 --- a/win/Qt/qt_bind.cpp +++ b/win/Qt/qt_bind.cpp @@ -502,7 +502,7 @@ void NetHackQtBind::qt_update_inventory(int arg UNUSED) /* doesn't work yet if (program_state.something_worth_saving && iflags.perm_invent) - display_inventory(NULL, false); + repopulate_perminvent(); */ } diff --git a/win/X11/winX.c b/win/X11/winX.c index 1021c6ce3..f74408269 100644 --- a/win/X11/winX.c +++ b/win/X11/winX.c @@ -1295,7 +1295,7 @@ X11_update_inventory(int arg) if (program_state.in_moveloop || program_state.gameover) { updated_inventory = 1; /* hack to avoid mapping&raising window */ if (!arg) { - (void) display_inventory((char *) 0, FALSE); + repopulate_perminvent(); } else { x11_scroll_perminv(arg); } diff --git a/win/curses/cursinvt.c b/win/curses/cursinvt.c index 4095ceb5a..1aadad9f9 100644 --- a/win/curses/cursinvt.c +++ b/win/curses/cursinvt.c @@ -96,7 +96,7 @@ curs_update_invt(int arg) /* ask core to display full inventory in a PICK_NONE menu; instead of setting up an ordinary menu, it will indirectly call curs_add_invt() for each line (including class headers) */ - display_inventory(NULL, FALSE); + repopulate_perminvent(); curs_invt_updated(win); } else { diff --git a/win/shim/winshim.c b/win/shim/winshim.c index fdbe18b9e..5e701c332 100644 --- a/win/shim/winshim.c +++ b/win/shim/winshim.c @@ -172,11 +172,11 @@ VDECLCB(shim_status_update, "vipiiip", A2P fldidx, P2V ptr, A2P chg, A2P percent, A2P color, P2V colormasks) #ifdef __EMSCRIPTEN__ -/* XXX: calling display_inventory() from shim_update_inventory() causes reentrancy that breaks emscripten Asyncify */ -/* this should be fine since according to windows.doc, the only purpose of shim_update_inventory() is to call display_inventory() */ +/* XXX: calling repopulate_perminvent() from shim_update_inventory() causes reentrancy that breaks emscripten Asyncify */ +/* this should be fine since according to windows.doc, the only purpose of shim_update_inventory() is to call repopulate_perminvent() */ void shim_update_inventory(int a1 UNUSED) { if(iflags.perm_invent) { - display_inventory(NULL, FALSE); + repopulate_perminvent(); } } diff --git a/win/win32/mswproc.c b/win/win32/mswproc.c index 06464c6de..abef776da 100644 --- a/win/win32/mswproc.c +++ b/win/win32/mswproc.c @@ -1248,7 +1248,7 @@ mswin_select_menu(winid wid, int how, MENU_ITEM_P **selected) /* -- Indicate to the window port that the inventory has been changed. - -- Merely calls display_inventory() for window-ports that leave the + -- Merely calls repopulate_perminvent() for window-ports that leave the window up, otherwise empty. */ void @@ -1257,7 +1257,7 @@ mswin_update_inventory(int arg) logDebug("mswin_update_inventory(%d)\n", arg); if (iflags.perm_invent && program_state.something_worth_saving && iflags.window_inited && WIN_INVEN != WIN_ERR) - display_inventory(NULL, FALSE); + repopulate_perminvent(); } win_request_info * From 4dfdeb8e92a0930ccc6aa20639f992fcdf8eb7c7 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 8 Nov 2025 14:53:50 -0500 Subject: [PATCH 069/442] follow-up in seldom-used places --- outdated/sys/amiga/winfuncs.c | 2 +- outdated/sys/wince/mswproc.c | 2 +- outdated/win/Qt3/qt3_win.cpp | 2 +- outdated/win/gnome/gnbind.c | 2 +- sys/libnh/libnhmain.c | 2 +- sys/unix/hints/include/cross-pre2.370 | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/outdated/sys/amiga/winfuncs.c b/outdated/sys/amiga/winfuncs.c index 28a811ba7..9907ce1c6 100644 --- a/outdated/sys/amiga/winfuncs.c +++ b/outdated/sys/amiga/winfuncs.c @@ -2098,7 +2098,7 @@ amii_update_inventory() if (WIN_INVEN != WIN_ERR && (cw = amii_wins[WIN_INVEN]) && cw->type == NHW_MENU && cw->win) { - display_inventory(NULL, FALSE); + repopulate_perminvent(); } } diff --git a/outdated/sys/wince/mswproc.c b/outdated/sys/wince/mswproc.c index 8011991bc..44106e616 100644 --- a/outdated/sys/wince/mswproc.c +++ b/outdated/sys/wince/mswproc.c @@ -1147,7 +1147,7 @@ mswin_select_menu(winid wid, int how, MENU_ITEM_P **selected) /* -- Indicate to the window port that the inventory has been changed. - -- Merely calls display_inventory() for window-ports that leave the + -- Merely calls repopulate_perminvent() for window-ports that leave the window up, otherwise empty. */ void diff --git a/outdated/win/Qt3/qt3_win.cpp b/outdated/win/Qt3/qt3_win.cpp index 26e46c4bb..9adde0fe9 100644 --- a/outdated/win/Qt3/qt3_win.cpp +++ b/outdated/win/Qt3/qt3_win.cpp @@ -4850,7 +4850,7 @@ void NetHackQtBind::qt_update_inventory() main->updateInventory(); /* doesn't work yet if (program_state.something_worth_saving && iflags.perm_invent) - display_inventory(NULL, FALSE); + repopulate_perminvent(); */ } diff --git a/outdated/win/gnome/gnbind.c b/outdated/win/gnome/gnbind.c index a298af5a0..9c193c625 100644 --- a/outdated/win/gnome/gnbind.c +++ b/outdated/win/gnome/gnbind.c @@ -786,7 +786,7 @@ gnome_select_menu(winid wid, int how, MENU_ITEM_P **selected) /* -- Indicate to the window port that the inventory has been changed. - -- Merely calls display_inventory() for window-ports that leave the + -- Merely calls repopulate_perminvent() for window-ports that leave the window up, otherwise empty. */ void diff --git a/sys/libnh/libnhmain.c b/sys/libnh/libnhmain.c index 562de1c78..44765987e 100644 --- a/sys/libnh/libnhmain.c +++ b/sys/libnh/libnhmain.c @@ -810,7 +810,7 @@ EM_JS(void, js_helpers_init, (), { // used by update_inventory function displayInventory() { // Asyncify.handleAsync(async () => { - return _display_inventory(0, 0); + return _repopulate_perminvent(); // }); } diff --git a/sys/unix/hints/include/cross-pre2.370 b/sys/unix/hints/include/cross-pre2.370 index df4ef6105..89434556a 100644 --- a/sys/unix/hints/include/cross-pre2.370 +++ b/sys/unix/hints/include/cross-pre2.370 @@ -275,7 +275,7 @@ EMCC_LFLAGS += -s ALLOW_TABLE_GROWTH EMCC_LFLAGS += -s ASYNCIFY -s ASYNCIFY_IMPORTS='["local_callback"]' EMCC_LFLAGS += -O3 EMCC_LFLAGS += -s MODULARIZE -EMCC_LFLAGS += -s EXPORTED_FUNCTIONS='["_main", "_shim_graphics_set_callback", "_display_inventory", "_malloc"]' +EMCC_LFLAGS += -s EXPORTED_FUNCTIONS='["_main", "_shim_graphics_set_callback", "_repopulate_perminvent", "_malloc"]' EMCC_LFLAGS += -s EXPORTED_RUNTIME_METHODS='["cwrap", "ccall", "addFunction", \ "removeFunction", "UTF8ToString", "stringToUTF8", "getValue", \ "setValue", "ENV", "FS", "IDBFS"]' From 33401b0d086a760dcda7e151851c47780438cc41 Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 8 Nov 2025 18:13:31 -0800 Subject: [PATCH 070/442] spell selection tweak For Z command, prevent an arbitrary number of reprompts when choosing which spell to cast under menustyle:Traditional. --- src/spell.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/spell.c b/src/spell.c index e3d7f2e12..03f5ee788 100644 --- a/src/spell.c +++ b/src/spell.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 spell.c $NHDT-Date: 1725227807 2024/09/01 21:56:47 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.173 $ */ +/* NetHack 3.7 spell.c $NHDT-Date: 1762680996 2025/11/09 01:36:36 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.179 $ */ /* Copyright (c) M. Stephenson 1988 */ /* NetHack may be freely redistributed. See license for details. */ @@ -713,11 +713,12 @@ rejectcasting(void) staticfn boolean getspell(int *spell_no) { - int nspells, idx; + int nspells, idx, retry_limit; char ilet, lets[BUFSZ], qbuf[QBUFSZ]; struct _cmd_queue cq, *cmdq; - if (spellid(0) == NO_SPELL) { + nspells = num_spells(); + if (!nspells) { You("don't know any spells right now."); return FALSE; } @@ -728,7 +729,6 @@ getspell(int *spell_no) cq = *cmdq; free(cmdq); if (cq.typ == CMDQ_KEY) { - nspells = num_spells(); idx = spell_let_to_idx(cq.key); if (idx < 0 || idx >= nspells) return FALSE; @@ -740,27 +740,33 @@ getspell(int *spell_no) } if (flags.menu_style == MENU_TRADITIONAL) { - /* we know there is at least 1 known spell */ - nspells = num_spells(); - + /* if we get here, we know there is at least 1 known spell */ if (nspells == 1) Strcpy(lets, "a"); else if (nspells < 27) Sprintf(lets, "a-%c", 'a' + nspells - 1); else if (nspells == 27) - Sprintf(lets, "a-zA"); + Strcpy(lets, "a-zA"); /* this assumes that there are at most 52 spells... */ else Sprintf(lets, "a-zA-%c", 'A' + nspells - 27); - for (;;) { - Snprintf(qbuf, sizeof(qbuf), "Cast which spell? [%s *?]", - lets); + Snprintf(qbuf, sizeof qbuf, "Cast which spell? [%s *?]", lets); + for (retry_limit = 0; ; ++retry_limit) { + if (retry_limit == 10) { + /* limit is mainly to prevent the fuzzer from getting stuck + since hangup should hit the 'quitchars' case; fuzzer + would too, but after an arbitrary number of attempts */ + pline("That's enough tries."); + return FALSE; + } ilet = yn_function(qbuf, (char *) 0, '\0', TRUE); if (ilet == '*' || ilet == '?') break; /* use menu mode */ - if (strchr(quitchars, ilet)) + if (strchr(quitchars, ilet)) { + pline1(Never_mind); return FALSE; + } idx = spell_let_to_idx(ilet); if (idx < 0 || idx >= nspells) { From f7e12d2801047d18efe73a009ba9216e15bc6ce8 Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 8 Nov 2025 18:38:55 -0800 Subject: [PATCH 071/442] do-again vs yn_function() This fixes the impossible from yn_function() for ^A after Z. One call to yn_function stored the spell letter for do-again and then another call was unintentionally using that when getting a y/n response for askchain() while using menustyle:Traditional [when spell was identify and eligible objects needed confirmation about whether to be ID'd]. Fixing that seemed to break #pray so the paranoid_confirm routine has been changed to not rely on canned input, even for queries where the player hasn't specified that confirmation be required. Behavior of ^A might be different in unexpected ways, but it wasn't working correctly before. --- doc/fixes3-7-0.txt | 5 ++++- src/cmd.c | 12 +++++++----- src/invent.c | 9 ++++++--- src/pray.c | 6 +++--- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index f8aa94516..2fec8e6ea 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1,4 +1,4 @@ -NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1572 $ $NHDT-Date: 1762577372 2025/11/07 20:49:32 $ +NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1573 $ $NHDT-Date: 1762680996 2025/11/09 01:36:36 $ General Fixes and Modified Features ----------------------------------- @@ -2137,6 +2137,9 @@ timer sanity check for melting ice gave false complaint about non-ice for mhitm_ad_phys() was not applying Half_physical_damage when hero was target throwing crystal plate mail or helm of brilliance up against the ceiling could result in the item being cracked and then vanishing +yn_function (used all over the place) sometimes triggered an impossible() + during do-again (^A) processing [ongoing revisions; more are probably + needed; listed here in case ^A behavior changes] Fixes to 3.7.0-x Platform and/or Interface Problems Exposed Via git Repository diff --git a/src/cmd.c b/src/cmd.c index cd82e234d..780bca6c3 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 cmd.c $NHDT-Date: 1736401574 2025/01/08 21:46:14 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.744 $ */ +/* NetHack 3.7 cmd.c $NHDT-Date: 1762680996 2025/11/09 01:36:36 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.755 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -5260,7 +5260,7 @@ yn_function( query = qbuf; } - if ((cmdq = cmdq_pop()) != 0) { + if (addcmdq && (cmdq = cmdq_pop()) != 0) { cq = *cmdq; free(cmdq); } else { @@ -5323,7 +5323,7 @@ yn_function( it is most likely caused by saving a keystroke that was just used to answer a context-sensitive prompt, then using the do-again command with context that has changed */ - if (resp && res && !strchr(resp, res)) { + if (resp && *resp && res && !strchr(resp, res)) { /* this probably needs refinement since caller is expecting something within 'resp' and ESC won't be (it could be present, but as a flag for unshown possibilities rather than as acceptable input) */ @@ -5405,9 +5405,11 @@ paranoid_ynq( /* for empty input, return value c will already be 'n' */ } while (ParanoidConfirm && strcmpi(ans, "no") && --trylimit); } else if (accept_q) { - c = ynq(prompt); /* 'y', 'n', or 'q' */ + /* 'y', 'n', or 'q' */ + c = yn_function(prompt, ynqchars, 'n', FALSE); } else { - c = y_n(prompt); /* 'y' or 'n' */ + /* 'y' or 'n' */ + c = yn_function(prompt, ynchars, 'n', FALSE); } if (c != 'y' && (c != 'q' || !accept_q)) c = 'n'; diff --git a/src/invent.c b/src/invent.c index 37b23a87f..bd8c689c9 100644 --- a/src/invent.c +++ b/src/invent.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 invent.c $NHDT-Date: 1737384766 2025/01/20 06:52:46 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.531 $ */ +/* NetHack 3.7 invent.c $NHDT-Date: 1762680996 2025/11/09 01:36:36 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.543 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2445,8 +2445,11 @@ askchain( ininv ? safeq_xprname : doname, ininv ? safeq_shortxprname : ansimpleoname, "item"); - sym = (takeoff || ident || otmp->quan < 2L) ? nyaq(qbuf) - : nyNaq(qbuf); + /* nyaq(qbuf) or nyNaq(qbuf), bypassing canned input for ^A */ + sym = yn_function(qbuf, + (takeoff || ident || otmp->quan < 2L) + ? ynaqchars : ynNaqchars, + 'n', FALSE); } else sym = 'y'; diff --git a/src/pray.c b/src/pray.c index 6250505ee..5f00487d1 100644 --- a/src/pray.c +++ b/src/pray.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 pray.c $NHDT-Date: 1727250729 2024/09/25 07:52:09 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.220 $ */ +/* NetHack 3.7 pray.c $NHDT-Date: 1762680996 2025/11/09 01:36:36 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.244 $ */ /* Copyright (c) Benson I. Margulies, Mike Stephenson, Steve Linhart, 1989. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2207,12 +2207,12 @@ dopray(void) if (ParanoidPray) { ok = paranoid_query(ParanoidConfirm, "Are you sure you want to pray?"); - +#if 0 /* clear command recall buffer; otherwise ^A to repeat p(ray) would do so without confirmation (if 'ok') or do nothing (if '!ok') */ cmdq_clear(CQ_REPEAT); cmdq_add_ec(CQ_REPEAT, dopray); - +#endif if (!ok) /* declined the "are you sure?" confirmation */ return ECMD_OK; } From 0524ff482b8331346c73a6b4c118fabb410f6d5e Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 9 Nov 2025 06:33:19 -0800 Subject: [PATCH 072/442] fix #S14678 - livelog of bribery when hallucinat If hallucinating hero bribes a demon lord, report its true identity for livelog/#chronicle. Unlike with the similar change for genocide, this does give away information if the player checks #chronicle. Again, the report via the web contact form was misclassified as spam. --- doc/fixes3-7-0.txt | 4 +++- src/minion.c | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 2fec8e6ea..a52b4ac9d 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1,4 +1,4 @@ -NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1573 $ $NHDT-Date: 1762680996 2025/11/09 01:36:36 $ +NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1574 $ $NHDT-Date: 1762727599 2025/11/09 14:33:19 $ General Fixes and Modified Features ----------------------------------- @@ -2830,6 +2830,8 @@ add "make fetch-docs" to download pre-formatted documentation monsters will use throw-and-return weapons such as an aklys uncursed genocide while hallucinating deliberately mis-reports hero's role as target but was also inappropriately showing it to livelog/#chronicle +livelog/#chronicle for bribing a demon lord reported random monster and + currency if hero was hallucinating Platform- and/or Interface-Specific New Features diff --git a/src/minion.c b/src/minion.c index 6acd508a7..085a51643 100644 --- a/src/minion.c +++ b/src/minion.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 minion.c $NHDT-Date: 1624322864 2021/06/22 00:47:44 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.60 $ */ +/* NetHack 3.7 minion.c $NHDT-Date: 1762727599 2025/11/09 14:33:19 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.81 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2008. */ /* NetHack may be freely redistributed. See license for details. */ @@ -346,8 +346,12 @@ demon_talk(struct monst *mtmp) return 0; } } + /* if 'mtmp' is unrecognizable due to hero's hallucination, + #chronicle will reveal its true identity -- just live with that; + also, avoid random hallucinatory currency() units */ livelog_printf(LL_UMONST, "bribed %s with %ld %s for safe passage", - Amonnam(mtmp), offer, currency(offer)); + x_monnam(mtmp, ARTICLE_A, (char *) 0, EXACT_NAME, FALSE), + offer, (offer == 1L) ? "zorkmid" : "zorkmids"); mongone(mtmp); return 1; } From 9b1bb1150e7feb0a9bd27dc0beefc96035964a5d Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 9 Nov 2025 12:58:19 -0800 Subject: [PATCH 073/442] fix issue #1458 - inappropriate verbal messages Issue reported by chappg: succubi could produce "it's on the house" (quoted verbal message) when hero is deaf. The mail daemon could produce a variety of verbal message when hero is deaf. The succubus/incubus one is easy to fix. The mail daemon ones are untested and a couple haven't been given non-verbal alternatives. Fixes #1458 --- doc/fixes3-7-0.txt | 4 +++- src/mail.c | 42 +++++++++++++++++++++++++++++------------- src/mhitu.c | 10 +++++++--- 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index a52b4ac9d..8954f3c00 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1,4 +1,4 @@ -NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1574 $ $NHDT-Date: 1762727599 2025/11/09 14:33:19 $ +NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1575 $ $NHDT-Date: 1762750699 2025/11/09 20:58:19 $ General Fixes and Modified Features ----------------------------------- @@ -1530,6 +1530,8 @@ hallucination can display objects on the map that have a description (for trigger a crash some food and paper items do no damage when bashing improved messages for oilskin sacks protecting from water damage +some messages by amorous demons and mail daemon were delivered as verbal ones + even when the hero is deaf Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/mail.c b/src/mail.c index e6a5c2935..25572f029 100644 --- a/src/mail.c +++ b/src/mail.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mail.c $NHDT-Date: 1596498174 2020/08/03 23:42:54 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.47 $ */ +/* NetHack 3.7 mail.c $NHDT-Date: 1762750699 2025/11/09 20:58:19 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.77 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Pasi Kallinen, 2018. */ /* NetHack may be freely redistributed. See license for details. */ @@ -333,18 +333,21 @@ md_rush(struct monst *md, if (fx == tx && fy == ty) break; - SetVoice(md, 0, 80, 0); - if ((mon = m_at(fx, fy)) != 0) /* save monster at this position */ - verbalize1(md_exclamations()); - else if (u_at(fx, fy)) - verbalize("Excuse me."); + mon = m_at(fx, fy); /* save monster at this position */ + if (!Deaf) { + SetVoice(md, 0, 80, 0); + if (mon) + verbalize1(md_exclamations()); + else if (u_at(fx, fy)) + verbalize("Excuse me."); + } if (mon) remove_monster(fx, fy); place_monster(md, fx, fy); /* put md down */ newsym(fx, fy); /* see it */ flush_screen(0); /* make sure md shows up */ - nh_delay_output(); /* wait a little bit */ + nh_delay_output(); /* wait a little bit */ /* Remove md from the dungeon. Restore original mon, if necessary. */ remove_monster(fx, fy); @@ -365,8 +368,12 @@ md_rush(struct monst *md, remove_monster(fx, fy); place_monster(md, fx, fy); /* display md with text below */ newsym(fx, fy); - SetVoice(md, 0, 80, 0); - verbalize("This place's too crowded. I'm outta here."); + if (!Deaf) { + SetVoice(md, 0, 80, 0); + verbalize("This place's too crowded. I'm outta here."); + } else { + pline1(Never_mind); + } remove_monster(fx, fy); if ((mon->mx != fx) || (mon->my != fy)) /* put mon back */ @@ -406,8 +413,12 @@ newmail(struct mail_info *info) goto go_back; message_seen = TRUE; - SetVoice(md, 0, 80, 0); - verbalize("%s, %s! %s.", Hello(md), svp.plname, info->display_txt); + if (!Deaf) { + SetVoice(md, 0, 80, 0); + verbalize("%s, %s! %s.", Hello(md), svp.plname, info->display_txt); + } else { + pline("Message: %s.", info->display_txt); + } if (info->message_typ) { struct obj *obj = mksobj(SCR_MAIL, FALSE, FALSE); @@ -418,8 +429,13 @@ newmail(struct mail_info *info) new_omailcmd(obj, info->response_cmd); if (!m_next2u(md)) { - SetVoice(md, 0, 80, 0); - verbalize("Catch!"); + if (!Deaf) { + SetVoice(md, 0, 80, 0); + verbalize("Catch!"); + } else { + /* don't bother with nonverbal alternative ... */ + ; + } } display_nhwindow(WIN_MESSAGE, FALSE); obj = hold_another_object(obj, "Oops!", (const char *) 0, diff --git a/src/mhitu.c b/src/mhitu.c index 480d616fe..21622c012 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mhitu.c $NHDT-Date: 1740534854 2025/02/25 17:54:14 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.327 $ */ +/* NetHack 3.7 mhitu.c $NHDT-Date: 1762750699 2025/11/09 20:58:19 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.334 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2264,8 +2264,12 @@ doseduce(struct monst *mon) if (cost > umoney) cost = umoney; if (!cost) { - SetVoice(mon, 0, 80, 0); - verbalize("It's on the house!"); + if (!Deaf) { + SetVoice(mon, 0, 80, 0); + verbalize("It's on the house!"); + } else { + pline("No charge."); + } } else { pline_mon(mon, "%s takes %ld %s for services rendered!", noit_Monnam(mon), cost, currency(cost)); From a55a7f785c81cf6e4ed15146b0c5b96864802449 Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 10 Nov 2025 07:20:32 -0500 Subject: [PATCH 074/442] CI update --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 9da64ea50..98d9af3a3 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -32,8 +32,8 @@ strategy: imageName: 'windows-2025' toolchainName: mingw buildTargetName: all - linux_noble_cross_msdos: - imageName: 'ubuntu-24.04' + linux_latest_cross_msdos: + imageName: 'ubuntu-latest' toolchainName: cross buildTargetName: msdos linux_noble_docs: From 157fcad161453acee2fcd9698da019e6ae69f185 Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 10 Nov 2025 08:24:39 -0500 Subject: [PATCH 075/442] troubleshoot Terminus font download in CI environment --- sys/msdos/fetch-cross-compiler.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sys/msdos/fetch-cross-compiler.sh b/sys/msdos/fetch-cross-compiler.sh index 36558a00c..26c84a36a 100755 --- a/sys/msdos/fetch-cross-compiler.sh +++ b/sys/msdos/fetch-cross-compiler.sh @@ -214,6 +214,7 @@ if [ -d djgpp/target ]; then cd ../../ fi +set -x FONT_VERSION="4.49" FONT_FILE="terminus-font-$FONT_VERSION" @@ -228,13 +229,15 @@ if [ ! -d "$FONT_LFILE" ]; then #Mac curl -L $FONT_URL --output $FONT_RFILE else - wget --quiet --no-hsts $FONT_URL +# wget --quiet --no-hsts $FONT_URL + curl -L $FONT_URL --output $FONT_RFILE fi tar -xvf $FONT_RFILE rm $FONT_RFILE else echo "terminus fonts are already available in lib/$FONT_LFILE" fi +set +x cd ../ From efbc0d8f8f7bf6011d33d1c46589608f2682022c Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 10 Nov 2025 08:52:06 -0500 Subject: [PATCH 076/442] more troubleshooting Terminus font use in CI environment --- sys/msdos/fetch-cross-compiler.sh | 14 ++++++++++---- sys/unix/hints/include/cross-post.370 | 16 ++++++++-------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/sys/msdos/fetch-cross-compiler.sh b/sys/msdos/fetch-cross-compiler.sh index 26c84a36a..ce9ad3ada 100755 --- a/sys/msdos/fetch-cross-compiler.sh +++ b/sys/msdos/fetch-cross-compiler.sh @@ -227,13 +227,19 @@ if [ ! -d "$FONT_LFILE" ]; then echo "Getting terminus fonts" if [ "$(uname)" = "Darwin" ]; then #Mac - curl -L $FONT_URL --output $FONT_RFILE + curl -s -L $FONT_URL --output $FONT_RFILE else # wget --quiet --no-hsts $FONT_URL - curl -L $FONT_URL --output $FONT_RFILE + curl --help all + curl -s -L $FONT_URL --output $FONT_RFILE + fi + ls -l + if [ -f "$FONT_RFILE" ]; then + tar -xvf $FONT_RFILE + rm $FONT_RFILE + else + echo "terminus fonts failed to download correctly to lib/$FONT_LFILE" fi - tar -xvf $FONT_RFILE - rm $FONT_RFILE else echo "terminus fonts are already available in lib/$FONT_LFILE" fi diff --git a/sys/unix/hints/include/cross-post.370 b/sys/unix/hints/include/cross-post.370 index 9d7c5b548..01a998572 100644 --- a/sys/unix/hints/include/cross-post.370 +++ b/sys/unix/hints/include/cross-post.370 @@ -57,14 +57,14 @@ dospkg: dodata dosfonts $(GAMEBIN) $(TARGETPFX)recover.exe ../dat/nhtiles.bmp cp ../sys/share/NetHack.cnf $(TARGETPFX)pkg/NETHACK.CNF cp ../sys/msdos/sysconf $(TARGETPFX)pkg/SYSCONF cp ../doc/nethack.txt $(TARGETPFX)pkg/NETHACK.TXT - cp $(DOSFONT)/ter-u16b.psf $(TARGETPFX)pkg/TER-U16B.PSF - cp $(DOSFONT)/ter-u16v.psf $(TARGETPFX)pkg/TER-U16V.PSF - cp $(DOSFONT)/ter-u18b.psf $(TARGETPFX)pkg/TER-U18B.PSF - cp $(DOSFONT)/ter-u20b.psf $(TARGETPFX)pkg/TER-U20B.PSF - cp $(DOSFONT)/ter-u22b.psf $(TARGETPFX)pkg/TER-U22B.PSF - cp $(DOSFONT)/ter-u24b.psf $(TARGETPFX)pkg/TER-U24B.PSF - cp $(DOSFONT)/ter-u28b.psf $(TARGETPFX)pkg/TER-U28B.PSF - cp $(DOSFONT)/ter-u32b.psf $(TARGETPFX)pkg/TER-U32B.PSF + -cp $(DOSFONT)/ter-u16b.psf $(TARGETPFX)pkg/TER-U16B.PSF + -cp $(DOSFONT)/ter-u16v.psf $(TARGETPFX)pkg/TER-U16V.PSF + -cp $(DOSFONT)/ter-u18b.psf $(TARGETPFX)pkg/TER-U18B.PSF + -cp $(DOSFONT)/ter-u20b.psf $(TARGETPFX)pkg/TER-U20B.PSF + -cp $(DOSFONT)/ter-u22b.psf $(TARGETPFX)pkg/TER-U22B.PSF + -cp $(DOSFONT)/ter-u24b.psf $(TARGETPFX)pkg/TER-U24B.PSF + -cp $(DOSFONT)/ter-u28b.psf $(TARGETPFX)pkg/TER-U28B.PSF + -cp $(DOSFONT)/ter-u32b.psf $(TARGETPFX)pkg/TER-U32B.PSF cp ../lib/djgpp/cwsdpmi/bin/CWSDPMI.EXE $(TARGETPFX)pkg/CWSDPMI.EXE ( if [ -f ../lib/djgpp/target/bin/symify.exe ]; then \ cp ../lib/djgpp/target/bin/symify.exe $(TARGETPFX)pkg/SYMIFY.EXE; \ From 2c747b079e1217f9a98ab3d6b8b4d81c517dcf53 Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 10 Nov 2025 09:19:47 -0500 Subject: [PATCH 077/442] revert some CI test changes --- sys/msdos/fetch-cross-compiler.sh | 17 ++++------------- sys/unix/hints/include/cross-post.370 | 16 ++++++++-------- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/sys/msdos/fetch-cross-compiler.sh b/sys/msdos/fetch-cross-compiler.sh index ce9ad3ada..36558a00c 100755 --- a/sys/msdos/fetch-cross-compiler.sh +++ b/sys/msdos/fetch-cross-compiler.sh @@ -214,7 +214,6 @@ if [ -d djgpp/target ]; then cd ../../ fi -set -x FONT_VERSION="4.49" FONT_FILE="terminus-font-$FONT_VERSION" @@ -227,23 +226,15 @@ if [ ! -d "$FONT_LFILE" ]; then echo "Getting terminus fonts" if [ "$(uname)" = "Darwin" ]; then #Mac - curl -s -L $FONT_URL --output $FONT_RFILE + curl -L $FONT_URL --output $FONT_RFILE else -# wget --quiet --no-hsts $FONT_URL - curl --help all - curl -s -L $FONT_URL --output $FONT_RFILE - fi - ls -l - if [ -f "$FONT_RFILE" ]; then - tar -xvf $FONT_RFILE - rm $FONT_RFILE - else - echo "terminus fonts failed to download correctly to lib/$FONT_LFILE" + wget --quiet --no-hsts $FONT_URL fi + tar -xvf $FONT_RFILE + rm $FONT_RFILE else echo "terminus fonts are already available in lib/$FONT_LFILE" fi -set +x cd ../ diff --git a/sys/unix/hints/include/cross-post.370 b/sys/unix/hints/include/cross-post.370 index 01a998572..9d7c5b548 100644 --- a/sys/unix/hints/include/cross-post.370 +++ b/sys/unix/hints/include/cross-post.370 @@ -57,14 +57,14 @@ dospkg: dodata dosfonts $(GAMEBIN) $(TARGETPFX)recover.exe ../dat/nhtiles.bmp cp ../sys/share/NetHack.cnf $(TARGETPFX)pkg/NETHACK.CNF cp ../sys/msdos/sysconf $(TARGETPFX)pkg/SYSCONF cp ../doc/nethack.txt $(TARGETPFX)pkg/NETHACK.TXT - -cp $(DOSFONT)/ter-u16b.psf $(TARGETPFX)pkg/TER-U16B.PSF - -cp $(DOSFONT)/ter-u16v.psf $(TARGETPFX)pkg/TER-U16V.PSF - -cp $(DOSFONT)/ter-u18b.psf $(TARGETPFX)pkg/TER-U18B.PSF - -cp $(DOSFONT)/ter-u20b.psf $(TARGETPFX)pkg/TER-U20B.PSF - -cp $(DOSFONT)/ter-u22b.psf $(TARGETPFX)pkg/TER-U22B.PSF - -cp $(DOSFONT)/ter-u24b.psf $(TARGETPFX)pkg/TER-U24B.PSF - -cp $(DOSFONT)/ter-u28b.psf $(TARGETPFX)pkg/TER-U28B.PSF - -cp $(DOSFONT)/ter-u32b.psf $(TARGETPFX)pkg/TER-U32B.PSF + cp $(DOSFONT)/ter-u16b.psf $(TARGETPFX)pkg/TER-U16B.PSF + cp $(DOSFONT)/ter-u16v.psf $(TARGETPFX)pkg/TER-U16V.PSF + cp $(DOSFONT)/ter-u18b.psf $(TARGETPFX)pkg/TER-U18B.PSF + cp $(DOSFONT)/ter-u20b.psf $(TARGETPFX)pkg/TER-U20B.PSF + cp $(DOSFONT)/ter-u22b.psf $(TARGETPFX)pkg/TER-U22B.PSF + cp $(DOSFONT)/ter-u24b.psf $(TARGETPFX)pkg/TER-U24B.PSF + cp $(DOSFONT)/ter-u28b.psf $(TARGETPFX)pkg/TER-U28B.PSF + cp $(DOSFONT)/ter-u32b.psf $(TARGETPFX)pkg/TER-U32B.PSF cp ../lib/djgpp/cwsdpmi/bin/CWSDPMI.EXE $(TARGETPFX)pkg/CWSDPMI.EXE ( if [ -f ../lib/djgpp/target/bin/symify.exe ]; then \ cp ../lib/djgpp/target/bin/symify.exe $(TARGETPFX)pkg/SYMIFY.EXE; \ From d4a04b2632b24c20baa60f195ab7881855ad8ba5 Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 10 Nov 2025 09:21:22 -0500 Subject: [PATCH 078/442] turn off msdos build in CI for now --- azure-pipelines.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 98d9af3a3..091ac0823 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -32,10 +32,10 @@ strategy: imageName: 'windows-2025' toolchainName: mingw buildTargetName: all - linux_latest_cross_msdos: - imageName: 'ubuntu-latest' - toolchainName: cross - buildTargetName: msdos +# linux_latest_cross_msdos: +# imageName: 'ubuntu-latest' +# toolchainName: cross +# buildTargetName: msdos linux_noble_docs: imageName: 'ubuntu-24.04' toolchainName: docs From c12344cd48083a48694321898830de71e10a4dfa Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 10 Nov 2025 12:56:49 -0500 Subject: [PATCH 079/442] build dos in CI but skip faltering Terminus font steps --- azure-pipelines.yml | 11 ++++++----- sys/unix/hints/include/cross-post.370 | 16 ++++++++-------- sys/unix/hints/include/cross-pre2.370 | 4 ++++ 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 091ac0823..4c0a5b5d2 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -32,10 +32,10 @@ strategy: imageName: 'windows-2025' toolchainName: mingw buildTargetName: all -# linux_latest_cross_msdos: -# imageName: 'ubuntu-latest' -# toolchainName: cross -# buildTargetName: msdos + linux_latest_cross_msdos: + imageName: 'ubuntu-latest' + toolchainName: cross + buildTargetName: msdos linux_noble_docs: imageName: 'ubuntu-24.04' toolchainName: docs @@ -227,6 +227,7 @@ steps: - bash: | sudo apt -qq -y install libfl2 export GCCVER=gcc1220 + export IN_CI=SKIP_FONTS_IN_CI=1 cd sys/unix sh setup.sh hints/linux.370 cd ../.. @@ -234,7 +235,7 @@ steps: sys/msdos/fetch-cross-compiler.sh retVal=$? if [ $retVal -eq 0 ]; then - make LUA_VERSION=5.4.6 WANT_WIN_TTY=1 WANT_WIN_CURSES=1 CROSS_TO_MSDOS=1 package + make LUA_VERSION=5.4.6 WANT_WIN_TTY=1 WANT_WIN_CURSES=1 CROSS_TO_MSDOS=1 $IN_CI package fi condition: and(eq( variables['Agent.OS'], 'Linux' ), eq( variables.toolchain, 'cross')) workingDirectory: $(Agent.BuildDirectory)/$(netHackPath) diff --git a/sys/unix/hints/include/cross-post.370 b/sys/unix/hints/include/cross-post.370 index 9d7c5b548..01a998572 100644 --- a/sys/unix/hints/include/cross-post.370 +++ b/sys/unix/hints/include/cross-post.370 @@ -57,14 +57,14 @@ dospkg: dodata dosfonts $(GAMEBIN) $(TARGETPFX)recover.exe ../dat/nhtiles.bmp cp ../sys/share/NetHack.cnf $(TARGETPFX)pkg/NETHACK.CNF cp ../sys/msdos/sysconf $(TARGETPFX)pkg/SYSCONF cp ../doc/nethack.txt $(TARGETPFX)pkg/NETHACK.TXT - cp $(DOSFONT)/ter-u16b.psf $(TARGETPFX)pkg/TER-U16B.PSF - cp $(DOSFONT)/ter-u16v.psf $(TARGETPFX)pkg/TER-U16V.PSF - cp $(DOSFONT)/ter-u18b.psf $(TARGETPFX)pkg/TER-U18B.PSF - cp $(DOSFONT)/ter-u20b.psf $(TARGETPFX)pkg/TER-U20B.PSF - cp $(DOSFONT)/ter-u22b.psf $(TARGETPFX)pkg/TER-U22B.PSF - cp $(DOSFONT)/ter-u24b.psf $(TARGETPFX)pkg/TER-U24B.PSF - cp $(DOSFONT)/ter-u28b.psf $(TARGETPFX)pkg/TER-U28B.PSF - cp $(DOSFONT)/ter-u32b.psf $(TARGETPFX)pkg/TER-U32B.PSF + -cp $(DOSFONT)/ter-u16b.psf $(TARGETPFX)pkg/TER-U16B.PSF + -cp $(DOSFONT)/ter-u16v.psf $(TARGETPFX)pkg/TER-U16V.PSF + -cp $(DOSFONT)/ter-u18b.psf $(TARGETPFX)pkg/TER-U18B.PSF + -cp $(DOSFONT)/ter-u20b.psf $(TARGETPFX)pkg/TER-U20B.PSF + -cp $(DOSFONT)/ter-u22b.psf $(TARGETPFX)pkg/TER-U22B.PSF + -cp $(DOSFONT)/ter-u24b.psf $(TARGETPFX)pkg/TER-U24B.PSF + -cp $(DOSFONT)/ter-u28b.psf $(TARGETPFX)pkg/TER-U28B.PSF + -cp $(DOSFONT)/ter-u32b.psf $(TARGETPFX)pkg/TER-U32B.PSF cp ../lib/djgpp/cwsdpmi/bin/CWSDPMI.EXE $(TARGETPFX)pkg/CWSDPMI.EXE ( if [ -f ../lib/djgpp/target/bin/symify.exe ]; then \ cp ../lib/djgpp/target/bin/symify.exe $(TARGETPFX)pkg/SYMIFY.EXE; \ diff --git a/sys/unix/hints/include/cross-pre2.370 b/sys/unix/hints/include/cross-pre2.370 index 89434556a..efa4ca3e0 100644 --- a/sys/unix/hints/include/cross-pre2.370 +++ b/sys/unix/hints/include/cross-pre2.370 @@ -188,6 +188,7 @@ MSDOS_TARGET_CXXFLAGS = -c -O $(DBGFLAGS) -I../include -I../sys/msdos -I../win/s PDCINCL += -I$(PDCPORT) PDC_TARGET_CFLAGS = $(MSDOS_TARGET_CFLAGS) -Wno-unused-parameter \ -Wno-missing-prototypes +ifndef SKIP_FONTS_IN_CI FONTVER = terminus-font-4.49.1 FONTTOP = ../lib/$(FONTVER) DOSFONT = ../sys/msdos/fonts @@ -195,6 +196,9 @@ FONTTARGETS = $(DOSFONT)/ter-u16b.psf $(DOSFONT)/ter-u16v.psf \ $(DOSFONT)/ter-u18b.psf $(DOSFONT)/ter-u20b.psf \ $(DOSFONT)/ter-u22b.psf $(DOSFONT)/ter-u24b.psf \ $(DOSFONT)/ter-u28b.psf $(DOSFONT)/ter-u32b.psf +else +FONTTARGETS= +endif LUABIN = ../lib/lua-$(LUA_VERSION)/src/lua LUA_TARGET_CFLAGS = $(MSDOS_TARGET_CFLAGS) override TARGET_CFLAGS = $(MSDOS_TARGET_CFLAGS) $(MSDOS_PEDANTIC) From cd98047de47a5466331ac9ec77ef6e7967aac01b Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 12 Nov 2025 20:40:01 -0500 Subject: [PATCH 080/442] update tested versions of Visual Studio 2025-11-12 --- sys/windows/Makefile.nmake | 5 +++-- sys/windows/build-nmake.txt | 6 +++--- sys/windows/build-vs.txt | 6 +++--- sys/windows/vs/NetHack/NetHack.vcxproj | 14 +++++++------- sys/windows/vs/NetHackProperties.props | 2 +- sys/windows/vs/PDCurses/PDCurses.vcxproj | 2 +- sys/windows/vs/hacklib/hacklib.vcxproj | 17 ++++++++--------- sys/windows/vs/lualib/lualib.vcxproj | 17 ++++++++--------- 8 files changed, 34 insertions(+), 35 deletions(-) diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index 2cd87415b..cc0209e22 100644 --- a/sys/windows/Makefile.nmake +++ b/sys/windows/Makefile.nmake @@ -8,7 +8,8 @@ # MS Visual Studio Visual C++ compiler # # Visual Studio Compilers Tested: -# - Microsoft Visual Studio 2022 Community Edition v 17.14.18 +# - Microsoft Visual Studio Community 2022 v 17.14.20 +# - Microsoft Visual Studio Community 2026 v 18.0.0 # #============================================================================== # This is used for building two distinct executables of NetHack: @@ -1177,7 +1178,7 @@ rc=Rc.exe # is too old or untested. # # Recently tested versions: -TESTEDVS2022 = 14.44.35217.0 +TESTEDVS2022 = 14.44.35220.0 # Other versions: TESTEDVS2026 = 14.50.35717.0 diff --git a/sys/windows/build-nmake.txt b/sys/windows/build-nmake.txt index 16c5fce52..a917eb3ad 100644 --- a/sys/windows/build-nmake.txt +++ b/sys/windows/build-nmake.txt @@ -2,8 +2,8 @@ Building NetHack using the Visual Studio nmake from the command line Prerequisite Requirements: - o Visual Studio Community Edition - A copy of Microsoft Visual Studio Community Edition needs to + o Visual Studio Community + A copy of Microsoft Visual Studio Community needs to be installed on your machine. See: https://visualstudio.microsoft.com/vs/community/ o Lua @@ -54,7 +54,7 @@ You can use one of the following build environments: /-----------------------------------------------------------\ | Building From the Command Line Using nmake from one of the | -| Visual Studio Community Editions | +| Visual Studio Community versions | \-----------------------------------------------------------/ Building diff --git a/sys/windows/build-vs.txt b/sys/windows/build-vs.txt index 2585c0308..90c12c3e1 100644 --- a/sys/windows/build-vs.txt +++ b/sys/windows/build-vs.txt @@ -2,8 +2,8 @@ Building NetHack using the Visual Studio IDE Prerequisite Requirements: - o Visual Studio Community Edition - A copy of Microsoft Visual Studio Community Edition needs to + o Visual Studio Community + A copy of a version of Microsoft Visual Studio Community needs to be installed on your machine. See: https://visualstudio.microsoft.com/vs/community/ o Lua @@ -57,7 +57,7 @@ versions: | Building And Running Using Visual Studio 2022 or greater | \-----------------------------------------------------------/ -When using Visual Studio Community Edition, load the provided solution +When using a version of Visual Studio Community, load the provided solution file within the IDE, build the solution. The Visual Studio NetHack solution file can be found here: diff --git a/sys/windows/vs/NetHack/NetHack.vcxproj b/sys/windows/vs/NetHack/NetHack.vcxproj index 7b28b0837..1ce8af4cd 100644 --- a/sys/windows/vs/NetHack/NetHack.vcxproj +++ b/sys/windows/vs/NetHack/NetHack.vcxproj @@ -1,13 +1,7 @@ - + - - {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751} - Win32Proj - NetHack - 10.0 - @@ -15,6 +9,12 @@ + + {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751} + Win32Proj + NetHack + 10.0 + $(BinDir) diff --git a/sys/windows/vs/NetHackProperties.props b/sys/windows/vs/NetHackProperties.props index 0e5f10889..1bda6a0f0 100644 --- a/sys/windows/vs/NetHackProperties.props +++ b/sys/windows/vs/NetHackProperties.props @@ -1,5 +1,5 @@ - + 3 diff --git a/sys/windows/vs/PDCurses/PDCurses.vcxproj b/sys/windows/vs/PDCurses/PDCurses.vcxproj index ab75f18db..90550816c 100644 --- a/sys/windows/vs/PDCurses/PDCurses.vcxproj +++ b/sys/windows/vs/PDCurses/PDCurses.vcxproj @@ -1,5 +1,5 @@ - + diff --git a/sys/windows/vs/hacklib/hacklib.vcxproj b/sys/windows/vs/hacklib/hacklib.vcxproj index 9ac94ffb1..5876a277b 100644 --- a/sys/windows/vs/hacklib/hacklib.vcxproj +++ b/sys/windows/vs/hacklib/hacklib.vcxproj @@ -6,6 +6,12 @@ + + Win32Proj + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} + hacklib + 10.0 + Debug @@ -43,13 +49,6 @@ - - 17.0 - Win32Proj - {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} - hacklib - 10.0 - StaticLibrary @@ -67,15 +66,15 @@ StaticLibrary true - v143 Unicode + v143 StaticLibrary false - v143 true Unicode + v143 diff --git a/sys/windows/vs/lualib/lualib.vcxproj b/sys/windows/vs/lualib/lualib.vcxproj index 185f94b9d..d18f8a5ac 100644 --- a/sys/windows/vs/lualib/lualib.vcxproj +++ b/sys/windows/vs/lualib/lualib.vcxproj @@ -6,6 +6,12 @@ + + Win32Proj + {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} + lualib + 10.0 + Debug @@ -69,13 +75,6 @@ - - 17.0 - Win32Proj - {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} - lualib - 10.0 - StaticLibrary @@ -93,15 +92,15 @@ StaticLibrary true - v143 Unicode + v143 StaticLibrary false - v143 true Unicode + v143 From 2b845066fa8ec4fb2a30e27430a0227d3db34789 Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 12 Nov 2025 21:14:42 -0500 Subject: [PATCH 081/442] update naming for generated Files --- sys/windows/vs/.gitattributes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/windows/vs/.gitattributes b/sys/windows/vs/.gitattributes index 047ae4d7a..508769ffa 100644 --- a/sys/windows/vs/.gitattributes +++ b/sys/windows/vs/.gitattributes @@ -1,2 +1,2 @@ -* NH_filestag=(file%s_for_Visual_Studio_2022_Community_Edition_builds) +* NH_filestag=(file%s_for_Visual_Studio_Community_builds) ..files !NH_filegenerated From b5792107a1670e447bb4b43aa0767f2f004dfa62 Mon Sep 17 00:00:00 2001 From: nhw_cron Date: Wed, 12 Nov 2025 21:24:07 -0500 Subject: [PATCH 082/442] This is cron-daily v1-Apr-1-2024. 000files updated: Files --- Files | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Files b/Files index 40aa65597..d5cecb8fd 100644 --- a/Files +++ b/Files @@ -442,7 +442,7 @@ win10.c win10.h win32api.h windmain.c windsys.c winos.h sys/windows/vs: -(files for Visual Studio 2022 Community Edition builds) +(files for Visual Studio Community builds) NetHack.sln NetHackPackage.appxmanifest NetHackPackage.wapproj NetHackProperties.props Package.StoreAssociation.xml ScreenShot.PNG @@ -454,11 +454,11 @@ dirs.props dll.props files.props sfctool.sln sys/windows/vs/FetchPrereq: -(files for Visual Studio 2022 Community Edition builds) +(files for Visual Studio Community builds) fetchprereq.nmake fetchprereq.vcxproj sys/windows/vs/Images: -(files for Visual Studio 2022 Community Edition builds) +(files for Visual Studio Community builds) BadgeLogo.scale-100.png BadgeLogo.scale-125.png BadgeLogo.scale-150.png @@ -513,71 +513,71 @@ Wide310x150Logo.scale-200.png Wide310x150Logo.scale-400.png sys/windows/vs/NetHack: -(files for Visual Studio 2022 Community Edition builds) +(files for Visual Studio Community builds) NetHack.vcxproj afternethack.proj sys/windows/vs/NetHackW: -(file for Visual Studio 2022 Community Edition builds) +(file for Visual Studio Community builds) NetHackW.vcxproj sys/windows/vs/PDCurses: -(file for Visual Studio 2022 Community Edition builds) +(file for Visual Studio Community builds) PDCurses.vcxproj sys/windows/vs/PDCursesGui: -(file for Visual Studio 2022 Community Edition builds) +(file for Visual Studio Community builds) pdcursesgui.vcxproj sys/windows/vs/dlb: -(files for Visual Studio 2022 Community Edition builds) +(files for Visual Studio Community builds) afterdlb.proj dlb.vcxproj sys/windows/vs/fetchctags: -(files for Visual Studio 2022 Community Edition builds) +(files for Visual Studio Community builds) fetchctags.nmake fetchctags.vcxproj sys/windows/vs/hacklib: -(file for Visual Studio 2022 Community Edition builds) +(file for Visual Studio Community builds) hacklib.vcxproj sys/windows/vs/lualib: -(file for Visual Studio 2022 Community Edition builds) +(file for Visual Studio Community builds) lualib.vcxproj sys/windows/vs/makedefs: -(files for Visual Studio 2022 Community Edition builds) +(files for Visual Studio Community builds) aftermakedefs.proj makedefs.vcxproj sys/windows/vs/package: -(files for Visual Studio 2022 Community Edition builds) +(files for Visual Studio Community builds) package.nmake package.vcxproj sys/windows/vs/recover: -(files for Visual Studio 2022 Community Edition builds) +(files for Visual Studio Community builds) afterrecover.proj recover.vcxproj sys/windows/vs/sfctool: -(file for Visual Studio 2022 Community Edition builds) +(file for Visual Studio Community builds) sfctool.vcxproj sys/windows/vs/sftags: -(files for Visual Studio 2022 Community Edition builds) +(files for Visual Studio Community builds) aftersftags.proj sftags.vcxproj sys/windows/vs/tile2bmp: -(files for Visual Studio 2022 Community Edition builds) +(files for Visual Studio Community builds) aftertile2bmp.proj tile2bmp.vcxproj sys/windows/vs/tilemap: -(files for Visual Studio 2022 Community Edition builds) +(files for Visual Studio Community builds) aftertilemap.proj tilemap.vcxproj sys/windows/vs/tiles: -(file for Visual Studio 2022 Community Edition builds) +(file for Visual Studio Community builds) tiles.vcxproj sys/windows/vs/uudecode: -(files for Visual Studio 2022 Community Edition builds) +(files for Visual Studio Community builds) afteruudecode.proj uudecode.vcxproj test: From eb4496c575439669dace9ea031582eb2073ae298 Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 13 Nov 2025 09:11:49 -0500 Subject: [PATCH 083/442] vcxproj updates define HAS_INTTYPES_H alongside HAS_STDINT_H in the visual studio projects --- sys/windows/vs/NetHack/NetHack.vcxproj | 2 +- sys/windows/vs/NetHackW/NetHackW.vcxproj | 2 +- sys/windows/vs/dlb/dlb.vcxproj | 2 +- sys/windows/vs/hacklib/hacklib.vcxproj | 8 ++++---- sys/windows/vs/lualib/lualib.vcxproj | 10 +++++----- sys/windows/vs/makedefs/makedefs.vcxproj | 2 +- sys/windows/vs/recover/recover.vcxproj | 4 ++-- sys/windows/vs/sfctool/sfctool.vcxproj | 8 ++++---- sys/windows/vs/sftags/sftags.vcxproj | 8 ++++---- sys/windows/vs/tile2bmp/tile2bmp.vcxproj | 2 +- sys/windows/vs/tilemap/tilemap.vcxproj | 2 +- 11 files changed, 25 insertions(+), 25 deletions(-) diff --git a/sys/windows/vs/NetHack/NetHack.vcxproj b/sys/windows/vs/NetHack/NetHack.vcxproj index 1ce8af4cd..15bf8e093 100644 --- a/sys/windows/vs/NetHack/NetHack.vcxproj +++ b/sys/windows/vs/NetHack/NetHack.vcxproj @@ -47,7 +47,7 @@ Speed true $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);%(AdditionalIncludeDirectories) - WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;%(PreprocessorDefinitions) + WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) stdclatest diff --git a/sys/windows/vs/NetHackW/NetHackW.vcxproj b/sys/windows/vs/NetHackW/NetHackW.vcxproj index 11e6baa99..7414e83de 100644 --- a/sys/windows/vs/NetHackW/NetHackW.vcxproj +++ b/sys/windows/vs/NetHackW/NetHackW.vcxproj @@ -53,7 +53,7 @@ Disabled true $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);%(AdditionalIncludeDirectories) - TILES;_WINDOWS;DLB;MSWIN_GRAPHICS;SAFEPROCS;NOTTYGRAPHICS;SND_LIB_WINDSOUND;USER_SOUNDS;HAS_STDINT_H;PDC_WIDE;%(PreprocessorDefinitions) + TILES;_WINDOWS;DLB;MSWIN_GRAPHICS;SAFEPROCS;NOTTYGRAPHICS;SND_LIB_WINDSOUND;USER_SOUNDS;HAS_STDINT_H;HAS_INTTYPES_H;PDC_WIDE;%(PreprocessorDefinitions) stdclatest stdclatest stdclatest diff --git a/sys/windows/vs/dlb/dlb.vcxproj b/sys/windows/vs/dlb/dlb.vcxproj index 71b96d823..6b91e9b3f 100644 --- a/sys/windows/vs/dlb/dlb.vcxproj +++ b/sys/windows/vs/dlb/dlb.vcxproj @@ -27,7 +27,7 @@ $(IncDir);$(SysWindDir);$(SysShareDir);$(LuaDir);%(AdditionalIncludeDirectories) - WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;%(PreprocessorDefinitions) + WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) stdclatest stdclatest stdclatest diff --git a/sys/windows/vs/hacklib/hacklib.vcxproj b/sys/windows/vs/hacklib/hacklib.vcxproj index 5876a277b..6c439c101 100644 --- a/sys/windows/vs/hacklib/hacklib.vcxproj +++ b/sys/windows/vs/hacklib/hacklib.vcxproj @@ -98,7 +98,7 @@ Level3 true - WIN32;_DEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) + WIN32;_DEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) true $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) stdclatest @@ -116,7 +116,7 @@ true true true - WIN32;NDEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) + WIN32;NDEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) true $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) stdclatest @@ -134,7 +134,7 @@ Level3 true - _DEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) + _DEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) true $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) stdclatest @@ -152,7 +152,7 @@ true true true - NDEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) + NDEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) true $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) stdclatest diff --git a/sys/windows/vs/lualib/lualib.vcxproj b/sys/windows/vs/lualib/lualib.vcxproj index d18f8a5ac..d244e8eb4 100644 --- a/sys/windows/vs/lualib/lualib.vcxproj +++ b/sys/windows/vs/lualib/lualib.vcxproj @@ -37,7 +37,7 @@ Disabled true $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);%(AdditionalIncludeDirectories) - TILES;_WINDOWS;DLB;MSWIN_GRAPHICS;SAFEPROCS;NOTTYGRAPHICS;SND_LIB_WINDSOUND;USER_SOUNDS;HAS_STDINT_H;PDC_WIDE;%(PreprocessorDefinitions) + TILES;_WINDOWS;DLB;MSWIN_GRAPHICS;SAFEPROCS;NOTTYGRAPHICS;SND_LIB_WINDSOUND;USER_SOUNDS;HAS_STDINT_H;HAS_INTTYPES_H;PDC_WIDE;%(PreprocessorDefinitions) stdclatest @@ -124,7 +124,7 @@ Level3 true - WIN32;_DEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) + WIN32;_DEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) true $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) /Gs /Oi- /w44774 %(AdditionalOptions) @@ -141,7 +141,7 @@ true true true - WIN32;NDEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) + WIN32;NDEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) true $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) /Gs /Oi- /w44774 %(AdditionalOptions) @@ -158,7 +158,7 @@ Level3 true - _DEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) + _DEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) true $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) /Gs /Oi- /w44774 %(AdditionalOptions) @@ -175,7 +175,7 @@ true true true - NDEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) + NDEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) true $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) /Gs /Oi- /w44774 %(AdditionalOptions) diff --git a/sys/windows/vs/makedefs/makedefs.vcxproj b/sys/windows/vs/makedefs/makedefs.vcxproj index 5b3c90584..235c390e2 100644 --- a/sys/windows/vs/makedefs/makedefs.vcxproj +++ b/sys/windows/vs/makedefs/makedefs.vcxproj @@ -27,7 +27,7 @@ $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) - WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) + WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) stdclatest /w45262 %(AdditionalOptions) diff --git a/sys/windows/vs/recover/recover.vcxproj b/sys/windows/vs/recover/recover.vcxproj index d03553015..eceda86fb 100644 --- a/sys/windows/vs/recover/recover.vcxproj +++ b/sys/windows/vs/recover/recover.vcxproj @@ -19,7 +19,7 @@ $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) - WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;MINIMAL_FOR_RECOVER;%(PreprocessorDefinitions) + WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;HAS_INTTYPES_H;MINIMAL_FOR_RECOVER;%(PreprocessorDefinitions) stdclatest /w45262 %(AdditionalOptions) @@ -31,7 +31,7 @@ - WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;MINIMAL_FOR_RECOVER;%(PreprocessorDefinitions) + WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;HAS_INTTYPES_H;MINIMAL_FOR_RECOVER;%(PreprocessorDefinitions) diff --git a/sys/windows/vs/sfctool/sfctool.vcxproj b/sys/windows/vs/sfctool/sfctool.vcxproj index 73fee3c05..e46b1d9bc 100644 --- a/sys/windows/vs/sfctool/sfctool.vcxproj +++ b/sys/windows/vs/sfctool/sfctool.vcxproj @@ -123,7 +123,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 stdclatest true @@ -139,7 +139,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_NDEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_NDEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 stdclatest true @@ -159,7 +159,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 stdclatest true @@ -175,7 +175,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_NDEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_NDEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 stdclatest true diff --git a/sys/windows/vs/sftags/sftags.vcxproj b/sys/windows/vs/sftags/sftags.vcxproj index f2979e613..e40df7824 100644 --- a/sys/windows/vs/sftags/sftags.vcxproj +++ b/sys/windows/vs/sftags/sftags.vcxproj @@ -83,7 +83,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 true true @@ -98,7 +98,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 true true @@ -117,7 +117,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 true true @@ -132,7 +132,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 true true diff --git a/sys/windows/vs/tile2bmp/tile2bmp.vcxproj b/sys/windows/vs/tile2bmp/tile2bmp.vcxproj index 8207ebceb..1bab9d61c 100644 --- a/sys/windows/vs/tile2bmp/tile2bmp.vcxproj +++ b/sys/windows/vs/tile2bmp/tile2bmp.vcxproj @@ -27,7 +27,7 @@ $(IncDir);$(SysWindDir);$(SysShareDir);$(LuaDir);%(AdditionalIncludeDirectories) - WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;%(PreprocessorDefinitions) + WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) stdclatest /w45262 %(AdditionalOptions) diff --git a/sys/windows/vs/tilemap/tilemap.vcxproj b/sys/windows/vs/tilemap/tilemap.vcxproj index fdbbc92e7..7c99182b5 100644 --- a/sys/windows/vs/tilemap/tilemap.vcxproj +++ b/sys/windows/vs/tilemap/tilemap.vcxproj @@ -29,7 +29,7 @@ $(IncDir);$(SysWindDir);$(SysShareDir);$(LuaDir);%(AdditionalIncludeDirectories) - WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;%(PreprocessorDefinitions) + WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) stdclatest /w45262 %(AdditionalOptions) From d7e4f2e2b1a4d15cd69f06606b02f0f0b94c6e4f Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 14 Nov 2025 21:24:24 -0500 Subject: [PATCH 084/442] Windows build support for Windows 11 arm64 (snapdragon) --- sys/windows/Makefile.nmake | 26 ++++++ .../vs/FetchPrereq/fetchprereq.vcxproj | 16 ++++ sys/windows/vs/NetHack.sln | 62 +++++++++++++- sys/windows/vs/NetHack/NetHack.vcxproj | 40 +++++++-- sys/windows/vs/NetHackW/NetHackW.vcxproj | 41 +++++++++- sys/windows/vs/PDCurses/PDCurses.vcxproj | 50 +++++++++++ .../vs/PDCursesGui/pdcursesgui.vcxproj | 46 +++++++++++ sys/windows/vs/dlb/dlb.vcxproj | 38 ++++++++- sys/windows/vs/fetchctags/fetchctags.vcxproj | 19 ++++- sys/windows/vs/hacklib/hacklib.vcxproj | 63 ++++++++++++++ sys/windows/vs/lualib/lualib.vcxproj | 61 ++++++++++++++ sys/windows/vs/makedefs/makedefs.vcxproj | 34 +++++++- sys/windows/vs/package/package.vcxproj | 24 +++++- sys/windows/vs/recover/recover.vcxproj | 28 ++++++- sys/windows/vs/sfctool.sln | 22 ++++- sys/windows/vs/sfctool/sfctool.vcxproj | 63 ++++++++++++++ sys/windows/vs/sftags/sftags.vcxproj | 82 ++++++++++++++++--- sys/windows/vs/tile2bmp/tile2bmp.vcxproj | 40 ++++++++- sys/windows/vs/tilemap/tilemap.vcxproj | 35 +++++++- sys/windows/vs/uudecode/uudecode.vcxproj | 32 ++++++++ 20 files changed, 786 insertions(+), 36 deletions(-) diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index cc0209e22..47fcfda03 100644 --- a/sys/windows/Makefile.nmake +++ b/sys/windows/Makefile.nmake @@ -107,6 +107,7 @@ GIT_AVAILABLE = N #TARGET_CPU=x64 #TARGET_CPU=x86 +#TARGET_CPU=arm64 #============================================================================== #======================== End of Modification Section ========================= @@ -393,6 +394,7 @@ ADD_CURSES = N #developer command prompts. #VSCMD_ARG_HOST_ARCH=x64 #VSCMD_ARG_TGT_ARCH=x86 +#VSCMD_ARG_TGT_ARCH=arm64 # We need to do this here, so some output files can # incorporate TARGET_CPU into their names. @@ -404,7 +406,11 @@ ADD_CURSES = N ! IF "$(VSCMD_ARG_TGT_ARCH)"=="x64" TARGET_CPU=x64 ! ELSE +! IF "$(VSCMD_ARG_TGT_ARCH)"=="arm64" +TARGET_CPU=arm64 +! ELSE TARGET_CPU=x86 +! ENDIF ! ENDIF ! ENDIF !ENDIF @@ -1269,6 +1275,9 @@ scall = -Gz !ELSEIF "$(TARGET_CPU)" == "x64" ctmpflags = $(ctmpflags1) -D_AMD64_=1 -DWIN64 -D_WIN64 -DWIN32 -D_WIN32 -W4 scall = +!ELSEIF "$(TARGET_CPU)" == "arm64" +ctmpflags = $(ctmpflags1) -DWIN64 -D_WIN64 -DWIN32 -D_WIN32 -W4 +scall = !ENDIF !IF ($(VSVER) >= 2012) @@ -1326,6 +1335,12 @@ DLLENTRY = MACHINE=/MACHINE:X64 !ENDIF +# declarations for use on arm64 systems +!IF "$(TARGET_CPU)" == "arm64" +DLLENTRY = +MACHINE=/MACHINE:arm64 +!ENDIF + # for Windows applications conlflags = $(lflags) -subsystem:console guilflags = $(lflags) -subsystem:windows @@ -2009,7 +2024,11 @@ cpu.tag: ! IF "$(TARGET_CPU)"=="x64" @echo Windows x64 64-bit target build ! ELSE +! IF "$(TARGET_CPU)"=="arm64" + @echo Windows arm64 64-bit target build +! ELSE @echo Windows x86 32-bit target build +! ENDIF ! ENDIF @echo cpu >$@ @@ -2768,6 +2787,13 @@ spotless: clean if exist opdcdirx64.tag del opdcdirx64.tag if exist opdccdirx64.tag del opdccdirx64.tag if exist opdcgdirx64.tag del opdcgdirx64.tag + if exist outldirarm64.tag del outldirarm64.tag + if exist ottydirarm64.tag del ottydirarm64.tag + if exist oguidirarm64.tag del oguidirarm64.tag + if exist oluadirarm64.tag del oluadirarm64.tag + if exist opdcdirarm64.tag del opdcdirarm64.tag + if exist opdccdirarm64.tag del opdccdirarm64.tag + if exist opdcgdirarm64.tag del opdcgdirarm64.tag if exist libdir.tag del libdir.tag if exist gamedir.tag del gamedir.tag if exist $(MSWIN)mnsel.bmp del $(MSWIN)mnsel.bmp diff --git a/sys/windows/vs/FetchPrereq/fetchprereq.vcxproj b/sys/windows/vs/FetchPrereq/fetchprereq.vcxproj index 650e8ed87..1aab0cbbc 100644 --- a/sys/windows/vs/FetchPrereq/fetchprereq.vcxproj +++ b/sys/windows/vs/FetchPrereq/fetchprereq.vcxproj @@ -1,10 +1,18 @@ + + Debug + ARM64 + Debug Win32 + + Release + ARM64 + Release Win32 @@ -39,10 +47,18 @@ Makefile true + + Makefile + true + Makefile false + + Makefile + false + diff --git a/sys/windows/vs/NetHack.sln b/sys/windows/vs/NetHack.sln index 3bb69f828..99f79d38d 100644 --- a/sys/windows/vs/NetHack.sln +++ b/sys/windows/vs/NetHack.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.31903.59 +# Visual Studio Version 18 +VisualStudioVersion = 18.0.11205.157 d18.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NetHackW", "NetHackW\NetHackW.vcxproj", "{CEC5D360-8804-454F-8591-002184C23499}" ProjectSection(ProjectDependencies) = postProject @@ -110,120 +110,178 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lualib", "lualib\lualib.vcx EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 + Release|ARM64 = Release|ARM64 Release|Win32 = Release|Win32 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CEC5D360-8804-454F-8591-002184C23499}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {CEC5D360-8804-454F-8591-002184C23499}.Debug|ARM64.Build.0 = Debug|ARM64 {CEC5D360-8804-454F-8591-002184C23499}.Debug|Win32.ActiveCfg = Debug|Win32 {CEC5D360-8804-454F-8591-002184C23499}.Debug|Win32.Build.0 = Debug|Win32 {CEC5D360-8804-454F-8591-002184C23499}.Debug|x64.ActiveCfg = Debug|x64 {CEC5D360-8804-454F-8591-002184C23499}.Debug|x64.Build.0 = Debug|x64 + {CEC5D360-8804-454F-8591-002184C23499}.Release|ARM64.ActiveCfg = Release|ARM64 + {CEC5D360-8804-454F-8591-002184C23499}.Release|ARM64.Build.0 = Release|ARM64 {CEC5D360-8804-454F-8591-002184C23499}.Release|Win32.ActiveCfg = Release|Win32 {CEC5D360-8804-454F-8591-002184C23499}.Release|Win32.Build.0 = Release|Win32 {CEC5D360-8804-454F-8591-002184C23499}.Release|x64.ActiveCfg = Release|x64 {CEC5D360-8804-454F-8591-002184C23499}.Release|x64.Build.0 = Release|x64 + {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC}.Debug|ARM64.Build.0 = Debug|ARM64 {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC}.Debug|Win32.ActiveCfg = Debug|Win32 {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC}.Debug|Win32.Build.0 = Debug|Win32 {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC}.Debug|x64.ActiveCfg = Debug|x64 {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC}.Debug|x64.Build.0 = Debug|x64 + {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC}.Release|ARM64.ActiveCfg = Release|ARM64 + {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC}.Release|ARM64.Build.0 = Release|ARM64 {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC}.Release|Win32.ActiveCfg = Release|Win32 {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC}.Release|Win32.Build.0 = Release|Win32 {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC}.Release|x64.ActiveCfg = Release|x64 {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC}.Release|x64.Build.0 = Release|x64 + {BA3DD34C-04B7-40D0-B373-9329AA9E8945}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {BA3DD34C-04B7-40D0-B373-9329AA9E8945}.Debug|ARM64.Build.0 = Debug|ARM64 {BA3DD34C-04B7-40D0-B373-9329AA9E8945}.Debug|Win32.ActiveCfg = Debug|Win32 {BA3DD34C-04B7-40D0-B373-9329AA9E8945}.Debug|Win32.Build.0 = Debug|Win32 {BA3DD34C-04B7-40D0-B373-9329AA9E8945}.Debug|x64.ActiveCfg = Debug|x64 {BA3DD34C-04B7-40D0-B373-9329AA9E8945}.Debug|x64.Build.0 = Debug|x64 + {BA3DD34C-04B7-40D0-B373-9329AA9E8945}.Release|ARM64.ActiveCfg = Release|ARM64 + {BA3DD34C-04B7-40D0-B373-9329AA9E8945}.Release|ARM64.Build.0 = Release|ARM64 {BA3DD34C-04B7-40D0-B373-9329AA9E8945}.Release|Win32.ActiveCfg = Release|Win32 {BA3DD34C-04B7-40D0-B373-9329AA9E8945}.Release|Win32.Build.0 = Release|Win32 {BA3DD34C-04B7-40D0-B373-9329AA9E8945}.Release|x64.ActiveCfg = Release|x64 {BA3DD34C-04B7-40D0-B373-9329AA9E8945}.Release|x64.Build.0 = Release|x64 + {2F35F228-6733-4FE5-9B46-B3AA10D4BC2E}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {2F35F228-6733-4FE5-9B46-B3AA10D4BC2E}.Debug|ARM64.Build.0 = Debug|ARM64 {2F35F228-6733-4FE5-9B46-B3AA10D4BC2E}.Debug|Win32.ActiveCfg = Debug|Win32 {2F35F228-6733-4FE5-9B46-B3AA10D4BC2E}.Debug|Win32.Build.0 = Debug|Win32 {2F35F228-6733-4FE5-9B46-B3AA10D4BC2E}.Debug|x64.ActiveCfg = Debug|x64 {2F35F228-6733-4FE5-9B46-B3AA10D4BC2E}.Debug|x64.Build.0 = Debug|x64 + {2F35F228-6733-4FE5-9B46-B3AA10D4BC2E}.Release|ARM64.ActiveCfg = Release|ARM64 + {2F35F228-6733-4FE5-9B46-B3AA10D4BC2E}.Release|ARM64.Build.0 = Release|ARM64 {2F35F228-6733-4FE5-9B46-B3AA10D4BC2E}.Release|Win32.ActiveCfg = Release|Win32 {2F35F228-6733-4FE5-9B46-B3AA10D4BC2E}.Release|Win32.Build.0 = Release|Win32 {2F35F228-6733-4FE5-9B46-B3AA10D4BC2E}.Release|x64.ActiveCfg = Release|x64 {2F35F228-6733-4FE5-9B46-B3AA10D4BC2E}.Release|x64.Build.0 = Release|x64 + {642BC75D-ABAF-403E-8224-7C725FD4CB42}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {642BC75D-ABAF-403E-8224-7C725FD4CB42}.Debug|ARM64.Build.0 = Debug|ARM64 {642BC75D-ABAF-403E-8224-7C725FD4CB42}.Debug|Win32.ActiveCfg = Debug|Win32 {642BC75D-ABAF-403E-8224-7C725FD4CB42}.Debug|Win32.Build.0 = Debug|Win32 {642BC75D-ABAF-403E-8224-7C725FD4CB42}.Debug|x64.ActiveCfg = Debug|x64 {642BC75D-ABAF-403E-8224-7C725FD4CB42}.Debug|x64.Build.0 = Debug|x64 + {642BC75D-ABAF-403E-8224-7C725FD4CB42}.Release|ARM64.ActiveCfg = Release|ARM64 + {642BC75D-ABAF-403E-8224-7C725FD4CB42}.Release|ARM64.Build.0 = Release|ARM64 {642BC75D-ABAF-403E-8224-7C725FD4CB42}.Release|Win32.ActiveCfg = Release|Win32 {642BC75D-ABAF-403E-8224-7C725FD4CB42}.Release|Win32.Build.0 = Release|Win32 {642BC75D-ABAF-403E-8224-7C725FD4CB42}.Release|x64.ActiveCfg = Release|x64 {642BC75D-ABAF-403E-8224-7C725FD4CB42}.Release|x64.Build.0 = Release|x64 + {93F10526-209E-41D7-BBEA-775787876895}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {93F10526-209E-41D7-BBEA-775787876895}.Debug|ARM64.Build.0 = Debug|ARM64 {93F10526-209E-41D7-BBEA-775787876895}.Debug|Win32.ActiveCfg = Debug|Win32 {93F10526-209E-41D7-BBEA-775787876895}.Debug|Win32.Build.0 = Debug|Win32 {93F10526-209E-41D7-BBEA-775787876895}.Debug|x64.ActiveCfg = Debug|x64 {93F10526-209E-41D7-BBEA-775787876895}.Debug|x64.Build.0 = Debug|x64 + {93F10526-209E-41D7-BBEA-775787876895}.Release|ARM64.ActiveCfg = Release|ARM64 + {93F10526-209E-41D7-BBEA-775787876895}.Release|ARM64.Build.0 = Release|ARM64 {93F10526-209E-41D7-BBEA-775787876895}.Release|Win32.ActiveCfg = Release|Win32 {93F10526-209E-41D7-BBEA-775787876895}.Release|Win32.Build.0 = Release|Win32 {93F10526-209E-41D7-BBEA-775787876895}.Release|x64.ActiveCfg = Release|x64 {93F10526-209E-41D7-BBEA-775787876895}.Release|x64.Build.0 = Release|x64 + {63F9B82B-F589-4082-ABE5-D4F0682050AB}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {63F9B82B-F589-4082-ABE5-D4F0682050AB}.Debug|ARM64.Build.0 = Debug|ARM64 {63F9B82B-F589-4082-ABE5-D4F0682050AB}.Debug|Win32.ActiveCfg = Debug|Win32 {63F9B82B-F589-4082-ABE5-D4F0682050AB}.Debug|Win32.Build.0 = Debug|Win32 {63F9B82B-F589-4082-ABE5-D4F0682050AB}.Debug|x64.ActiveCfg = Debug|x64 {63F9B82B-F589-4082-ABE5-D4F0682050AB}.Debug|x64.Build.0 = Debug|x64 + {63F9B82B-F589-4082-ABE5-D4F0682050AB}.Release|ARM64.ActiveCfg = Release|ARM64 + {63F9B82B-F589-4082-ABE5-D4F0682050AB}.Release|ARM64.Build.0 = Release|ARM64 {63F9B82B-F589-4082-ABE5-D4F0682050AB}.Release|Win32.ActiveCfg = Release|Win32 {63F9B82B-F589-4082-ABE5-D4F0682050AB}.Release|Win32.Build.0 = Release|Win32 {63F9B82B-F589-4082-ABE5-D4F0682050AB}.Release|x64.ActiveCfg = Release|x64 {63F9B82B-F589-4082-ABE5-D4F0682050AB}.Release|x64.Build.0 = Release|x64 + {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751}.Debug|ARM64.Build.0 = Debug|ARM64 {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751}.Debug|Win32.ActiveCfg = Debug|Win32 {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751}.Debug|Win32.Build.0 = Debug|Win32 {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751}.Debug|x64.ActiveCfg = Debug|x64 {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751}.Debug|x64.Build.0 = Debug|x64 + {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751}.Release|ARM64.ActiveCfg = Release|ARM64 + {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751}.Release|ARM64.Build.0 = Release|ARM64 {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751}.Release|Win32.ActiveCfg = Release|Win32 {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751}.Release|Win32.Build.0 = Release|Win32 {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751}.Release|x64.ActiveCfg = Release|x64 {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751}.Release|x64.Build.0 = Release|x64 + {BAA70D0F-3EC7-4D10-91F0-974F1F49308B}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {BAA70D0F-3EC7-4D10-91F0-974F1F49308B}.Debug|ARM64.Build.0 = Debug|ARM64 {BAA70D0F-3EC7-4D10-91F0-974F1F49308B}.Debug|Win32.ActiveCfg = Debug|Win32 {BAA70D0F-3EC7-4D10-91F0-974F1F49308B}.Debug|Win32.Build.0 = Debug|Win32 {BAA70D0F-3EC7-4D10-91F0-974F1F49308B}.Debug|x64.ActiveCfg = Debug|x64 {BAA70D0F-3EC7-4D10-91F0-974F1F49308B}.Debug|x64.Build.0 = Debug|x64 + {BAA70D0F-3EC7-4D10-91F0-974F1F49308B}.Release|ARM64.ActiveCfg = Release|ARM64 + {BAA70D0F-3EC7-4D10-91F0-974F1F49308B}.Release|ARM64.Build.0 = Release|ARM64 {BAA70D0F-3EC7-4D10-91F0-974F1F49308B}.Release|Win32.ActiveCfg = Release|Win32 {BAA70D0F-3EC7-4D10-91F0-974F1F49308B}.Release|Win32.Build.0 = Release|Win32 {BAA70D0F-3EC7-4D10-91F0-974F1F49308B}.Release|x64.ActiveCfg = Release|x64 {BAA70D0F-3EC7-4D10-91F0-974F1F49308B}.Release|x64.Build.0 = Release|x64 + {1693F852-A207-4348-8223-222C2A7FEEEB}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {1693F852-A207-4348-8223-222C2A7FEEEB}.Debug|ARM64.Build.0 = Debug|ARM64 {1693F852-A207-4348-8223-222C2A7FEEEB}.Debug|Win32.ActiveCfg = Debug|Win32 {1693F852-A207-4348-8223-222C2A7FEEEB}.Debug|Win32.Build.0 = Debug|Win32 {1693F852-A207-4348-8223-222C2A7FEEEB}.Debug|x64.ActiveCfg = Debug|x64 {1693F852-A207-4348-8223-222C2A7FEEEB}.Debug|x64.Build.0 = Debug|x64 + {1693F852-A207-4348-8223-222C2A7FEEEB}.Release|ARM64.ActiveCfg = Release|ARM64 + {1693F852-A207-4348-8223-222C2A7FEEEB}.Release|ARM64.Build.0 = Release|ARM64 {1693F852-A207-4348-8223-222C2A7FEEEB}.Release|Win32.ActiveCfg = Release|Win32 {1693F852-A207-4348-8223-222C2A7FEEEB}.Release|Win32.Build.0 = Release|Win32 {1693F852-A207-4348-8223-222C2A7FEEEB}.Release|x64.ActiveCfg = Release|x64 {1693F852-A207-4348-8223-222C2A7FEEEB}.Release|x64.Build.0 = Release|x64 + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Debug|ARM64.Build.0 = Debug|ARM64 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Debug|Win32.ActiveCfg = Debug|Win32 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Debug|Win32.Build.0 = Debug|Win32 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Debug|x64.ActiveCfg = Debug|x64 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Debug|x64.Build.0 = Debug|x64 + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Release|ARM64.ActiveCfg = Release|ARM64 + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Release|ARM64.Build.0 = Release|ARM64 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Release|Win32.ActiveCfg = Release|Win32 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Release|Win32.Build.0 = Release|Win32 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Release|x64.ActiveCfg = Release|x64 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Release|x64.Build.0 = Release|x64 + {503AE687-C33A-45ED-93AA-83967E176D67}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {503AE687-C33A-45ED-93AA-83967E176D67}.Debug|ARM64.Build.0 = Debug|ARM64 {503AE687-C33A-45ED-93AA-83967E176D67}.Debug|Win32.ActiveCfg = Debug|Win32 {503AE687-C33A-45ED-93AA-83967E176D67}.Debug|Win32.Build.0 = Debug|Win32 {503AE687-C33A-45ED-93AA-83967E176D67}.Debug|x64.ActiveCfg = Debug|x64 {503AE687-C33A-45ED-93AA-83967E176D67}.Debug|x64.Build.0 = Debug|x64 + {503AE687-C33A-45ED-93AA-83967E176D67}.Release|ARM64.ActiveCfg = Release|ARM64 + {503AE687-C33A-45ED-93AA-83967E176D67}.Release|ARM64.Build.0 = Release|ARM64 {503AE687-C33A-45ED-93AA-83967E176D67}.Release|Win32.ActiveCfg = Release|Win32 {503AE687-C33A-45ED-93AA-83967E176D67}.Release|Win32.Build.0 = Release|Win32 {503AE687-C33A-45ED-93AA-83967E176D67}.Release|x64.ActiveCfg = Release|x64 {503AE687-C33A-45ED-93AA-83967E176D67}.Release|x64.Build.0 = Release|x64 + {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Debug|ARM64.Build.0 = Debug|ARM64 {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Debug|Win32.ActiveCfg = Debug|Win32 {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Debug|Win32.Build.0 = Debug|Win32 {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Debug|x64.ActiveCfg = Debug|x64 {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Debug|x64.Build.0 = Debug|x64 + {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Release|ARM64.ActiveCfg = Release|ARM64 + {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Release|ARM64.Build.0 = Release|ARM64 {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Release|Win32.ActiveCfg = Release|Win32 {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Release|Win32.Build.0 = Release|Win32 {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Release|x64.ActiveCfg = Release|x64 {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Release|x64.Build.0 = Release|x64 + {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037}.Debug|ARM64.Build.0 = Debug|ARM64 {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037}.Debug|Win32.ActiveCfg = Debug|Win32 {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037}.Debug|Win32.Build.0 = Debug|Win32 {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037}.Debug|x64.ActiveCfg = Debug|x64 {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037}.Debug|x64.Build.0 = Debug|x64 + {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037}.Release|ARM64.ActiveCfg = Release|ARM64 + {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037}.Release|ARM64.Build.0 = Release|ARM64 {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037}.Release|Win32.ActiveCfg = Release|Win32 {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037}.Release|Win32.Build.0 = Release|Win32 {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037}.Release|x64.ActiveCfg = Release|x64 diff --git a/sys/windows/vs/NetHack/NetHack.vcxproj b/sys/windows/vs/NetHack/NetHack.vcxproj index 15bf8e093..65e2d88f8 100644 --- a/sys/windows/vs/NetHack/NetHack.vcxproj +++ b/sys/windows/vs/NetHack/NetHack.vcxproj @@ -9,12 +9,38 @@ - - {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751} - Win32Proj - NetHack - 10.0 - + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751} + Win32Proj + NetHack + 10.0 + $(BinDir) @@ -300,4 +326,4 @@ - + \ No newline at end of file diff --git a/sys/windows/vs/NetHackW/NetHackW.vcxproj b/sys/windows/vs/NetHackW/NetHackW.vcxproj index 7414e83de..5a7e0c2e1 100644 --- a/sys/windows/vs/NetHackW/NetHackW.vcxproj +++ b/sys/windows/vs/NetHackW/NetHackW.vcxproj @@ -1,6 +1,32 @@ + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + {CEC5D360-8804-454F-8591-002184C23499} NetHackW @@ -42,10 +68,17 @@ false + + false + false false + + false + false + /Gs /Oi- /w44774 /w45262 %(AdditionalOptions) @@ -64,8 +97,10 @@ 0x0409 $(SndWavDir);%(AdditionalIncludeDirectories) $(SndWavDir);%(AdditionalIncludeDirectories) + $(SndWavDir);%(AdditionalIncludeDirectories) $(SndWavDir);%(AdditionalIncludeDirectories) $(SndWavDir);%(AdditionalIncludeDirectories) + $(SndWavDir);%(AdditionalIncludeDirectories) Windows @@ -144,7 +179,9 @@ 4100;4244;4245;4310;4706;4820;4324 + 4100;4244;4245;4310;4706;4820;4324 4100;4244;4245;4310;4706;4820;4324 + 4100;4244;4245;4310;4706;4820;4324 @@ -215,7 +252,9 @@ 4100;4201;4244;4245;4310;4706;4820;4324 + 4100;4201;4244;4245;4310;4706;4820;4324 4100;4201;4244;4245;4310;4706;4820;4324 + 4100;4201;4244;4245;4310;4706;4820;4324 4100;4201;4244;4245;4310;4706;4820;4324 4100;4201;4244;4245;4310;4706;4820 @@ -369,4 +408,4 @@ - + \ No newline at end of file diff --git a/sys/windows/vs/PDCurses/PDCurses.vcxproj b/sys/windows/vs/PDCurses/PDCurses.vcxproj index 90550816c..10f9712ec 100644 --- a/sys/windows/vs/PDCurses/PDCurses.vcxproj +++ b/sys/windows/vs/PDCurses/PDCurses.vcxproj @@ -2,6 +2,32 @@ + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + {BAA70D0F-3EC7-4D10-91F0-974F1F49308B} Win32Proj @@ -34,7 +60,9 @@ 4996;4244;4267;%(DisableSpecificWarnings) + 4996;4244;4267;%(DisableSpecificWarnings) 4996;4244;4267;%(DisableSpecificWarnings) + 4996;4244;4267;%(DisableSpecificWarnings) @@ -56,11 +84,15 @@ 4996;4244;4267;%(DisableSpecificWarnings) + 4996;4244;4267;%(DisableSpecificWarnings) 4996;4244;4267;%(DisableSpecificWarnings) + 4996;4244;4267;%(DisableSpecificWarnings) 4996;4244;4267;%(DisableSpecificWarnings) + 4996;4244;4267;%(DisableSpecificWarnings) 4996;4244;4267;%(DisableSpecificWarnings) + 4996;4244;4267;%(DisableSpecificWarnings) @@ -100,6 +132,12 @@ _DEBUG;_LIB;PDC_RGB;%(PreprocessorDefinitions) + + + Disabled + _DEBUG;_LIB;PDC_RGB;%(PreprocessorDefinitions) + + MaxSpeed @@ -124,5 +162,17 @@ true + + + MaxSpeed + true + true + NDEBUG;_LIB;PDC_RGB;%(PreprocessorDefinitions) + + + true + true + + \ No newline at end of file diff --git a/sys/windows/vs/PDCursesGui/pdcursesgui.vcxproj b/sys/windows/vs/PDCursesGui/pdcursesgui.vcxproj index 1c62705f8..e709ade6d 100644 --- a/sys/windows/vs/PDCursesGui/pdcursesgui.vcxproj +++ b/sys/windows/vs/PDCursesGui/pdcursesgui.vcxproj @@ -2,6 +2,32 @@ + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + {1693F852-A207-4348-8223-222C2A7FEEEB} Win32Proj @@ -34,7 +60,9 @@ 4996;4244;4267;%(DisableSpecificWarnings) + 4996;4244;4267;%(DisableSpecificWarnings) 4996;4244;4267;%(DisableSpecificWarnings) + 4996;4244;4267;%(DisableSpecificWarnings) @@ -94,6 +122,12 @@ _DEBUG;_LIB;PDC_WIDE;PDC_RGB;%(PreprocessorDefinitions) + + + Disabled + _DEBUG;_LIB;PDC_WIDE;PDC_RGB;%(PreprocessorDefinitions) + + MaxSpeed @@ -118,5 +152,17 @@ true + + + MaxSpeed + true + true + NDEBUG;_LIB;PDC_WIDE;PDC_RGB;%(PreprocessorDefinitions) + + + true + true + + \ No newline at end of file diff --git a/sys/windows/vs/dlb/dlb.vcxproj b/sys/windows/vs/dlb/dlb.vcxproj index 6b91e9b3f..44a1dad6a 100644 --- a/sys/windows/vs/dlb/dlb.vcxproj +++ b/sys/windows/vs/dlb/dlb.vcxproj @@ -2,6 +2,32 @@ + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC} 10.0 @@ -15,12 +41,18 @@ + + + + + + @@ -35,7 +67,9 @@ $(ToolsDir);%(AdditionalLibraryDirectories) + $(ToolsDir);%(AdditionalLibraryDirectories) hacklib.lib;%(AdditionalDependencies) + hacklib.lib;%(AdditionalDependencies) $(ToolsDir);%(AdditionalLibraryDirectories) @@ -47,7 +81,9 @@ $(ToolsDir);%(AdditionalLibraryDirectories) + $(ToolsDir);%(AdditionalLibraryDirectories) hacklib.lib;%(AdditionalDependencies) + hacklib.lib;%(AdditionalDependencies) @@ -68,4 +104,4 @@ - + \ No newline at end of file diff --git a/sys/windows/vs/fetchctags/fetchctags.vcxproj b/sys/windows/vs/fetchctags/fetchctags.vcxproj index 58df9ec83..8bc71cdb4 100644 --- a/sys/windows/vs/fetchctags/fetchctags.vcxproj +++ b/sys/windows/vs/fetchctags/fetchctags.vcxproj @@ -1,10 +1,18 @@ + + Debug + ARM64 + Debug Win32 + + Release + ARM64 + Release Win32 @@ -39,10 +47,18 @@ Makefile true + + Makefile + true + Makefile false + + Makefile + false + @@ -67,5 +83,4 @@ - - + \ No newline at end of file diff --git a/sys/windows/vs/hacklib/hacklib.vcxproj b/sys/windows/vs/hacklib/hacklib.vcxproj index 6c439c101..a708c1e3a 100644 --- a/sys/windows/vs/hacklib/hacklib.vcxproj +++ b/sys/windows/vs/hacklib/hacklib.vcxproj @@ -13,10 +13,18 @@ 10.0 + + Debug + ARM64 + Debug Win32 + + Release + ARM64 + Release Win32 @@ -69,6 +77,12 @@ Unicode v143 + + StaticLibrary + true + Unicode + v143 + StaticLibrary false @@ -76,6 +90,13 @@ Unicode v143 + + StaticLibrary + false + true + Unicode + v143 + @@ -90,9 +111,15 @@ + + + + + + @@ -146,6 +173,22 @@ true + + + Level3 + true + _DEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) + true + $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) + stdclatest + /w45262 %(AdditionalOptions) + + + + + true + + Level3 @@ -166,6 +209,26 @@ true + + + Level3 + true + true + true + NDEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) + true + $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) + stdclatest + /w45262 %(AdditionalOptions) + + + + + true + true + true + + diff --git a/sys/windows/vs/lualib/lualib.vcxproj b/sys/windows/vs/lualib/lualib.vcxproj index d244e8eb4..b004fcbfb 100644 --- a/sys/windows/vs/lualib/lualib.vcxproj +++ b/sys/windows/vs/lualib/lualib.vcxproj @@ -13,10 +13,18 @@ 10.0 + + Debug + ARM64 + Debug Win32 + + Release + ARM64 + Release Win32 @@ -95,6 +103,12 @@ Unicode v143 + + StaticLibrary + true + Unicode + v143 + StaticLibrary false @@ -102,6 +116,13 @@ Unicode v143 + + StaticLibrary + false + true + Unicode + v143 + @@ -116,9 +137,15 @@ + + + + + + @@ -169,6 +196,21 @@ true + + + Level3 + true + _DEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) + true + $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) + /Gs /Oi- /w44774 %(AdditionalOptions) + + + + + true + + Level3 @@ -188,6 +230,25 @@ true + + + Level3 + true + true + true + NDEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) + true + $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) + /Gs /Oi- /w44774 %(AdditionalOptions) + + + + + true + true + true + + diff --git a/sys/windows/vs/makedefs/makedefs.vcxproj b/sys/windows/vs/makedefs/makedefs.vcxproj index 235c390e2..b832056f9 100644 --- a/sys/windows/vs/makedefs/makedefs.vcxproj +++ b/sys/windows/vs/makedefs/makedefs.vcxproj @@ -2,6 +2,32 @@ + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + {BA3DD34C-04B7-40D0-B373-9329AA9E8945} 10.0 @@ -15,12 +41,18 @@ + + + + + + @@ -68,4 +100,4 @@ - + \ No newline at end of file diff --git a/sys/windows/vs/package/package.vcxproj b/sys/windows/vs/package/package.vcxproj index 7b09e9277..ef1c3c9ce 100644 --- a/sys/windows/vs/package/package.vcxproj +++ b/sys/windows/vs/package/package.vcxproj @@ -1,10 +1,18 @@ + + Debug + ARM64 + Debug Win32 + + Release + ARM64 + Release Win32 @@ -40,10 +48,18 @@ Makefile true + + Makefile + true + Makefile false + + Makefile + false + @@ -54,11 +70,11 @@ - pushd $(vsDir)package %26%26 nmake -F package.nmake BinDir="$(BinDir)\" Platform="$(Platform)" PlatformShortName="$(PlatformShortName)" Configuration="$(Configuration)" NETHACK_VERSION="$(NETHACK_VERSION)" %26%26 popd - pushd $(vsDir)package %26%26 nmake -F package.nmake clean %26%26 popd - pushd $(vsDir)package %26%26 nmake -F package.nmake rebuild %26%26 popd + pushd $(vsDir)package %26%26 nmake -F package.nmake BinDir="$(BinDir)\" Platform="$(Platform)" PlatformShortName="$(PlatformShortName)" Configuration="$(Configuration)" NETHACK_VERSION="$(NETHACK_VERSION)" %26%26 popd + pushd $(vsDir)package %26%26 nmake -F package.nmake clean %26%26 popd + pushd $(vsDir)package %26%26 nmake -F package.nmake rebuild %26%26 popd - + \ No newline at end of file diff --git a/sys/windows/vs/recover/recover.vcxproj b/sys/windows/vs/recover/recover.vcxproj index eceda86fb..1fe6f0a16 100644 --- a/sys/windows/vs/recover/recover.vcxproj +++ b/sys/windows/vs/recover/recover.vcxproj @@ -2,6 +2,32 @@ + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + {2F35F228-6733-4FE5-9B46-B3AA10D4BC2E} 10.0 @@ -53,4 +79,4 @@ - + \ No newline at end of file diff --git a/sys/windows/vs/sfctool.sln b/sys/windows/vs/sfctool.sln index 06ef30f69..3bf333c50 100644 --- a/sys/windows/vs/sfctool.sln +++ b/sys/windows/vs/sfctool.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.13.35913.81 +# Visual Studio Version 18 +VisualStudioVersion = 18.0.11205.157 d18.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sfctool", "sfctool\sfctool.vcxproj", "{3BFA3C14-6DA2-4750-B1C6-028B9BCE825E}" ProjectSection(ProjectDependencies) = postProject @@ -20,40 +20,58 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hacklib", "hacklib\hacklib. EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 + Release|ARM64 = Release|ARM64 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3BFA3C14-6DA2-4750-B1C6-028B9BCE825E}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {3BFA3C14-6DA2-4750-B1C6-028B9BCE825E}.Debug|ARM64.Build.0 = Debug|ARM64 {3BFA3C14-6DA2-4750-B1C6-028B9BCE825E}.Debug|x64.ActiveCfg = Debug|x64 {3BFA3C14-6DA2-4750-B1C6-028B9BCE825E}.Debug|x64.Build.0 = Debug|x64 {3BFA3C14-6DA2-4750-B1C6-028B9BCE825E}.Debug|x86.ActiveCfg = Debug|Win32 {3BFA3C14-6DA2-4750-B1C6-028B9BCE825E}.Debug|x86.Build.0 = Debug|Win32 + {3BFA3C14-6DA2-4750-B1C6-028B9BCE825E}.Release|ARM64.ActiveCfg = Release|ARM64 + {3BFA3C14-6DA2-4750-B1C6-028B9BCE825E}.Release|ARM64.Build.0 = Release|ARM64 {3BFA3C14-6DA2-4750-B1C6-028B9BCE825E}.Release|x64.ActiveCfg = Release|x64 {3BFA3C14-6DA2-4750-B1C6-028B9BCE825E}.Release|x64.Build.0 = Release|x64 {3BFA3C14-6DA2-4750-B1C6-028B9BCE825E}.Release|x86.ActiveCfg = Release|Win32 {3BFA3C14-6DA2-4750-B1C6-028B9BCE825E}.Release|x86.Build.0 = Release|Win32 + {628C594A-7565-4366-9FCA-41DB67C6B615}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {628C594A-7565-4366-9FCA-41DB67C6B615}.Debug|ARM64.Build.0 = Debug|ARM64 {628C594A-7565-4366-9FCA-41DB67C6B615}.Debug|x64.ActiveCfg = Debug|x64 {628C594A-7565-4366-9FCA-41DB67C6B615}.Debug|x64.Build.0 = Debug|x64 {628C594A-7565-4366-9FCA-41DB67C6B615}.Debug|x86.ActiveCfg = Debug|Win32 {628C594A-7565-4366-9FCA-41DB67C6B615}.Debug|x86.Build.0 = Debug|Win32 + {628C594A-7565-4366-9FCA-41DB67C6B615}.Release|ARM64.ActiveCfg = Release|ARM64 + {628C594A-7565-4366-9FCA-41DB67C6B615}.Release|ARM64.Build.0 = Release|ARM64 {628C594A-7565-4366-9FCA-41DB67C6B615}.Release|x64.ActiveCfg = Release|x64 {628C594A-7565-4366-9FCA-41DB67C6B615}.Release|x64.Build.0 = Release|x64 {628C594A-7565-4366-9FCA-41DB67C6B615}.Release|x86.ActiveCfg = Release|Win32 {628C594A-7565-4366-9FCA-41DB67C6B615}.Release|x86.Build.0 = Release|Win32 + {AAE63AD3-8185-4E19-B6A3-13F4CB891AAD}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {AAE63AD3-8185-4E19-B6A3-13F4CB891AAD}.Debug|ARM64.Build.0 = Debug|ARM64 {AAE63AD3-8185-4E19-B6A3-13F4CB891AAD}.Debug|x64.ActiveCfg = Debug|x64 {AAE63AD3-8185-4E19-B6A3-13F4CB891AAD}.Debug|x64.Build.0 = Debug|x64 {AAE63AD3-8185-4E19-B6A3-13F4CB891AAD}.Debug|x86.ActiveCfg = Debug|Win32 {AAE63AD3-8185-4E19-B6A3-13F4CB891AAD}.Debug|x86.Build.0 = Debug|Win32 + {AAE63AD3-8185-4E19-B6A3-13F4CB891AAD}.Release|ARM64.ActiveCfg = Release|ARM64 + {AAE63AD3-8185-4E19-B6A3-13F4CB891AAD}.Release|ARM64.Build.0 = Release|ARM64 {AAE63AD3-8185-4E19-B6A3-13F4CB891AAD}.Release|x64.ActiveCfg = Release|x64 {AAE63AD3-8185-4E19-B6A3-13F4CB891AAD}.Release|x64.Build.0 = Release|x64 {AAE63AD3-8185-4E19-B6A3-13F4CB891AAD}.Release|x86.ActiveCfg = Release|Win32 {AAE63AD3-8185-4E19-B6A3-13F4CB891AAD}.Release|x86.Build.0 = Release|Win32 + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Debug|ARM64.Build.0 = Debug|ARM64 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Debug|x64.ActiveCfg = Debug|x64 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Debug|x64.Build.0 = Debug|x64 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Debug|x86.ActiveCfg = Debug|Win32 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Debug|x86.Build.0 = Debug|Win32 + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Release|ARM64.ActiveCfg = Release|ARM64 + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Release|ARM64.Build.0 = Release|ARM64 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Release|x64.ActiveCfg = Release|x64 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Release|x64.Build.0 = Release|x64 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Release|x86.ActiveCfg = Release|Win32 diff --git a/sys/windows/vs/sfctool/sfctool.vcxproj b/sys/windows/vs/sfctool/sfctool.vcxproj index e46b1d9bc..5dff90066 100644 --- a/sys/windows/vs/sfctool/sfctool.vcxproj +++ b/sys/windows/vs/sfctool/sfctool.vcxproj @@ -11,10 +11,18 @@ $(BinDir) + + Debug + ARM64 + Debug Win32 + + Release + ARM64 + Release Win32 @@ -95,6 +103,12 @@ v143 Unicode + + Application + true + v143 + Unicode + Application false @@ -102,6 +116,13 @@ true Unicode + + Application + false + v143 + true + Unicode + @@ -116,9 +137,15 @@ + + + + + + @@ -172,6 +199,22 @@ hacklib.lib;userenv.lib;advapi32.lib;%(AdditionalDependencies) + + + $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + Level3 + stdclatest + true + true + + + Console + true + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;userenv.lib;advapi32.lib;%(AdditionalDependencies) + + $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) @@ -192,6 +235,26 @@ hacklib.lib;userenv.lib;advapi32.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) + WIN32;_NDEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + Level3 + stdclatest + true + true + true + true + + + Console + true + true + true + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;userenv.lib;advapi32.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + diff --git a/sys/windows/vs/sftags/sftags.vcxproj b/sys/windows/vs/sftags/sftags.vcxproj index e40df7824..43f024c24 100644 --- a/sys/windows/vs/sftags/sftags.vcxproj +++ b/sys/windows/vs/sftags/sftags.vcxproj @@ -8,10 +8,18 @@ + + Debug + ARM64 + Debug Win32 + + Release + ARM64 + Release Win32 @@ -55,6 +63,12 @@ v143 Unicode + + Application + true + v143 + Unicode + Application false @@ -62,6 +76,13 @@ true Unicode + + Application + false + v143 + true + Unicode + @@ -76,9 +97,15 @@ + + + + + + @@ -91,8 +118,8 @@ Console true - $(ToolsDir);%(AdditionalLibraryDirectories) - hacklib.lib;%(AdditionalDependencies) + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;%(AdditionalDependencies) @@ -110,8 +137,8 @@ true true true - $(ToolsDir);%(AdditionalLibraryDirectories) - hacklib.lib;%(AdditionalDependencies) + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;%(AdditionalDependencies) @@ -125,8 +152,23 @@ Console true - $(ToolsDir);%(AdditionalLibraryDirectories) - hacklib.lib;%(AdditionalDependencies) + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;%(AdditionalDependencies) + + + + + $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + Level3 + true + true + + + Console + true + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;%(AdditionalDependencies) @@ -145,14 +187,34 @@ true true true - $(ToolsDir);%(AdditionalLibraryDirectories) - hacklib.lib;%(AdditionalDependencies) + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;%(AdditionalDependencies) + + + + + $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;%(AdditionalDependencies) - + @@ -161,4 +223,4 @@ - + \ No newline at end of file diff --git a/sys/windows/vs/tile2bmp/tile2bmp.vcxproj b/sys/windows/vs/tile2bmp/tile2bmp.vcxproj index 1bab9d61c..6ed724f2b 100644 --- a/sys/windows/vs/tile2bmp/tile2bmp.vcxproj +++ b/sys/windows/vs/tile2bmp/tile2bmp.vcxproj @@ -2,6 +2,32 @@ + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + {642BC75D-ABAF-403E-8224-7C725FD4CB42} 10.0 @@ -15,12 +41,18 @@ + + + + + + @@ -32,9 +64,9 @@ /w45262 %(AdditionalOptions) - $(ToolsDir);%(AdditionalLibraryDirectories) - hacklib.lib;%(AdditionalDependencies) - + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;%(AdditionalDependencies) + @@ -56,4 +88,4 @@ - + \ No newline at end of file diff --git a/sys/windows/vs/tilemap/tilemap.vcxproj b/sys/windows/vs/tilemap/tilemap.vcxproj index 7c99182b5..70ce0296b 100644 --- a/sys/windows/vs/tilemap/tilemap.vcxproj +++ b/sys/windows/vs/tilemap/tilemap.vcxproj @@ -2,6 +2,32 @@ + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + {93F10526-209E-41D7-BBEA-775787876895} 10.0 @@ -15,17 +41,24 @@ + + + + + + + $(IncDir);$(SysWindDir);$(SysShareDir);$(LuaDir);%(AdditionalIncludeDirectories) @@ -96,4 +129,4 @@ - + \ No newline at end of file diff --git a/sys/windows/vs/uudecode/uudecode.vcxproj b/sys/windows/vs/uudecode/uudecode.vcxproj index 0975cacdf..296d97ab8 100644 --- a/sys/windows/vs/uudecode/uudecode.vcxproj +++ b/sys/windows/vs/uudecode/uudecode.vcxproj @@ -2,6 +2,32 @@ + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + {63F9B82B-F589-4082-ABE5-D4F0682050AB} 10.0 @@ -15,12 +41,18 @@ + + + + + + From 3885638934ad49dd72ca1bfb73f25ec88a77d9c1 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 16 Nov 2025 14:12:14 -0500 Subject: [PATCH 085/442] update suppressed warnings in NetHack vcxproj Also be more consistent on preprocessor #define entries between Visual Studio builds and command line nmake Makefile builds. --- sys/windows/vs/NetHack/NetHack.vcxproj | 4 ++-- sys/windows/vs/NetHackW/NetHackW.vcxproj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sys/windows/vs/NetHack/NetHack.vcxproj b/sys/windows/vs/NetHack/NetHack.vcxproj index 65e2d88f8..3e2282bee 100644 --- a/sys/windows/vs/NetHack/NetHack.vcxproj +++ b/sys/windows/vs/NetHack/NetHack.vcxproj @@ -67,13 +67,13 @@ /Gs /Oi- /w44774 /w45262 %(AdditionalOptions) - 4100;4244;4245;4310;4706;4820;4324 + 4244;4245;4310;4706;4820;4324 Disabled Default Speed true $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);%(AdditionalIncludeDirectories) - WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) + WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;SND_SOUNDEFFECTS_AUTOMAP;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) stdclatest diff --git a/sys/windows/vs/NetHackW/NetHackW.vcxproj b/sys/windows/vs/NetHackW/NetHackW.vcxproj index 5a7e0c2e1..5ea4aafe5 100644 --- a/sys/windows/vs/NetHackW/NetHackW.vcxproj +++ b/sys/windows/vs/NetHackW/NetHackW.vcxproj @@ -82,11 +82,11 @@ /Gs /Oi- /w44774 /w45262 %(AdditionalOptions) - 4100;4244;4245;4310;4706;4820;4324 + 4244;4245;4310;4706;4820;4324 Disabled true $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);%(AdditionalIncludeDirectories) - TILES;_WINDOWS;DLB;MSWIN_GRAPHICS;SAFEPROCS;NOTTYGRAPHICS;SND_LIB_WINDSOUND;USER_SOUNDS;HAS_STDINT_H;HAS_INTTYPES_H;PDC_WIDE;%(PreprocessorDefinitions) + TILES;_WINDOWS;DLB;MSWIN_GRAPHICS;SAFEPROCS;NOTTYGRAPHICS;SND_LIB_WINDSOUND;USER_SOUNDS;SND_SOUNDEFFECTS_AUTOMAP;HAS_STDINT_H;HAS_INTTYPES_H;PDC_WIDE;%(PreprocessorDefinitions) stdclatest stdclatest stdclatest From 4f59f2448562c3024fc16e99d442e60ecf8491c9 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 16 Nov 2025 15:16:10 -0500 Subject: [PATCH 086/442] some vcxproj fixes --- sys/windows/vs/NetHack/NetHack.vcxproj | 2 +- sys/windows/vs/NetHackW/NetHackW.vcxproj | 4 ++-- sys/windows/vs/package/package.nmake | 4 ++-- sys/windows/vs/uudecode/afteruudecode.proj | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sys/windows/vs/NetHack/NetHack.vcxproj b/sys/windows/vs/NetHack/NetHack.vcxproj index 3e2282bee..0a53df82e 100644 --- a/sys/windows/vs/NetHack/NetHack.vcxproj +++ b/sys/windows/vs/NetHack/NetHack.vcxproj @@ -80,7 +80,7 @@ hacklib.lib;lualib.lib;kernel32.lib;dbghelp.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;winmm.lib;Winmm.lib;UserEnv.lib;bcrypt.lib;%(AdditionalDependencies) - $(SndWavDir);%(AdditionalIncludeDirectories) + $(SndWavDir);$(SysWindDir);%(AdditionalIncludeDirectories) diff --git a/sys/windows/vs/NetHackW/NetHackW.vcxproj b/sys/windows/vs/NetHackW/NetHackW.vcxproj index 5ea4aafe5..d6d7bdc37 100644 --- a/sys/windows/vs/NetHackW/NetHackW.vcxproj +++ b/sys/windows/vs/NetHackW/NetHackW.vcxproj @@ -96,10 +96,10 @@ NDEBUG;%(PreprocessorDefinitions) 0x0409 $(SndWavDir);%(AdditionalIncludeDirectories) - $(SndWavDir);%(AdditionalIncludeDirectories) + $(SndWavDir);$(SysWindDir);%(AdditionalIncludeDirectories) $(SndWavDir);%(AdditionalIncludeDirectories) $(SndWavDir);%(AdditionalIncludeDirectories) - $(SndWavDir);%(AdditionalIncludeDirectories) + $(SndWavDir);$(SysWindDir);%(AdditionalIncludeDirectories) $(SndWavDir);%(AdditionalIncludeDirectories) diff --git a/sys/windows/vs/package/package.nmake b/sys/windows/vs/package/package.nmake index fb7f127f6..8ac732240 100644 --- a/sys/windows/vs/package/package.nmake +++ b/sys/windows/vs/package/package.nmake @@ -91,11 +91,11 @@ $(DBGSYMZIP): $(PDBTOZIP) $(VSBINDIR)\license: $(BinDir)\license copy /Y $(BinDir)\license $@ $(VSBINDIR)\Guidebook.txt: $(BinDir)\Guidebook.txt - copy /Y $(BinDir)\Guidebook.txt $@ + copy /Y $(DocDir)\Guidebook.txt $@ $(VSBINDIR)\NetHack.exe: $(BinDir)\NetHack.exe copy /Y $(BinDir)\NetHack.exe $@ $(VSBINDIR)\NetHack.txt: $(BinDir)\NetHack.txt - copy /Y $(BinDir)\NetHack.txt $@ + copy /Y $(DocDir)\NetHack.txt $@ $(VSBINDIR)\NetHackW.exe: $(BinDir)\NetHackW.exe copy /Y $(BinDir)\NetHackW.exe $@ $(VSBINDIR)\opthelp: $(BinDir)\opthelp diff --git a/sys/windows/vs/uudecode/afteruudecode.proj b/sys/windows/vs/uudecode/afteruudecode.proj index 92286f678..e8b4da4c0 100644 --- a/sys/windows/vs/uudecode/afteruudecode.proj +++ b/sys/windows/vs/uudecode/afteruudecode.proj @@ -4,7 +4,6 @@ - @@ -74,6 +73,7 @@ + From f7904585878099d46e2016b84ea2066c5e69baea Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 16 Nov 2025 15:26:49 -0500 Subject: [PATCH 087/442] follow-up bit for NetHackW.vcxproj Remove a resource compiler architecture conditional, since it does not vary between the different architectures. This is simpler. --- sys/windows/vs/NetHackW/NetHackW.vcxproj | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/sys/windows/vs/NetHackW/NetHackW.vcxproj b/sys/windows/vs/NetHackW/NetHackW.vcxproj index d6d7bdc37..bcf2924b2 100644 --- a/sys/windows/vs/NetHackW/NetHackW.vcxproj +++ b/sys/windows/vs/NetHackW/NetHackW.vcxproj @@ -95,12 +95,7 @@ NDEBUG;%(PreprocessorDefinitions) 0x0409 - $(SndWavDir);%(AdditionalIncludeDirectories) - $(SndWavDir);$(SysWindDir);%(AdditionalIncludeDirectories) - $(SndWavDir);%(AdditionalIncludeDirectories) - $(SndWavDir);%(AdditionalIncludeDirectories) - $(SndWavDir);$(SysWindDir);%(AdditionalIncludeDirectories) - $(SndWavDir);%(AdditionalIncludeDirectories) + $(SndWavDir);%(AdditionalIncludeDirectories) Windows @@ -408,4 +403,4 @@ - \ No newline at end of file + From 043b867da78a64d95bb1573fb5c3d1bafe0358c3 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 16 Nov 2025 15:31:41 -0500 Subject: [PATCH 088/442] yet-another follow-up for NetHackW.vcxproj --- sys/windows/vs/NetHackW/NetHackW.vcxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/windows/vs/NetHackW/NetHackW.vcxproj b/sys/windows/vs/NetHackW/NetHackW.vcxproj index bcf2924b2..696436355 100644 --- a/sys/windows/vs/NetHackW/NetHackW.vcxproj +++ b/sys/windows/vs/NetHackW/NetHackW.vcxproj @@ -95,7 +95,7 @@ NDEBUG;%(PreprocessorDefinitions) 0x0409 - $(SndWavDir);%(AdditionalIncludeDirectories) + $(SndWavDir);$(SysWindDir);%(AdditionalIncludeDirectories) Windows From 7dbc0ac6b8b2e483764666ff7ba51f978beee665 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 16 Nov 2025 15:48:05 -0500 Subject: [PATCH 089/442] visual studio Makefile variable --- sys/windows/vs/package/package.nmake | 1 + 1 file changed, 1 insertion(+) diff --git a/sys/windows/vs/package/package.nmake b/sys/windows/vs/package/package.nmake index 8ac732240..dc13ea351 100644 --- a/sys/windows/vs/package/package.nmake +++ b/sys/windows/vs/package/package.nmake @@ -41,6 +41,7 @@ X11 = $(ROOTDIR)\win\X11 # X11 support files LIBDIR = $(ROOTDIR)\lib # libraries and external bits SUBMDIR = $(ROOTDIR)\submodules # NetHack git submodules SndWavDir = $(ROOTDIR)\sound\wav # sound files that get integrated +DocDir = $(ROOTDIR)\doc # Directories we might place collected things # From 8f97b824d56d86e95eab8408b679e7565bdd7c41 Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 17 Nov 2025 20:44:08 -0500 Subject: [PATCH 090/442] visual studio build update address warning disparity between ARM64 and x64 --- sys/windows/vs/NetHack/NetHack.vcxproj | 5 +++-- sys/windows/vs/NetHackW/NetHackW.vcxproj | 3 ++- sys/windows/vs/package/package.vcxproj | 8 ++++---- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/sys/windows/vs/NetHack/NetHack.vcxproj b/sys/windows/vs/NetHack/NetHack.vcxproj index 0a53df82e..d2ca44c61 100644 --- a/sys/windows/vs/NetHack/NetHack.vcxproj +++ b/sys/windows/vs/NetHack/NetHack.vcxproj @@ -67,7 +67,8 @@ /Gs /Oi- /w44774 /w45262 %(AdditionalOptions) - 4244;4245;4310;4706;4820;4324 + 4100;4244;4245;4310;4706;4820;4324 + 4244;4245;4310;4706;4820;4324 Disabled Default Speed @@ -326,4 +327,4 @@ - \ No newline at end of file + diff --git a/sys/windows/vs/NetHackW/NetHackW.vcxproj b/sys/windows/vs/NetHackW/NetHackW.vcxproj index 696436355..a3d74baa4 100644 --- a/sys/windows/vs/NetHackW/NetHackW.vcxproj +++ b/sys/windows/vs/NetHackW/NetHackW.vcxproj @@ -82,7 +82,8 @@ /Gs /Oi- /w44774 /w45262 %(AdditionalOptions) - 4244;4245;4310;4706;4820;4324 + 4100;4244;4245;4310;4706;4820;4324 + 4244;4245;4310;4706;4820;4324 Disabled true $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);%(AdditionalIncludeDirectories) diff --git a/sys/windows/vs/package/package.vcxproj b/sys/windows/vs/package/package.vcxproj index ef1c3c9ce..f0c0c660c 100644 --- a/sys/windows/vs/package/package.vcxproj +++ b/sys/windows/vs/package/package.vcxproj @@ -70,11 +70,11 @@ - pushd $(vsDir)package %26%26 nmake -F package.nmake BinDir="$(BinDir)\" Platform="$(Platform)" PlatformShortName="$(PlatformShortName)" Configuration="$(Configuration)" NETHACK_VERSION="$(NETHACK_VERSION)" %26%26 popd - pushd $(vsDir)package %26%26 nmake -F package.nmake clean %26%26 popd - pushd $(vsDir)package %26%26 nmake -F package.nmake rebuild %26%26 popd + pushd $(vsDir)package %26%26 nmake /NOLOGO -F package.nmake BinDir="$(BinDir)\" Platform="$(Platform)" PlatformShortName="$(PlatformShortName)" Configuration="$(Configuration)" NETHACK_VERSION="$(NETHACK_VERSION)" %26%26 popd + pushd $(vsDir)package %26%26 nmake /NOLOGO -F package.nmake clean %26%26 popd + pushd $(vsDir)package %26%26 nmake /NOLOGO -F package.nmake rebuild %26%26 popd - \ No newline at end of file + From a6801b004c4c4e68bd3378a7589b0686cf9ef6b4 Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 17 Nov 2025 21:02:45 -0500 Subject: [PATCH 091/442] use lowercase platform name in zip file name --- sys/windows/vs/package/package.nmake | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sys/windows/vs/package/package.nmake b/sys/windows/vs/package/package.nmake index dc13ea351..55b3e8e02 100644 --- a/sys/windows/vs/package/package.nmake +++ b/sys/windows/vs/package/package.nmake @@ -22,6 +22,7 @@ NHV=$(NHV:"=) #SUBMSDIR=submodules # NetHack git submodules PACKAGESDIR=vspackage # put in vspackage to distinguish ROOTDIR=..\..\..\.. # root of NetHack tree relative to project file +PlatformFileName=$(lowercase $(PlatformShortName)) # Directories we might have to collect things from # @@ -74,8 +75,8 @@ FILESTOZIP=$(VSBINDIR)\Guidebook.txt $(VSBINDIR)\license \ DBGSYMS = NetHack.PDB NetHackW.PDB PDBTOZIP = ..\NetHack\symbols\$(Configuration)\$(Platform)\NetHack.PDB \ ..\NetHackW\symbols\$(Configuration)\$(Platform)\NetHackW.PDB -MAINZIP = $(VSPACKAGEDIR)\nethack-$(NHV)-win-$(PlatformShortName).zip -DBGSYMZIP = $(VSPACKAGEDIR)\nethack-$(NHV)-win-$(PlatformShortName)-debugsymbols.zip +MAINZIP = $(VSPACKAGEDIR)\nethack-$(NHV)-win-$(PlatformFileName).zip +DBGSYMZIP = $(VSPACKAGEDIR)\nethack-$(NHV)-win-$(PlatformFileName)-debugsymbols.zip packageall: packagezip From b1cffe11f9faa2b5797a97ce9e30c9e5932a6cf4 Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 18 Nov 2025 10:10:32 -0500 Subject: [PATCH 092/442] Cross-compiling on Windows 11 with Visual Studio Cross-compiling NetHack with Visual Studio from an x64 platform to an ARM64 target presents some new build challenges. In the current nethack.sln solution, the build attempts to execute the several just-built tools during the build of various subprojects. For example, when cross-compiling on a typical Windows 11 x64 machine to build a target ARM64 Windows 11 package, the build process tries to run the following just-built target tools: (under a Debug build) "$(ToolsDir)\Debug\ARM64\uudecode.exe" "$(ToolsDir)\Debug\ARM64\makedefs.exe" "$(ToolsDir)\Debug\ARM64\tilemap.exe" "$(ToolsDir)\Debug\ARM64\tile2bmp.exe" "$(ToolsDir)\Debug\ARM64\dlb.exe" (under a Release build) "$(ToolsDir)\Release\ARM64\uudecode.exe" "$(ToolsDir)\Release\ARM64\makedefs.exe" "$(ToolsDir)\Release\ARM64\tilemap.exe" "$(ToolsDir)\Release\ARM64\tile2bmp.exe" "$(ToolsDir)\Release\ARM64\dlb.exe" Those fail to execute successfully on Intel x64 (or x86) since they are actually ARM64 executables, and the build attempts to execute them on the host Intel x64 hardware. The situation is a little different if the cross-compile is carried out on a Windows 11 ARM64 machine (such as SnapDragon). On an ARM64 machine, the cross-compile to build a target Intel x64 Windows 11 package, tries to execute the following: (under a Debug build) "$(ToolsDir)\Debug\x64\uudecode.exe" "$(ToolsDir)\Debug\x64\makedefs.exe" "$(ToolsDir)\Debug\x64\tilemap.exe" "$(ToolsDir)\Debug\x64\tile2bmp.exe" "$(ToolsDir)\Debug\x64\dlb.exe" (under a Release build) "$(ToolsDir)\Release\x64\uudecode.exe" "$(ToolsDir)\Release\x64\makedefs.exe" "$(ToolsDir)\Release\x64\tilemap.exe" "$(ToolsDir)\Release\x64\tile2bmp.exe" "$(ToolsDir)\Release\x64\dlb.exe" Those actual do succeed in executing on ARM64, because of the "prism emulation" that is available on Windows 11 ARM64 operating systems to allow x64 and x86 executables to run. The following change adds some detection to build environment, leading to the definition of a "HostTools" macro that leads to the native host tools for those steps. There is a catch: It means that the native build of the interim tools for Windows 11 must be executed prior to attempting a cross-compile build to a non-native target. That ensures that the native x64 interim uudecode, makedefs, tilemap, tile2bmp and dlb tools are available on the disk for use by a subsequent cross-compile. This change consistently switches to the use of the host-native interim tools for uudecode, makedefs, tilemap, tile2bmp and dlb tools. Technically, this change would not be strictly necessary on an ARM64-hosted build that was targeting x64 or x86, because of the available prism-emulation that allows ARM64, x64, and x86 images to execute, but this maintains consistency of the build process on either platform. It is also likely that the native host versions execute more quickly than versions requiring the prism emulation, although that isn't really a concern for a NetHack build. The use of native host uudecode, makedefs, tilemap, tile2bmp and dlb tools is done with the Unix-hosted cross-compiles to other target platforms as well (on Linux or macOS). --- sys/windows/vs/dirs.props | 21 +++ sys/windows/vs/dlb/afterdlb.proj | 4 +- sys/windows/vs/makedefs/aftermakedefs.proj | 10 +- sys/windows/vs/tile2bmp/aftertile2bmp.proj | 4 +- sys/windows/vs/tilemap/aftertilemap.proj | 4 +- sys/windows/vs/uudecode/afteruudecode.proj | 142 ++++++++++----------- 6 files changed, 103 insertions(+), 82 deletions(-) diff --git a/sys/windows/vs/dirs.props b/sys/windows/vs/dirs.props index 8f1f17062..f481482b1 100644 --- a/sys/windows/vs/dirs.props +++ b/sys/windows/vs/dirs.props @@ -40,4 +40,25 @@ $(SubmodulesDir)pdcursesmod\ $(PDCURSESMOD) + + + + + ..\ARM64\ + + + ..\ARM64\ + + + ..\ARM64\ + + + ..\x64\ + + + ..\x64\ + + + ..\x64\ + diff --git a/sys/windows/vs/dlb/afterdlb.proj b/sys/windows/vs/dlb/afterdlb.proj index 515473a3a..44a663187 100644 --- a/sys/windows/vs/dlb/afterdlb.proj +++ b/sys/windows/vs/dlb/afterdlb.proj @@ -2,12 +2,12 @@ - + diff --git a/sys/windows/vs/makedefs/aftermakedefs.proj b/sys/windows/vs/makedefs/aftermakedefs.proj index 8aaf1e314..1a9e7d8bc 100644 --- a/sys/windows/vs/makedefs/aftermakedefs.proj +++ b/sys/windows/vs/makedefs/aftermakedefs.proj @@ -2,12 +2,12 @@ - - - - + + + + diff --git a/sys/windows/vs/tile2bmp/aftertile2bmp.proj b/sys/windows/vs/tile2bmp/aftertile2bmp.proj index 0d7525d5f..e5576ee61 100644 --- a/sys/windows/vs/tile2bmp/aftertile2bmp.proj +++ b/sys/windows/vs/tile2bmp/aftertile2bmp.proj @@ -2,9 +2,9 @@ - + diff --git a/sys/windows/vs/tilemap/aftertilemap.proj b/sys/windows/vs/tilemap/aftertilemap.proj index a8795e96a..703f2c46a 100644 --- a/sys/windows/vs/tilemap/aftertilemap.proj +++ b/sys/windows/vs/tilemap/aftertilemap.proj @@ -2,9 +2,9 @@ - + diff --git a/sys/windows/vs/uudecode/afteruudecode.proj b/sys/windows/vs/uudecode/afteruudecode.proj index e8b4da4c0..32cb45898 100644 --- a/sys/windows/vs/uudecode/afteruudecode.proj +++ b/sys/windows/vs/uudecode/afteruudecode.proj @@ -2,78 +2,78 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From a9d11db8533a96dbc652aa0f54cba2c6d837eb57 Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 18 Nov 2025 13:44:55 -0500 Subject: [PATCH 093/442] visual studio project tweaks --- sys/windows/vs/FetchPrereq/fetchprereq.nmake | 10 +-- sys/windows/vs/NetHack.sln | 33 ++++++- sys/windows/vs/dirs.props | 7 ++ sys/windows/vs/hacklib/hacklib.vcxproj | 15 +--- sys/windows/vs/lualib/lualib.vcxproj | 60 ++++--------- sys/windows/vs/nhlua_h/nhlua_h.vcxproj | 59 +++++++++++++ sys/windows/vs/package/package.nmake | 91 ++++++++++---------- sys/windows/vs/package/package.vcxproj | 2 +- 8 files changed, 162 insertions(+), 115 deletions(-) create mode 100644 sys/windows/vs/nhlua_h/nhlua_h.vcxproj diff --git a/sys/windows/vs/FetchPrereq/fetchprereq.nmake b/sys/windows/vs/FetchPrereq/fetchprereq.nmake index 07c62dbb6..2b826a302 100644 --- a/sys/windows/vs/FetchPrereq/fetchprereq.nmake +++ b/sys/windows/vs/FetchPrereq/fetchprereq.nmake @@ -33,7 +33,7 @@ SUBMDIR=$(ROOTDIR)\$(SUBMSDIR) default: fetchall -fetchall: libdir fetch-Lua fetch-pdcurses ..\..\..\..\include\nhlua.h +fetchall: libdir fetch-Lua fetch-pdcurses fetch-lua: fetch-actual-Lua @@ -56,14 +56,6 @@ fetch-pdcurses: cd ..\sys\windows\vs\fetchprereq @echo $(PDCDIST) has been fetched into $(LIBDIR)\$(PDCDIST) -..\..\..\..\include\nhlua.h: - @echo /* nhlua.h - generated by Makefile from fetchprereq.nmake */ > $@ - @echo #include "lua.h" >> $@ - @echo ATTRNORETURN LUA_API int (lua_error) (lua_State *L) NORETURN; >> $@ - @echo #include "lualib.h" >> $@ - @echo #include "lauxlib.h" >> $@ - @echo /*nhlua.h*/ >> $@ - libdir: @if not exist $(LIBDIR)\*.* echo creating directory $(LIB:\=/) @if not exist $(LIBDIR)\*.* mkdir $(LIBDIR) diff --git a/sys/windows/vs/NetHack.sln b/sys/windows/vs/NetHack.sln index 99f79d38d..2c70f0491 100644 --- a/sys/windows/vs/NetHack.sln +++ b/sys/windows/vs/NetHack.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 18 -VisualStudioVersion = 18.0.11205.157 d18.0 +VisualStudioVersion = 18.0.11205.157 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NetHackW", "NetHackW\NetHackW.vcxproj", "{CEC5D360-8804-454F-8591-002184C23499}" ProjectSection(ProjectDependencies) = postProject @@ -12,6 +12,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NetHackW", "NetHackW\NetHac {63F9B82B-F589-4082-ABE5-D4F0682050AB} = {63F9B82B-F589-4082-ABE5-D4F0682050AB} {642BC75D-ABAF-403E-8224-7C725FD4CB42} = {642BC75D-ABAF-403E-8224-7C725FD4CB42} {93F10526-209E-41D7-BBEA-775787876895} = {93F10526-209E-41D7-BBEA-775787876895} + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} = {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} = {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} EndProjectSection EndProject @@ -26,7 +27,7 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "makedefs", "makedefs\makedefs.vcxproj", "{BA3DD34C-04B7-40D0-B373-9329AA9E8945}" ProjectSection(ProjectDependencies) = postProject {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} = {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} - {503AE687-C33A-45ED-93AA-83967E176D67} = {503AE687-C33A-45ED-93AA-83967E176D67} + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} = {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "recover", "recover\recover.vcxproj", "{2F35F228-6733-4FE5-9B46-B3AA10D4BC2E}" @@ -41,26 +42,28 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tile2bmp", "tile2bmp\tile2b ProjectSection(ProjectDependencies) = postProject {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} = {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} {503AE687-C33A-45ED-93AA-83967E176D67} = {503AE687-C33A-45ED-93AA-83967E176D67} + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} = {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tilemap", "tilemap\tilemap.vcxproj", "{93F10526-209E-41D7-BBEA-775787876895}" ProjectSection(ProjectDependencies) = postProject {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} = {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} {503AE687-C33A-45ED-93AA-83967E176D67} = {503AE687-C33A-45ED-93AA-83967E176D67} + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} = {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "uudecode", "uudecode\uudecode.vcxproj", "{63F9B82B-F589-4082-ABE5-D4F0682050AB}" ProjectSection(ProjectDependencies) = postProject - {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} = {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} {503AE687-C33A-45ED-93AA-83967E176D67} = {503AE687-C33A-45ED-93AA-83967E176D67} + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} = {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NetHack", "NetHack\NetHack.vcxproj", "{609BC774-C6F8-4B2B-AA7D-5B3D0EA95751}" ProjectSection(ProjectDependencies) = postProject {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC} = {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC} {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} = {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} - {503AE687-C33A-45ED-93AA-83967E176D67} = {503AE687-C33A-45ED-93AA-83967E176D67} {63F9B82B-F589-4082-ABE5-D4F0682050AB} = {63F9B82B-F589-4082-ABE5-D4F0682050AB} + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} = {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} = {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} {BAA70D0F-3EC7-4D10-91F0-974F1F49308B} = {BAA70D0F-3EC7-4D10-91F0-974F1F49308B} EndProjectSection @@ -83,6 +86,8 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hacklib", "hacklib\hacklib.vcxproj", "{096FD6BB-256A-4E68-9B09-2ACA7C606FF3}" ProjectSection(ProjectDependencies) = postProject {503AE687-C33A-45ED-93AA-83967E176D67} = {503AE687-C33A-45ED-93AA-83967E176D67} + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} = {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} + {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} = {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fetchprereq", "FetchPrereq\fetchprereq.vcxproj", "{503AE687-C33A-45ED-93AA-83967E176D67}" @@ -98,6 +103,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "package\package. {63F9B82B-F589-4082-ABE5-D4F0682050AB} = {63F9B82B-F589-4082-ABE5-D4F0682050AB} {642BC75D-ABAF-403E-8224-7C725FD4CB42} = {642BC75D-ABAF-403E-8224-7C725FD4CB42} {93F10526-209E-41D7-BBEA-775787876895} = {93F10526-209E-41D7-BBEA-775787876895} + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} = {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} + {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} = {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} {BA3DD34C-04B7-40D0-B373-9329AA9E8945} = {BA3DD34C-04B7-40D0-B373-9329AA9E8945} {BAA70D0F-3EC7-4D10-91F0-974F1F49308B} = {BAA70D0F-3EC7-4D10-91F0-974F1F49308B} {CEC5D360-8804-454F-8591-002184C23499} = {CEC5D360-8804-454F-8591-002184C23499} @@ -108,6 +115,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lualib", "lualib\lualib.vcx {503AE687-C33A-45ED-93AA-83967E176D67} = {503AE687-C33A-45ED-93AA-83967E176D67} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nhlua_h", "nhlua_h\nhlua_h.vcxproj", "{A3B1A65E-4B65-4B67-ADF1-0E38567013A5}" + ProjectSection(ProjectDependencies) = postProject + {503AE687-C33A-45ED-93AA-83967E176D67} = {503AE687-C33A-45ED-93AA-83967E176D67} + {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} = {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM64 = Debug|ARM64 @@ -286,6 +299,18 @@ Global {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037}.Release|Win32.Build.0 = Release|Win32 {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037}.Release|x64.ActiveCfg = Release|x64 {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037}.Release|x64.Build.0 = Release|x64 + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5}.Debug|ARM64.Build.0 = Debug|ARM64 + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5}.Debug|Win32.ActiveCfg = Debug|Win32 + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5}.Debug|Win32.Build.0 = Debug|Win32 + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5}.Debug|x64.ActiveCfg = Debug|x64 + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5}.Debug|x64.Build.0 = Debug|x64 + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5}.Release|ARM64.ActiveCfg = Release|ARM64 + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5}.Release|ARM64.Build.0 = Release|ARM64 + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5}.Release|Win32.ActiveCfg = Release|Win32 + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5}.Release|Win32.Build.0 = Release|Win32 + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5}.Release|x64.ActiveCfg = Release|x64 + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/sys/windows/vs/dirs.props b/sys/windows/vs/dirs.props index f481482b1..91629701d 100644 --- a/sys/windows/vs/dirs.props +++ b/sys/windows/vs/dirs.props @@ -42,23 +42,30 @@ + $(Platform) ..\ARM64\ + arm64 ..\ARM64\ + arm64 ..\ARM64\ + arm64 ..\x64\ + x64 ..\x64\ + x64 ..\x64\ + x64 diff --git a/sys/windows/vs/hacklib/hacklib.vcxproj b/sys/windows/vs/hacklib/hacklib.vcxproj index a708c1e3a..3c7241496 100644 --- a/sys/windows/vs/hacklib/hacklib.vcxproj +++ b/sys/windows/vs/hacklib/hacklib.vcxproj @@ -75,27 +75,25 @@ StaticLibrary true Unicode - v143 + v145 StaticLibrary true Unicode - v143 StaticLibrary false true Unicode - v143 + v145 StaticLibrary false true Unicode - v143 @@ -230,15 +228,6 @@ - - - - - - - - - \ No newline at end of file diff --git a/sys/windows/vs/lualib/lualib.vcxproj b/sys/windows/vs/lualib/lualib.vcxproj index b004fcbfb..227d1618a 100644 --- a/sys/windows/vs/lualib/lualib.vcxproj +++ b/sys/windows/vs/lualib/lualib.vcxproj @@ -40,7 +40,7 @@ - /Gs /Oi- /w44774 /w45262 %(AdditionalOptions) + /Gs /w44774 /w45262 %(AdditionalOptions) 4100;4244;4245;4310;4706;4820;4324 Disabled true @@ -84,45 +84,29 @@ - - StaticLibrary - true - $(DefaultPlatformToolset) - Unicode - - - StaticLibrary - false - $(DefaultPlatformToolset) - true - Unicode - - + StaticLibrary true Unicode + + v143 - - StaticLibrary - true - Unicode + v143 - - StaticLibrary - false - true - Unicode + + v145 + + v143 - - StaticLibrary - false - true - Unicode + v143 + + v145 + @@ -203,7 +187,7 @@ _DEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) true $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) - /Gs /Oi- /w44774 %(AdditionalOptions) + /Gs /w44774 %(AdditionalOptions) @@ -234,12 +218,13 @@ Level3 true - true + false true NDEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) true $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) - /Gs /Oi- /w44774 %(AdditionalOptions) + /Gs /w44774 %(AdditionalOptions) + ProgramDatabase @@ -250,15 +235,6 @@ - - - - - - - - - - \ No newline at end of file + diff --git a/sys/windows/vs/nhlua_h/nhlua_h.vcxproj b/sys/windows/vs/nhlua_h/nhlua_h.vcxproj new file mode 100644 index 000000000..842f541d8 --- /dev/null +++ b/sys/windows/vs/nhlua_h/nhlua_h.vcxproj @@ -0,0 +1,59 @@ + + + + + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} + 10.0 + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sys/windows/vs/package/package.nmake b/sys/windows/vs/package/package.nmake index 55b3e8e02..f1db87c6c 100644 --- a/sys/windows/vs/package/package.nmake +++ b/sys/windows/vs/package/package.nmake @@ -21,37 +21,35 @@ NHV=$(NHV:"=) #LIBSDIR=lib # libraries and external bits #SUBMSDIR=submodules # NetHack git submodules PACKAGESDIR=vspackage # put in vspackage to distinguish -ROOTDIR=..\..\..\.. # root of NetHack tree relative to project file -PlatformFileName=$(lowercase $(PlatformShortName)) +ROOTDIR=..\..\..\..\ # root of NetHack tree relative to project file +PlatformFileName=$(lowercase $(PSN)) # Directories we might have to collect things from # -INCL = $(ROOTDIR)\include # NetHack include files -DAT = $(ROOTDIR)\dat # NetHack data files -DOC = $(ROOTDIR)\doc # NetHack documentation files -UTIL = $(ROOTDIR)\util # Utility source -SRC = $(ROOTDIR)\src # Main source -SSYS = $(ROOTDIR)\sys\share # Shared system files -MSWSYS = $(ROOTDIR)\sys\windows # MS windows specific files -TTY = $(ROOTDIR)\win\tty # window port files (tty) -MSWIN = $(ROOTDIR)\win\win32 # window port files (win32) -WCURSES = $(ROOTDIR)\win\curses # window port files (curses) -WSHR = $(ROOTDIR)\win\share # Tile support files -QT = $(ROOTDIR)\win\Qt # QT support files -X11 = $(ROOTDIR)\win\X11 # X11 support files -LIBDIR = $(ROOTDIR)\lib # libraries and external bits -SUBMDIR = $(ROOTDIR)\submodules # NetHack git submodules -SndWavDir = $(ROOTDIR)\sound\wav # sound files that get integrated -DocDir = $(ROOTDIR)\doc +INCL = $(ROOTDIR)include # NetHack include files +DAT = $(ROOTDIR)dat # NetHack data files +DOC = $(ROOTDIR)doc # NetHack documentation files +UTIL = $(ROOTDIR)util # Utility source +SRC = $(ROOTDIR)src # Main source +SSYS = $(ROOTDIR)sys\share # Shared system files +MSWSYS = $(ROOTDIR)sys\windows # MS windows specific files +TTY = $(ROOTDIR)win\tty # window port files (tty) +MSWIN = $(ROOTDIR)win\win32 # window port files (win32) +WCURSES = $(ROOTDIR)win\curses # window port files (curses) +WSHR = $(ROOTDIR)win\share # Tile support files +QT = $(ROOTDIR)win\Qt # QT support files +X11 = $(ROOTDIR)win\X11 # X11 support files +LIBDIR = $(ROOTDIR)lib # libraries and external bits +SUBMDIR = $(ROOTDIR)submodules # NetHack git submodules +SndWavDir = $(ROOTDIR)sound\wav # sound files that get integrated +DocDir = $(ROOTDIR)doc # Directories we might place collected things # -VSBINDIR=$(ROOTDIR)\vsbinary -VSPACKAGEDIR = $(ROOTDIR)\vspackage +VSBINDIR=$(ROOTDIR)vsbinary +VSPACKAGEDIR = $(ROOTDIR)vspackage - - -default: packageall +default: showvar packageall #=============================================================================== # makefile rules @@ -80,7 +78,7 @@ DBGSYMZIP = $(VSPACKAGEDIR)\nethack-$(NHV)-win-$(PlatformFileName)-debugsymbols. packageall: packagezip -packagezip: showvar vsbindir vspackagedir $(FILESTOZIP) $(MAINZIP) $(DBGSYMZIP) +packagezip: vsbindir vspackagedir $(FILESTOZIP) $(MAINZIP) $(DBGSYMZIP) @echo NetHack Windows package created: $(MAINZIP) $(MAINZIP): $(FILESTOZIP) @@ -90,34 +88,35 @@ $(MAINZIP): $(FILESTOZIP) $(DBGSYMZIP): $(PDBTOZIP) tar -a -cf $(DBGSYMZIP) $(PDBTOZIP) -$(VSBINDIR)\license: $(BinDir)\license - copy /Y $(BinDir)\license $@ -$(VSBINDIR)\Guidebook.txt: $(BinDir)\Guidebook.txt - copy /Y $(DocDir)\Guidebook.txt $@ -$(VSBINDIR)\NetHack.exe: $(BinDir)\NetHack.exe - copy /Y $(BinDir)\NetHack.exe $@ -$(VSBINDIR)\NetHack.txt: $(BinDir)\NetHack.txt - copy /Y $(DocDir)\NetHack.txt $@ -$(VSBINDIR)\NetHackW.exe: $(BinDir)\NetHackW.exe - copy /Y $(BinDir)\NetHackW.exe $@ -$(VSBINDIR)\opthelp: $(BinDir)\opthelp - copy /Y $(BinDir)\opthelp $@ -$(VSBINDIR)\nhdat$(NHV): $(BinDir)\nhdat$(NHV) +$(VSBINDIR)\license: $(BinDir)license + copy /Y $(BinDir)license $@ +$(VSBINDIR)\Guidebook.txt: $(BinDir)Guidebook.txt + copy /Y $(DocDir)Guidebook.txt $@ +$(VSBINDIR)\NetHack.exe: $(BinDir)NetHack.exe + copy /Y $(BinDir)NetHack.exe $@ +$(VSBINDIR)\NetHack.txt: $(BinDir)NetHack.txt + copy /Y $(DocDir)NetHack.txt $@ +$(VSBINDIR)\NetHackW.exe: $(BinDir)NetHackW.exe + copy /Y $(BinDir)NetHackW.exe $@ +$(VSBINDIR)\opthelp: $(BinDir)opthelp + copy /Y $(BinDir)opthelp $@ +$(VSBINDIR)\nhdat$(NHV): $(BinDir)nhdat$(NHV) copy /Y $(BinDir)\nhdat$(NHV) $@ -$(VSBINDIR)\symbols.template: $(BinDir)\symbols.template - copy /Y $(BinDir)\symbols.template $@ -$(VSBINDIR)\nethackrc.template: $(BinDir)\nethackrc.template - copy /Y $(BinDir)\nethackrc.template $@ -$(VSBINDIR)\sysconf.template: $(BinDir)\sysconf.template - copy /Y $(BinDir)\sysconf.template $@ +$(VSBINDIR)\symbols.template: $(BinDir)symbols.template + copy /Y $(BinDir)symbols.template $@ +$(VSBINDIR)\nethackrc.template: $(BinDir)nethackrc.template + copy /Y $(BinDir)nethackrc.template $@ +$(VSBINDIR)\sysconf.template: $(BinDir)sysconf.template + copy /Y $(BinDir)sysconf.template $@ $(VSBINDIR)\record: - -if not exist $(VSBINDIR)\record. goto>$(VSBINDIR)\record. + -if not exist $(VSBINDIR)\record. goto>$(VSBINDIR)record. showvar: @echo BinDir=[$(BinDir)] @echo Platform=[$(Platform)] - @echo PlatformShortName=[$(PlatformShortName)] + @echo PlatformShortName=[$(PSN)] @echo Configuration=[$(Configuration)] + @echo Host=[$(Host)] vspackagedir: @if not exist $(VSPACKAGEDIR)\*.* echo creating directory $(VSPACKAGEDIR:\=/) diff --git a/sys/windows/vs/package/package.vcxproj b/sys/windows/vs/package/package.vcxproj index f0c0c660c..094bdc780 100644 --- a/sys/windows/vs/package/package.vcxproj +++ b/sys/windows/vs/package/package.vcxproj @@ -70,7 +70,7 @@ - pushd $(vsDir)package %26%26 nmake /NOLOGO -F package.nmake BinDir="$(BinDir)\" Platform="$(Platform)" PlatformShortName="$(PlatformShortName)" Configuration="$(Configuration)" NETHACK_VERSION="$(NETHACK_VERSION)" %26%26 popd + pushd $(vsDir)package %26%26 nmake /NOLOGO -F package.nmake BinDir="$(BinDir)\" Configuration="$(Configuration)" Platform="$(Platform)" PSN="$(PlatformShortName)" NETHACK_VERSION="$(NETHACK_VERSION)" Host="$(Host)" %26%26 popd pushd $(vsDir)package %26%26 nmake /NOLOGO -F package.nmake clean %26%26 popd pushd $(vsDir)package %26%26 nmake /NOLOGO -F package.nmake rebuild %26%26 popd From 8f52d51bd2a2ecd6c6805ee4705865ed0c81b44d Mon Sep 17 00:00:00 2001 From: nhw_cron Date: Tue, 18 Nov 2025 14:24:06 -0500 Subject: [PATCH 094/442] This is cron-daily v1-Apr-1-2024. 000files updated: Files --- Files | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Files b/Files index d5cecb8fd..be6ec970a 100644 --- a/Files +++ b/Files @@ -548,6 +548,10 @@ sys/windows/vs/makedefs: (files for Visual Studio Community builds) aftermakedefs.proj makedefs.vcxproj +sys/windows/vs/nhlua_h: +(file for Visual Studio Community builds) +nhlua_h.vcxproj + sys/windows/vs/package: (files for Visual Studio Community builds) package.nmake package.vcxproj From 6de1f39c38b99894f7c72f5205ec81d873cc5f5e Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 18 Nov 2025 18:27:45 -0500 Subject: [PATCH 095/442] follow-up Visual Studio package fix --- sys/windows/vs/package/package.nmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/windows/vs/package/package.nmake b/sys/windows/vs/package/package.nmake index f1db87c6c..0c01ca074 100644 --- a/sys/windows/vs/package/package.nmake +++ b/sys/windows/vs/package/package.nmake @@ -42,7 +42,7 @@ X11 = $(ROOTDIR)win\X11 # X11 support files LIBDIR = $(ROOTDIR)lib # libraries and external bits SUBMDIR = $(ROOTDIR)submodules # NetHack git submodules SndWavDir = $(ROOTDIR)sound\wav # sound files that get integrated -DocDir = $(ROOTDIR)doc +DocDir = $(ROOTDIR)doc\ # Directories we might place collected things # From 9221247e24bc7beae33aaa563368fdc138357dcf Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 18 Nov 2025 18:54:14 -0500 Subject: [PATCH 096/442] another Visual Studio follow-up path fix --- sys/windows/vs/package/package.nmake | 46 +++++++++++++++------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/sys/windows/vs/package/package.nmake b/sys/windows/vs/package/package.nmake index 0c01ca074..20e9ac7d3 100644 --- a/sys/windows/vs/package/package.nmake +++ b/sys/windows/vs/package/package.nmake @@ -26,22 +26,24 @@ PlatformFileName=$(lowercase $(PSN)) # Directories we might have to collect things from # -INCL = $(ROOTDIR)include # NetHack include files -DAT = $(ROOTDIR)dat # NetHack data files -DOC = $(ROOTDIR)doc # NetHack documentation files -UTIL = $(ROOTDIR)util # Utility source -SRC = $(ROOTDIR)src # Main source -SSYS = $(ROOTDIR)sys\share # Shared system files -MSWSYS = $(ROOTDIR)sys\windows # MS windows specific files -TTY = $(ROOTDIR)win\tty # window port files (tty) -MSWIN = $(ROOTDIR)win\win32 # window port files (win32) -WCURSES = $(ROOTDIR)win\curses # window port files (curses) -WSHR = $(ROOTDIR)win\share # Tile support files -QT = $(ROOTDIR)win\Qt # QT support files -X11 = $(ROOTDIR)win\X11 # X11 support files -LIBDIR = $(ROOTDIR)lib # libraries and external bits -SUBMDIR = $(ROOTDIR)submodules # NetHack git submodules -SndWavDir = $(ROOTDIR)sound\wav # sound files that get integrated +#INCL = $(ROOTDIR)include # NetHack include files +#DAT = $(ROOTDIR)dat # NetHack data files +#DOC = $(ROOTDIR)doc # NetHack documentation files +#UTIL = $(ROOTDIR)util # Utility source +#SRC = $(ROOTDIR)src # Main source +#SSYS = $(ROOTDIR)sys\share # Shared system files +#MSWSYS = $(ROOTDIR)sys\windows # MS windows specific files +#TTY = $(ROOTDIR)win\tty # window port files (tty) +#MSWIN = $(ROOTDIR)win\win32 # window port files (win32) +#WCURSES = $(ROOTDIR)win\curses # window port files (curses) +#WSHR = $(ROOTDIR)win\share # Tile support files +#QT = $(ROOTDIR)win\Qt # QT support files +#X11 = $(ROOTDIR)win\X11 # X11 support files +#SndWavDir = $(ROOTDIR)sound\wav # sound files that get integrated + +# libraries and external bits +LIBDIR = $(ROOTDIR)lib\ +# Documentation and text files DocDir = $(ROOTDIR)doc\ # Directories we might place collected things @@ -90,11 +92,11 @@ $(DBGSYMZIP): $(PDBTOZIP) $(VSBINDIR)\license: $(BinDir)license copy /Y $(BinDir)license $@ -$(VSBINDIR)\Guidebook.txt: $(BinDir)Guidebook.txt +$(VSBINDIR)\Guidebook.txt: $(DocDir)Guidebook.txt copy /Y $(DocDir)Guidebook.txt $@ $(VSBINDIR)\NetHack.exe: $(BinDir)NetHack.exe copy /Y $(BinDir)NetHack.exe $@ -$(VSBINDIR)\NetHack.txt: $(BinDir)NetHack.txt +$(VSBINDIR)\NetHack.txt: $(DocDir)NetHack.txt copy /Y $(DocDir)NetHack.txt $@ $(VSBINDIR)\NetHackW.exe: $(BinDir)NetHackW.exe copy /Y $(BinDir)NetHackW.exe $@ @@ -127,10 +129,10 @@ vsbindir: @if not exist $(VSBINDIR)\*.* mkdir $(VSBINDIR) clean: -# @if exist $(LIBDIR)\$(PDCDIST) rmdir /Q $(LIBDIR)\$(PDCDIST) /s -# @if exist $(LIBDIR)\lua-$(LUA_VERSION) rmdir /Q $(LIBDIR)\lua-$(LUA_VERSION) /s +# @if exist $(LIBDIR)$(PDCDIST) rmdir /Q $(LIBDIR)$(PDCDIST) /s +# @if exist $(LIBDIR)lua-$(LUA_VERSION) rmdir /Q $(LIBDIR)lua-$(LUA_VERSION) /s # @if exist ..\..\..\..\include\nhlua.h del /Q ..\..\..\..\include\nhlua.h rebuild: -# @if exist $(LIBDIR)\$(PDCDIST) echo nothing to do for lib\$(PDCDIST) -# @if exist $(LIBDIR)\lua-$(LUA_VERSION) echo nothing to do for lib\lua-$(LUA_VERSION) +# @if exist $(LIBDIR)$(PDCDIST) echo nothing to do for lib\$(PDCDIST) +# @if exist $(LIBDIR)lua-$(LUA_VERSION) echo nothing to do for lib\lua-$(LUA_VERSION) From df54c155b2671aa201d720308242aa87460184d7 Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 18 Nov 2025 18:56:35 -0500 Subject: [PATCH 097/442] yet another Visual Studio follow-up yet another path correction --- sys/windows/vs/package/package.nmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/windows/vs/package/package.nmake b/sys/windows/vs/package/package.nmake index 20e9ac7d3..13d5da8c6 100644 --- a/sys/windows/vs/package/package.nmake +++ b/sys/windows/vs/package/package.nmake @@ -103,7 +103,7 @@ $(VSBINDIR)\NetHackW.exe: $(BinDir)NetHackW.exe $(VSBINDIR)\opthelp: $(BinDir)opthelp copy /Y $(BinDir)opthelp $@ $(VSBINDIR)\nhdat$(NHV): $(BinDir)nhdat$(NHV) - copy /Y $(BinDir)\nhdat$(NHV) $@ + copy /Y $(BinDir)nhdat$(NHV) $@ $(VSBINDIR)\symbols.template: $(BinDir)symbols.template copy /Y $(BinDir)symbols.template $@ $(VSBINDIR)\nethackrc.template: $(BinDir)nethackrc.template From 2b0c0838946d14c2c189ec48d039aab18620e7ea Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 18 Nov 2025 19:04:07 -0500 Subject: [PATCH 098/442] nmake trailing slash fix --- sys/windows/vs/package/package.nmake | 30 +++++++--------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/sys/windows/vs/package/package.nmake b/sys/windows/vs/package/package.nmake index 13d5da8c6..7de47a428 100644 --- a/sys/windows/vs/package/package.nmake +++ b/sys/windows/vs/package/package.nmake @@ -18,33 +18,17 @@ NHV=$(NHV:"=) # relative directories from root of NetHack tree. # -#LIBSDIR=lib # libraries and external bits -#SUBMSDIR=submodules # NetHack git submodules -PACKAGESDIR=vspackage # put in vspackage to distinguish -ROOTDIR=..\..\..\..\ # root of NetHack tree relative to project file +BACKSLASH=^\ +#LIBSDIR=lib # libraries and external bits +#SUBMSDIR=submodules # NetHack git submodules +PACKAGESDIR=vspackage # put in vspackage to distinguish +ROOTDIR=..\..\..\..$(BACKSLASH) # root of NetHack tree relative to project file PlatformFileName=$(lowercase $(PSN)) # Directories we might have to collect things from -# -#INCL = $(ROOTDIR)include # NetHack include files -#DAT = $(ROOTDIR)dat # NetHack data files -#DOC = $(ROOTDIR)doc # NetHack documentation files -#UTIL = $(ROOTDIR)util # Utility source -#SRC = $(ROOTDIR)src # Main source -#SSYS = $(ROOTDIR)sys\share # Shared system files -#MSWSYS = $(ROOTDIR)sys\windows # MS windows specific files -#TTY = $(ROOTDIR)win\tty # window port files (tty) -#MSWIN = $(ROOTDIR)win\win32 # window port files (win32) -#WCURSES = $(ROOTDIR)win\curses # window port files (curses) -#WSHR = $(ROOTDIR)win\share # Tile support files -#QT = $(ROOTDIR)win\Qt # QT support files -#X11 = $(ROOTDIR)win\X11 # X11 support files -#SndWavDir = $(ROOTDIR)sound\wav # sound files that get integrated - -# libraries and external bits -LIBDIR = $(ROOTDIR)lib\ +LIBDIR = $(ROOTDIR)lib$(BACKSLASH) # Documentation and text files -DocDir = $(ROOTDIR)doc\ +DocDir = $(ROOTDIR)doc$(BACKSLASH) # Directories we might place collected things # From ec04123a715803398261407dd8f0da9fbbef6f27 Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 18 Nov 2025 19:10:28 -0500 Subject: [PATCH 099/442] lualib.vcxproj bit --- sys/windows/vs/lualib/lualib.vcxproj | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/sys/windows/vs/lualib/lualib.vcxproj b/sys/windows/vs/lualib/lualib.vcxproj index 227d1618a..0849b2893 100644 --- a/sys/windows/vs/lualib/lualib.vcxproj +++ b/sys/windows/vs/lualib/lualib.vcxproj @@ -95,18 +95,18 @@ v143 - - v145 - + + v145 + v143 v143 - - v145 - + + v145 + @@ -156,6 +156,7 @@ true $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) /Gs /Oi- /w44774 %(AdditionalOptions) + ProgramDatabase @@ -205,6 +206,7 @@ true $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) /Gs /Oi- /w44774 %(AdditionalOptions) + ProgramDatabase @@ -237,4 +239,4 @@ - + \ No newline at end of file From be10b30914eee7520a0d7c9100ec3d4e4cd66644 Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 18 Nov 2025 20:03:05 -0500 Subject: [PATCH 100/442] more Visual Studio vcxproj tweaking --- sys/windows/vs/dirs.props | 1 + sys/windows/vs/package/package.nmake | 56 +++++++++++++------------- sys/windows/vs/package/package.vcxproj | 2 +- 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/sys/windows/vs/dirs.props b/sys/windows/vs/dirs.props index 91629701d..640a5aa41 100644 --- a/sys/windows/vs/dirs.props +++ b/sys/windows/vs/dirs.props @@ -31,6 +31,7 @@ $(LibDir)lua-$(LUA_VERSION)\src\ $(RootDir)\sys\windows\vs\sftags\ $(RootDir)\sys\windows\vs\sfctool\ + $(RootDir)binary\$(Configuration)\$(Platform) $(LibDir)pdcursesmod\ diff --git a/sys/windows/vs/package/package.nmake b/sys/windows/vs/package/package.nmake index 7de47a428..edfbe932a 100644 --- a/sys/windows/vs/package/package.nmake +++ b/sys/windows/vs/package/package.nmake @@ -18,17 +18,17 @@ NHV=$(NHV:"=) # relative directories from root of NetHack tree. # -BACKSLASH=^\ -#LIBSDIR=lib # libraries and external bits -#SUBMSDIR=submodules # NetHack git submodules -PACKAGESDIR=vspackage # put in vspackage to distinguish -ROOTDIR=..\..\..\..$(BACKSLASH) # root of NetHack tree relative to project file +#LIBSDIR=lib # libraries and external bits +#SUBMSDIR=submodules # NetHack git submodules +ROOTDIR=..\..\..\..\ # root of NetHack tree relative to project file PlatformFileName=$(lowercase $(PSN)) +PackageDir=$(PackageBin) # Directories we might have to collect things from -LIBDIR = $(ROOTDIR)lib$(BACKSLASH) +LIBDIR = $(ROOTDIR)lib # Documentation and text files -DocDir = $(ROOTDIR)doc$(BACKSLASH) +DocDir = $(ROOTDIR)doc +# binary # Directories we might place collected things # @@ -74,31 +74,31 @@ $(MAINZIP): $(FILESTOZIP) $(DBGSYMZIP): $(PDBTOZIP) tar -a -cf $(DBGSYMZIP) $(PDBTOZIP) -$(VSBINDIR)\license: $(BinDir)license - copy /Y $(BinDir)license $@ -$(VSBINDIR)\Guidebook.txt: $(DocDir)Guidebook.txt - copy /Y $(DocDir)Guidebook.txt $@ -$(VSBINDIR)\NetHack.exe: $(BinDir)NetHack.exe - copy /Y $(BinDir)NetHack.exe $@ -$(VSBINDIR)\NetHack.txt: $(DocDir)NetHack.txt - copy /Y $(DocDir)NetHack.txt $@ -$(VSBINDIR)\NetHackW.exe: $(BinDir)NetHackW.exe - copy /Y $(BinDir)NetHackW.exe $@ -$(VSBINDIR)\opthelp: $(BinDir)opthelp - copy /Y $(BinDir)opthelp $@ -$(VSBINDIR)\nhdat$(NHV): $(BinDir)nhdat$(NHV) - copy /Y $(BinDir)nhdat$(NHV) $@ -$(VSBINDIR)\symbols.template: $(BinDir)symbols.template - copy /Y $(BinDir)symbols.template $@ -$(VSBINDIR)\nethackrc.template: $(BinDir)nethackrc.template - copy /Y $(BinDir)nethackrc.template $@ -$(VSBINDIR)\sysconf.template: $(BinDir)sysconf.template - copy /Y $(BinDir)sysconf.template $@ +$(VSBINDIR)\license: $(PackageDir)\license + copy /Y $(PackageDir)\license $@ +$(VSBINDIR)\Guidebook.txt: $(DocDir)\Guidebook.txt + copy /Y $(DocDir)\Guidebook.txt $@ +$(VSBINDIR)\NetHack.exe: $(PackageDir)\NetHack.exe + copy /Y $(PackageDir)\NetHack.exe $@ +$(VSBINDIR)\NetHack.txt: $(DocDir)\NetHack.txt + copy /Y $(DocDir)\NetHack.txt $@ +$(VSBINDIR)\NetHackW.exe: $(PackageDir)\NetHackW.exe + copy /Y $(PackageDir)\NetHackW.exe $@ +$(VSBINDIR)\opthelp: $(PackageDir)\opthelp + copy /Y $(PackageDir)\opthelp $@ +$(VSBINDIR)\nhdat$(NHV): $(PackageDir)\nhdat$(NHV) + copy /Y $(PackageDir)\nhdat$(NHV) $@ +$(VSBINDIR)\symbols.template: $(PackageDir)\symbols.template + copy /Y $(PackageDir)\symbols.template $@ +$(VSBINDIR)\nethackrc.template: $(PackageDir)\nethackrc.template + copy /Y $(PackageDir)\nethackrc.template $@ +$(VSBINDIR)\sysconf.template: $(PackageDir)\sysconf.template + copy /Y $(PackageDir)\sysconf.template $@ $(VSBINDIR)\record: -if not exist $(VSBINDIR)\record. goto>$(VSBINDIR)record. showvar: - @echo BinDir=[$(BinDir)] + @echo PackageDir=[$(PackageDir)] @echo Platform=[$(Platform)] @echo PlatformShortName=[$(PSN)] @echo Configuration=[$(Configuration)] diff --git a/sys/windows/vs/package/package.vcxproj b/sys/windows/vs/package/package.vcxproj index 094bdc780..deb647c41 100644 --- a/sys/windows/vs/package/package.vcxproj +++ b/sys/windows/vs/package/package.vcxproj @@ -70,7 +70,7 @@ - pushd $(vsDir)package %26%26 nmake /NOLOGO -F package.nmake BinDir="$(BinDir)\" Configuration="$(Configuration)" Platform="$(Platform)" PSN="$(PlatformShortName)" NETHACK_VERSION="$(NETHACK_VERSION)" Host="$(Host)" %26%26 popd + pushd $(vsDir)package %26%26 nmake /NOLOGO -F package.nmake PackageBin="$(PackageBin)" Configuration="$(Configuration)" Platform="$(Platform)" PSN="$(PlatformShortName)" NETHACK_VERSION="$(NETHACK_VERSION)" Host="$(Host)" %26%26 popd pushd $(vsDir)package %26%26 nmake /NOLOGO -F package.nmake clean %26%26 popd pushd $(vsDir)package %26%26 nmake /NOLOGO -F package.nmake rebuild %26%26 popd From e3a00c2ce34ea8ea3f132045afa5c1e7f7ce150c Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 18 Nov 2025 20:36:15 -0500 Subject: [PATCH 101/442] roll back changes to 2 files; issue in CI --- sys/windows/vs/package/package.nmake | 93 +++++++++++++++----------- sys/windows/vs/package/package.vcxproj | 2 +- 2 files changed, 55 insertions(+), 40 deletions(-) diff --git a/sys/windows/vs/package/package.nmake b/sys/windows/vs/package/package.nmake index edfbe932a..55b3e8e02 100644 --- a/sys/windows/vs/package/package.nmake +++ b/sys/windows/vs/package/package.nmake @@ -18,24 +18,40 @@ NHV=$(NHV:"=) # relative directories from root of NetHack tree. # -#LIBSDIR=lib # libraries and external bits -#SUBMSDIR=submodules # NetHack git submodules -ROOTDIR=..\..\..\..\ # root of NetHack tree relative to project file -PlatformFileName=$(lowercase $(PSN)) -PackageDir=$(PackageBin) +#LIBSDIR=lib # libraries and external bits +#SUBMSDIR=submodules # NetHack git submodules +PACKAGESDIR=vspackage # put in vspackage to distinguish +ROOTDIR=..\..\..\.. # root of NetHack tree relative to project file +PlatformFileName=$(lowercase $(PlatformShortName)) # Directories we might have to collect things from -LIBDIR = $(ROOTDIR)lib -# Documentation and text files -DocDir = $(ROOTDIR)doc -# binary +# +INCL = $(ROOTDIR)\include # NetHack include files +DAT = $(ROOTDIR)\dat # NetHack data files +DOC = $(ROOTDIR)\doc # NetHack documentation files +UTIL = $(ROOTDIR)\util # Utility source +SRC = $(ROOTDIR)\src # Main source +SSYS = $(ROOTDIR)\sys\share # Shared system files +MSWSYS = $(ROOTDIR)\sys\windows # MS windows specific files +TTY = $(ROOTDIR)\win\tty # window port files (tty) +MSWIN = $(ROOTDIR)\win\win32 # window port files (win32) +WCURSES = $(ROOTDIR)\win\curses # window port files (curses) +WSHR = $(ROOTDIR)\win\share # Tile support files +QT = $(ROOTDIR)\win\Qt # QT support files +X11 = $(ROOTDIR)\win\X11 # X11 support files +LIBDIR = $(ROOTDIR)\lib # libraries and external bits +SUBMDIR = $(ROOTDIR)\submodules # NetHack git submodules +SndWavDir = $(ROOTDIR)\sound\wav # sound files that get integrated +DocDir = $(ROOTDIR)\doc # Directories we might place collected things # -VSBINDIR=$(ROOTDIR)vsbinary -VSPACKAGEDIR = $(ROOTDIR)vspackage +VSBINDIR=$(ROOTDIR)\vsbinary +VSPACKAGEDIR = $(ROOTDIR)\vspackage -default: showvar packageall + + +default: packageall #=============================================================================== # makefile rules @@ -64,7 +80,7 @@ DBGSYMZIP = $(VSPACKAGEDIR)\nethack-$(NHV)-win-$(PlatformFileName)-debugsymbols. packageall: packagezip -packagezip: vsbindir vspackagedir $(FILESTOZIP) $(MAINZIP) $(DBGSYMZIP) +packagezip: showvar vsbindir vspackagedir $(FILESTOZIP) $(MAINZIP) $(DBGSYMZIP) @echo NetHack Windows package created: $(MAINZIP) $(MAINZIP): $(FILESTOZIP) @@ -74,35 +90,34 @@ $(MAINZIP): $(FILESTOZIP) $(DBGSYMZIP): $(PDBTOZIP) tar -a -cf $(DBGSYMZIP) $(PDBTOZIP) -$(VSBINDIR)\license: $(PackageDir)\license - copy /Y $(PackageDir)\license $@ -$(VSBINDIR)\Guidebook.txt: $(DocDir)\Guidebook.txt +$(VSBINDIR)\license: $(BinDir)\license + copy /Y $(BinDir)\license $@ +$(VSBINDIR)\Guidebook.txt: $(BinDir)\Guidebook.txt copy /Y $(DocDir)\Guidebook.txt $@ -$(VSBINDIR)\NetHack.exe: $(PackageDir)\NetHack.exe - copy /Y $(PackageDir)\NetHack.exe $@ -$(VSBINDIR)\NetHack.txt: $(DocDir)\NetHack.txt +$(VSBINDIR)\NetHack.exe: $(BinDir)\NetHack.exe + copy /Y $(BinDir)\NetHack.exe $@ +$(VSBINDIR)\NetHack.txt: $(BinDir)\NetHack.txt copy /Y $(DocDir)\NetHack.txt $@ -$(VSBINDIR)\NetHackW.exe: $(PackageDir)\NetHackW.exe - copy /Y $(PackageDir)\NetHackW.exe $@ -$(VSBINDIR)\opthelp: $(PackageDir)\opthelp - copy /Y $(PackageDir)\opthelp $@ -$(VSBINDIR)\nhdat$(NHV): $(PackageDir)\nhdat$(NHV) - copy /Y $(PackageDir)\nhdat$(NHV) $@ -$(VSBINDIR)\symbols.template: $(PackageDir)\symbols.template - copy /Y $(PackageDir)\symbols.template $@ -$(VSBINDIR)\nethackrc.template: $(PackageDir)\nethackrc.template - copy /Y $(PackageDir)\nethackrc.template $@ -$(VSBINDIR)\sysconf.template: $(PackageDir)\sysconf.template - copy /Y $(PackageDir)\sysconf.template $@ +$(VSBINDIR)\NetHackW.exe: $(BinDir)\NetHackW.exe + copy /Y $(BinDir)\NetHackW.exe $@ +$(VSBINDIR)\opthelp: $(BinDir)\opthelp + copy /Y $(BinDir)\opthelp $@ +$(VSBINDIR)\nhdat$(NHV): $(BinDir)\nhdat$(NHV) + copy /Y $(BinDir)\nhdat$(NHV) $@ +$(VSBINDIR)\symbols.template: $(BinDir)\symbols.template + copy /Y $(BinDir)\symbols.template $@ +$(VSBINDIR)\nethackrc.template: $(BinDir)\nethackrc.template + copy /Y $(BinDir)\nethackrc.template $@ +$(VSBINDIR)\sysconf.template: $(BinDir)\sysconf.template + copy /Y $(BinDir)\sysconf.template $@ $(VSBINDIR)\record: - -if not exist $(VSBINDIR)\record. goto>$(VSBINDIR)record. + -if not exist $(VSBINDIR)\record. goto>$(VSBINDIR)\record. showvar: - @echo PackageDir=[$(PackageDir)] + @echo BinDir=[$(BinDir)] @echo Platform=[$(Platform)] - @echo PlatformShortName=[$(PSN)] + @echo PlatformShortName=[$(PlatformShortName)] @echo Configuration=[$(Configuration)] - @echo Host=[$(Host)] vspackagedir: @if not exist $(VSPACKAGEDIR)\*.* echo creating directory $(VSPACKAGEDIR:\=/) @@ -113,10 +128,10 @@ vsbindir: @if not exist $(VSBINDIR)\*.* mkdir $(VSBINDIR) clean: -# @if exist $(LIBDIR)$(PDCDIST) rmdir /Q $(LIBDIR)$(PDCDIST) /s -# @if exist $(LIBDIR)lua-$(LUA_VERSION) rmdir /Q $(LIBDIR)lua-$(LUA_VERSION) /s +# @if exist $(LIBDIR)\$(PDCDIST) rmdir /Q $(LIBDIR)\$(PDCDIST) /s +# @if exist $(LIBDIR)\lua-$(LUA_VERSION) rmdir /Q $(LIBDIR)\lua-$(LUA_VERSION) /s # @if exist ..\..\..\..\include\nhlua.h del /Q ..\..\..\..\include\nhlua.h rebuild: -# @if exist $(LIBDIR)$(PDCDIST) echo nothing to do for lib\$(PDCDIST) -# @if exist $(LIBDIR)lua-$(LUA_VERSION) echo nothing to do for lib\lua-$(LUA_VERSION) +# @if exist $(LIBDIR)\$(PDCDIST) echo nothing to do for lib\$(PDCDIST) +# @if exist $(LIBDIR)\lua-$(LUA_VERSION) echo nothing to do for lib\lua-$(LUA_VERSION) diff --git a/sys/windows/vs/package/package.vcxproj b/sys/windows/vs/package/package.vcxproj index deb647c41..f0c0c660c 100644 --- a/sys/windows/vs/package/package.vcxproj +++ b/sys/windows/vs/package/package.vcxproj @@ -70,7 +70,7 @@ - pushd $(vsDir)package %26%26 nmake /NOLOGO -F package.nmake PackageBin="$(PackageBin)" Configuration="$(Configuration)" Platform="$(Platform)" PSN="$(PlatformShortName)" NETHACK_VERSION="$(NETHACK_VERSION)" Host="$(Host)" %26%26 popd + pushd $(vsDir)package %26%26 nmake /NOLOGO -F package.nmake BinDir="$(BinDir)\" Platform="$(Platform)" PlatformShortName="$(PlatformShortName)" Configuration="$(Configuration)" NETHACK_VERSION="$(NETHACK_VERSION)" %26%26 popd pushd $(vsDir)package %26%26 nmake /NOLOGO -F package.nmake clean %26%26 popd pushd $(vsDir)package %26%26 nmake /NOLOGO -F package.nmake rebuild %26%26 popd From 7e7f393aef721ff8776e8cdd5f6aac67b5131762 Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 20 Nov 2025 10:45:35 -0500 Subject: [PATCH 102/442] update tested versions of Visual Studio 2025-11-20 --- sys/windows/Makefile.nmake | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index 47fcfda03..083049d22 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.20 -# - Microsoft Visual Studio Community 2026 v 18.0.0 +# - Microsoft Visual Studio Community 2022 v 17.14.21 +# - Microsoft Visual Studio Community 2026 v 18.0.1 # #============================================================================== # This is used for building two distinct executables of NetHack: @@ -1184,9 +1184,9 @@ rc=Rc.exe # is too old or untested. # # Recently tested versions: -TESTEDVS2022 = 14.44.35220.0 +TESTEDVS2022 = 14.44.35221.0 # Other versions: -TESTEDVS2026 = 14.50.35717.0 +TESTEDVS2026 = 14.50.35718.0 VS20261ST = 1450000000 VS2026CUR = $(TESTEDVS2026:.=) From edc04fbf7761612f088e364657323c91ccb19215 Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 20 Nov 2025 14:15:00 -0500 Subject: [PATCH 103/442] documentation update build-vs.txt --- sys/windows/build-vs.txt | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/sys/windows/build-vs.txt b/sys/windows/build-vs.txt index 90c12c3e1..0eced6801 100644 --- a/sys/windows/build-vs.txt +++ b/sys/windows/build-vs.txt @@ -4,7 +4,31 @@ Prerequisite Requirements: o Visual Studio Community A copy of a version of Microsoft Visual Studio Community needs to - be installed on your machine. See: + be installed on your machine. Visual Studio Community can be either + the version for Windows 11 x64 for use on machines with + Intel/AMD64 processors, or the version for Windows 11 ARM64 for + use on machines with ARM64 processors (Snapdragon). + + You can opt to build for a target x64, or for a target ARM64 + on either the x64 version of Visual Studio, or on the ARM64 + version. Versions of Visual Studio, since Visual Studio 26, + are able to compile for the native host platform and + cross-compile to the other target platform. + + If you want to selectively cross-compile for both x64 and ARM64 + target types, ensure that the necessary Individual Components for + both types of target builds are selected (checked-off) in the + Visual Studio Installer Individual Components dialog: + + Under Compilers, build tools, and runtimes: + o MSVC Build Tools for ARM64/ARM64ED (Latest) + o MSVC Build Tools for x64/x86 (Latest) + + You can use the Visual Studio Installer "Modify" button + to add missing Individual Components after installation, + if needed. + + See: https://visualstudio.microsoft.com/vs/community/ o Lua NetHack 3.7 for Windows requires 3rd party Lua source that is not part From 57ccb6208ee34f8903b3b4bbce7b0c97cdf05803 Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 20 Nov 2025 14:28:59 -0500 Subject: [PATCH 104/442] another documentation update build-vs.txt --- sys/windows/build-vs.txt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/sys/windows/build-vs.txt b/sys/windows/build-vs.txt index 0eced6801..c407e5847 100644 --- a/sys/windows/build-vs.txt +++ b/sys/windows/build-vs.txt @@ -78,7 +78,7 @@ versions: /-----------------------------------------------------------\ -| Building And Running Using Visual Studio 2022 or greater | +| Building And Running Using Visual Studio Community | \-----------------------------------------------------------/ When using a version of Visual Studio Community, load the provided solution @@ -90,7 +90,15 @@ The Visual Studio NetHack solution file can be found here: The steps are: 1. Launch the IDE. 2. Open the appropriate solution file in sys\windows\vs\NetHack.sln. - 3. Select the build configuration you wish to use (Release, Debug, etc.). + 3. Select the build platform and configuration that you wish + to target (x64 or ARM64, and Release or Debug). + (Note: If you want to target an ARM64 build from an x64 machine, + or target an x64 build from an ARM64 machine, you need to + first complete the build for the native machine type target. + That's necessary to ensure that host-native versions of + uudecode.exe, makedefs.exe, tilemap.exe, tile2bmp.exe and + dlb.exe tools are available for execution during the build + steps that rely on those tools.) 4. From the build menu, select build solution. 5. Type F5 to start debugging. From 69886bd3d158915a69b042b920efcf91c557c1d7 Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 20 Nov 2025 14:32:23 -0500 Subject: [PATCH 105/442] typo fix --- sys/windows/build-vs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/windows/build-vs.txt b/sys/windows/build-vs.txt index c407e5847..04f69c9e1 100644 --- a/sys/windows/build-vs.txt +++ b/sys/windows/build-vs.txt @@ -21,7 +21,7 @@ Prerequisite Requirements: Visual Studio Installer Individual Components dialog: Under Compilers, build tools, and runtimes: - o MSVC Build Tools for ARM64/ARM64ED (Latest) + o MSVC Build Tools for ARM64/ARM64EC (Latest) o MSVC Build Tools for x64/x86 (Latest) You can use the Visual Studio Installer "Modify" button From 6466b47fc1263b7e16457429eabc969dfc4a2e08 Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 20 Nov 2025 14:54:57 -0800 Subject: [PATCH 106/442] miscellaneous formatting of zap.c Just to clear zap.c modifications out of my working directory. --- src/zap.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/zap.c b/src/zap.c index 930b4c150..98b74b04a 100644 --- a/src/zap.c +++ b/src/zap.c @@ -3801,8 +3801,8 @@ zap_map( * function) several objects and monsters on its path. The return value * is the monster hit (weapon != ZAPPED_WAND), or a null monster pointer. * - * Thrown and kicked objects (THROWN_WEAPON or KICKED_WEAPON) may be - * destroyed and *pobj set to NULL to indicate this. + * Thrown and kicked objects (THROWN_WEAPON or KICKED_WEAPON) may be + * destroyed and *pobj set to NULL to indicate this. * * Check !u.uswallow before calling bhit(). * This function reveals the absence of a remembered invisible monster in @@ -3828,8 +3828,8 @@ bhit( boolean in_skip = FALSE, allow_skip = FALSE; boolean tethered_weapon = FALSE; int skiprange_start = 0, skiprange_end = 0, skipcount = 0; - struct obj *was_returning = - (iflags.returning_missile == obj) ? obj : (struct obj *) 0; + struct obj *was_returning = (iflags.returning_missile == obj) ? obj + : (struct obj *) 0; if (weapon == KICKED_WEAPON) { /* object starts one square in front of player */ @@ -4067,8 +4067,7 @@ bhit( break; } if (weapon != ZAPPED_WAND && weapon != INVIS_BEAM) { - /* 'I' present but no monster: erase */ - /* do this before the tmp_at() */ + /* 'I' present but no monster: erase; do this before tmp_at() */ if (glyph_is_invisible(levl[x][y].glyph) && cansee(x, y)) { unmap_object(x, y); newsym(x, y); @@ -4715,11 +4714,11 @@ buzz(int type, int nd, coordxy sx, coordxy sy, int dx, int dy) */ void dobuzz( - int type, /* 0..29 (by hero) or -39..-10 (by monster) */ + int type, /* 0..29 (by hero) or -39..-10 (by monster) */ int nd, /* damage strength ('number of dice') */ coordxy sx, coordxy sy, /* starting point */ int dx, int dy, /* direction delta */ - boolean sayhit, boolean saymiss) /* announce out of sight hit/miss events if true */ + boolean sayhit, boolean saymiss) /* report out of sight hit/miss events */ { int range, fltyp = zaptype(type), damgtype = fltyp % 10; coordxy lsx, lsy; From 93f8e5b3b354a02c0ae55c28d605fde6976f84de Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 20 Nov 2025 15:02:54 -0800 Subject: [PATCH 107/442] fix #S14702 - travel to covered vibrating square Targeting '~' when vibrating square has been discovered would report "Can't find dungeon feature '~'" if it was covered by an object or a monster. That's normal behavior for a trap but the vibrating square is only one of those for display purposes. --- doc/fixes3-7-0.txt | 5 ++++- src/detect.c | 8 +++++++- src/getpos.c | 33 ++++++++++++++++++++++++++++----- src/hack.c | 7 +++++-- 4 files changed, 44 insertions(+), 9 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 8954f3c00..185f271ee 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1,4 +1,4 @@ -NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1575 $ $NHDT-Date: 1762750699 2025/11/09 20:58:19 $ +NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1576 $ $NHDT-Date: 1763708572 2025/11/20 23:02:52 $ General Fixes and Modified Features ----------------------------------- @@ -1532,6 +1532,9 @@ some food and paper items do no damage when bashing improved messages for oilskin sacks protecting from water damage some messages by amorous demons and mail daemon were delivered as verbal ones even when the hero is deaf +travel couldn't find the vibrating square if it was covered by an object or + a monster; it isn't really a trap so treat it as special terrain +travel would stop one step in front of known vibrating square like other traps Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/detect.c b/src/detect.c index 61e5dd3d6..a74cdfea4 100644 --- a/src/detect.c +++ b/src/detect.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 detect.c $NHDT-Date: 1745114235 2025/04/19 17:57:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.190 $ */ +/* NetHack 3.7 detect.c $NHDT-Date: 1763708572 2025/11/20 23:02:52 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.191 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2018. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2179,6 +2179,12 @@ reveal_terrain_getglyph( keep_mons = (which_subset & TER_MON) != 0, full = (which_subset & TER_FULL) != 0; + /* + * FIXME: + * travel treats discovered vibrating square as if it were terrain + * rather than a trap so this should do so too. + */ + /* for 'full', show the actual terrain for the entire level, otherwise what the hero remembers for seen locations with monsters, objects, and/or traps removed as caller dictates */ diff --git a/src/getpos.c b/src/getpos.c index 36e9d95ea..bc892a187 100644 --- a/src/getpos.c +++ b/src/getpos.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 getpos.c $NHDT-Date: 1723875487 2024/08/17 06:18:07 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.3 $ */ +/* NetHack 3.7 getpos.c $NHDT-Date: 1763708572 2025/11/20 23:02:52 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.6 $ */ /*-Copyright (c) Pasi Kallinen, 2023. */ /* NetHack may be freely redistributed. See license for details. */ @@ -18,6 +18,7 @@ staticfn void gloc_filter_floodfill(coordxy, coordxy); staticfn void gloc_filter_init(void); staticfn void gloc_filter_done(void); staticfn void gather_locs(coord **, int *, int); +staticfn boolean known_vibrating_square_at(coordxy, coordxy); staticfn void truncate_to_map(coordxy *, coordxy *, schar, schar); staticfn void getpos_refresh(void); /* Callback function for getpos() to highlight desired map locations. @@ -192,7 +193,7 @@ getpos_help(boolean force, const char *goal) putstr(tmpwin, 0, sbuf); putstr(tmpwin, 0, "Or enter a background symbol (ex. '<')."); Sprintf(sbuf, "Use '%s' to move the cursor on yourself.", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_SELF])); + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_SELF])); putstr(tmpwin, 0, sbuf); if (!iflags.terrainmode || (iflags.terrainmode & TER_MON) != 0) { getpos_help_keyxhelp(tmpwin, @@ -417,10 +418,26 @@ gloc_filter_done(void) } } +staticfn boolean +known_vibrating_square_at(coordxy x, coordxy y) +{ + /* note: this only acknowledges the genuine vibrating square, not + fake ones produced by wizard mode wishing for traps which could + possibly be transfered to normal play via bones file */ + if (invocation_pos(x, y)) { + struct trap *ttmp = t_at(x, y); + + return ttmp && ttmp->ttyp == VIBRATING_SQUARE && ttmp->tseen; + } + return FALSE; +} + DISABLE_WARNING_UNREACHABLE_CODE boolean -gather_locs_interesting(coordxy x, coordxy y, int gloc) +gather_locs_interesting( + coordxy x, coordxy y, + int gloc) { int glyph, sym; @@ -439,7 +456,7 @@ gather_locs_interesting(coordxy x, coordxy y, int gloc) /* unlike '/M', this skips monsters revealed by warning glyphs and remembered unseen ones */ return (glyph_is_monster(glyph) - && glyph != monnum_to_glyph(PM_LONG_WORM_TAIL,MALE) + && glyph != monnum_to_glyph(PM_LONG_WORM_TAIL, MALE) && glyph != monnum_to_glyph(PM_LONG_WORM_TAIL, FEMALE)); case GLOC_OBJS: return (glyph_is_object(glyph) @@ -482,7 +499,8 @@ gather_locs_interesting(coordxy x, coordxy y, int gloc) || is_cmap_room(sym) || is_cmap_corr(sym))) || glyph_is_nothing(glyph) - || glyph_is_unexplored(glyph))); + || glyph_is_unexplored(glyph)) + || known_vibrating_square_at(x, y)); } /*NOTREACHED*/ return FALSE; @@ -1069,6 +1087,11 @@ getpos(coord *ccp, boolean force, const char *goal) && matching[glyph_to_cmap(k)]) goto foundc; } + /* FIXME: check player-specified vib.sq trap + symbol rather than or in addition to '~' */ + if (c == '~' + && known_vibrating_square_at(tx, ty)) + goto foundc; /* last, try actual terrain here (shouldn't we be using svl.lastseentyp[][] instead?) */ if (levl[tx][ty].seenv) { diff --git a/src/hack.c b/src/hack.c index 89c153746..9c0a56712 100644 --- a/src/hack.c +++ b/src/hack.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 hack.c $NHDT-Date: 1736530208 2025/01/10 09:30:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.477 $ */ +/* NetHack 3.7 hack.c $NHDT-Date: 1763708572 2025/11/20 23:02:52 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.494 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2425,7 +2425,10 @@ avoid_moving_on_trap(coordxy x, coordxy y, boolean msg) { struct trap *trap; - if ((trap = t_at(x, y)) && trap->tseen) { + if ((trap = t_at(x, y)) && trap->tseen + /* the vibrating square is implemented as a trap but treated as if + it were a type of terrain */ + && trap->ttyp != VIBRATING_SQUARE) { if (msg && flags.mention_walls) { set_msg_xy(x, y); You("stop in front of %s.", From 113607823d587890995756e1aeae23aa8e80de1c Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 22 Nov 2025 10:34:45 -0500 Subject: [PATCH 108/442] Windows warning bit in windsys.c windows/windsys.c:263:15: warning: format string is not a string literal (potentially insecure) [-Wformat-security] 263 | msmsg(buf); | ^~~ ../sys/windows/windsys.c:263:15: note: treat the string as an argument to avoid this 263 | msmsg(buf); | ^ | "%s", ../sys/windows/windsys.c:267:20: warning: format string is not a string literal (potentially insecure) [-Wformat-security] 267 | raw_printf(buf); | ^~~ --- sys/windows/windsys.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sys/windows/windsys.c b/sys/windows/windsys.c index 3209d774f..d1327869f 100644 --- a/sys/windows/windsys.c +++ b/sys/windows/windsys.c @@ -25,6 +25,7 @@ #include "wintty.h" #endif #include +#include #ifdef WIN32 #include @@ -260,11 +261,11 @@ VA_DECL(const char *, s) buf[0] = '\n'; (void) vsnprintf(&buf[1], sizeof buf - (1 + sizeof "\n"), s, VA_ARGS); Strcat(buf, "\n"); - msmsg(buf); + msmsg("%s", buf); } else { (void) vsnprintf(buf, sizeof buf - sizeof "\n", s, VA_ARGS); Strcat(buf, "\n"); - raw_printf(buf); + raw_printf("%s",buf); } #ifdef MSWIN_GRAPHICS if (windowprocs.win_raw_print == mswin_raw_print) From f78e0d0caf3f15ef48ac3d1aaafe1ca8e454b326 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 22 Nov 2025 10:39:37 -0500 Subject: [PATCH 109/442] MSYS CLANGARM64 bit --- sys/windows/GNUmakefile | 43 ++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/sys/windows/GNUmakefile b/sys/windows/GNUmakefile index aac5608d4..48fc53e03 100644 --- a/sys/windows/GNUmakefile +++ b/sys/windows/GNUmakefile @@ -299,6 +299,18 @@ USE_DLB = Y #========================================== #========================================== +ifeq "$(MSYSTEM)" "MINGW32" +arch = x86 +else +arch = x64 +endif +MACH := $(shell echo $$PROCESSOR_IDENTIFIER | cut -f1 -d ' ') +MACH := $(strip $(MACH)) +# Print the detected architecture (for debugging) +#$(info Detected Architecture:$(MACH).) +ifeq "$(MACH)" "ARMv8" +arch=arm64 +endif ifdef CI_COMPILER cc = gcc -c cxx = g++ -c @@ -311,16 +323,17 @@ ld = gcc endif ifeq "$(MSYSTEM)" "MINGW32" rc = windres --target=pe-i386 -else # MINGW64 +else # MINGW64 or arm64 +ifeq "$(arch)" "arm64" +#rc = windres --target=pe-arm64 +rc = windres +else rc = windres --target=pe-x86-64 endif - -ifeq "$(MSYSTEM)" "MINGW32" -arch = x86 -else # MINGW64 -arch = x64 endif + + # # Handle user settings # @@ -414,8 +427,15 @@ $(U)lua.exe: $(OLUA)/lua.o $(LUALIB) $(LUADLL): $(ULUADLL) cp $< $@ + +ifeq "$(arch)" "arm64" +lparam=-Wl,--export-all-symbols +else +lparam=-Wl,--export-all-symbols -Wl,--add-stdcall-alias +endif + $(ULUADLL) $(LUAIMP): $(LUAOBJS) | $(OLUA) - $(ld) $(LDFLAGS) -fPIC -shared -Wl,--export-all-symbols -Wl,--add-stdcall-alias \ + $(ld) $(LDFLAGS) -fPIC -shared $(lparam) \ -Wl,--out-implib=$(LUAIMP) $^ -o$(ULUADLL) $(LUASTATIC): $(LUAOBJS) | $(OLUA) @@ -1357,7 +1377,16 @@ $(ONH)/%.o: $(SRC)/%.c $(NHLUAH) | $(ONH) #========================================== # package #========================================== +ifeq "$(arch)" "x64" TARGET_CPU = x64 +else +ifeq "$(arch)" "arm64" +TARGET_CPU = arm64 +else +TARGET_CPU = x86 +endif +endif + NHV=370 PKGFILES = nethackrc.template Guidebook.txt license NetHack.exe NetHack.txt \ NetHackW.exe opthelp nhdat370 record symbols sysconf.template $(notdir $(LUADLL)) From 0faa3a70cc076cc3ff1a06a5a4361e6c76209347 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 23 Nov 2025 09:28:38 -0500 Subject: [PATCH 110/442] clear up some warnings in sys/windows/consoletty.c --- sys/windows/consoletty.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/sys/windows/consoletty.c b/sys/windows/consoletty.c index 7754b3d1f..e947561f1 100644 --- a/sys/windows/consoletty.c +++ b/sys/windows/consoletty.c @@ -763,7 +763,7 @@ emit_stop_inverse(void) #define tcfmtstr256 "\x1b[38;5;%ldm" #else #define tcfmtstr24bit "\x1b[38:2:%ld:%ld:%ldm" -#define tcfmtstr256 "\x1b[38:5:%dm" +#define tcfmtstr256 "\x1b[38:5:%ldm" #endif void @@ -771,9 +771,10 @@ emit_start_256color(int u256coloridx) { DWORD unused; static char tcolorbuf[QBUFSZ]; - Snprintf(tcolorbuf, sizeof tcolorbuf, tcfmtstr256, u256coloridx); + Snprintf(tcolorbuf, sizeof tcolorbuf, tcfmtstr256, + (long) u256coloridx); WriteConsoleA(console.hConOut, (LPCSTR) tcolorbuf, - (int) strlen(tcolorbuf), &unused, NULL); + (long) strlen(tcolorbuf), &unused, NULL); } void @@ -783,9 +784,9 @@ emit_start_24bitcolor(long color24bit) static char tcolorbuf[QBUFSZ]; uint32 mcolor = COLORVAL(color24bit); Snprintf(tcolorbuf, sizeof tcolorbuf, tcfmtstr24bit, - ((mcolor >> 16) & 0xFF), /* red */ - ((mcolor >> 8) & 0xFF), /* green */ - ((mcolor >> 0) & 0xFF)); /* blue */ + (long) ((mcolor >> 16) & 0xFF), /* red */ + (long) ((mcolor >> 8) & 0xFF), /* green */ + (long) ((mcolor >> 0) & 0xFF)); /* blue */ WriteConsoleA(console.hConOut, (LPCSTR) tcolorbuf, (int) strlen(tcolorbuf), &unused, NULL); } @@ -1493,7 +1494,7 @@ console_g_putch(int in_ch) #else /* VIRTUAL_TERMINAL_SEQUENCES */ ccount = 0; WCHAR wch[2]; - boolean usemap = (ch >= 0 && ch < SIZE(console.cpMap)); + boolean usemap = (ch >= 0 && ((int) ch < SIZE(console.cpMap))); #endif /* VIRTUAL_TERMINAL_SEQUENCES */ set_console_cursor(ttyDisplay->curx, ttyDisplay->cury); From b3db46fd71458085356e6d74c608a6e4866ebaea Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 23 Nov 2025 09:40:39 -0500 Subject: [PATCH 111/442] clear up some warnings in win/win32/NetHackW.c --- win/win32/NetHackW.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/win/win32/NetHackW.c b/win/win32/NetHackW.c index 45f8bf709..92b851c7a 100644 --- a/win/win32/NetHackW.c +++ b/win/win32/NetHackW.c @@ -70,10 +70,6 @@ int GUILaunched = TRUE; /* We tell shared startup code in windmain.c #define _strdup(s1) strdup(s1) #endif -// Forward declarations of functions included in this code module: -extern boolean main(int, char **); -//static void __cdecl mswin_moveloop(void *); - #define MAX_CMDLINE_PARAM 255 #ifdef _MSC_VER @@ -191,7 +187,7 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, Sprintf(buf2, "Cannot load common control library.\n%s\n%s", "For further information, refer to the installation notes at", INSTALL_NOTES); - panic(buf2); + panic("%s", buf2); } if (major < MIN_COMCTLMAJOR || (major == MIN_COMCTLMAJOR && minor < MIN_COMCTLMINOR)) { @@ -201,7 +197,7 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, MIN_COMCTLMINOR, "For further information, refer to the installation notes at", INSTALL_NOTES); - panic(buf2); + panic("%s", buf2); } ZeroMemory(&InitCtrls, sizeof(InitCtrls)); InitCtrls.dwSize = sizeof(InitCtrls); From ae516ddc679161dd7276d874148016cd91ad915a Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 23 Nov 2025 09:49:49 -0500 Subject: [PATCH 112/442] follow-up: reverse one warning-related change --- sys/windows/consoletty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/windows/consoletty.c b/sys/windows/consoletty.c index e947561f1..10bc06c37 100644 --- a/sys/windows/consoletty.c +++ b/sys/windows/consoletty.c @@ -774,7 +774,7 @@ emit_start_256color(int u256coloridx) Snprintf(tcolorbuf, sizeof tcolorbuf, tcfmtstr256, (long) u256coloridx); WriteConsoleA(console.hConOut, (LPCSTR) tcolorbuf, - (long) strlen(tcolorbuf), &unused, NULL); + (int) strlen(tcolorbuf), &unused, NULL); } void From fce66245cab5a4c5f91faac975b17ddb1f4df267 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Mon, 24 Nov 2025 02:07:23 +0000 Subject: [PATCH 113/442] Don't attempt to cache encumber_msg result There was only one point in the code at which this caching was being done, and it was incorrect: it's possible for the result of near_capacity to change during a monster turn because monster actions can change either inventory weight or carry capacity. The bug was particularly relevant in cases where a character polymorphed into a slow weak monster gets attacked by a monster that moves at normal speed: due to the polyform being slow, the normal-speed monster gets in a lot of attacks and causes a rehumanization, but due to the polyform being weak, it was burdened at the start of the monster turn, and so when that penalty is (due to the bug) applied to the next turn it can mean that the character misses the next turn too, and may end up dying as a result. --- doc/fixes3-7-0.txt | 2 ++ include/extern.h | 2 +- src/allmain.c | 10 +++++++--- src/attrib.c | 10 +++++----- src/ball.c | 2 +- src/do.c | 6 +++--- src/do_wear.c | 2 +- src/dothrow.c | 8 ++++---- src/eat.c | 2 +- src/invent.c | 2 +- src/mkobj.c | 4 ++-- src/pickup.c | 8 +++----- src/polyself.c | 8 ++++---- src/pray.c | 4 ++-- src/steal.c | 6 +++--- src/steed.c | 2 +- src/trap.c | 10 +++++----- src/wield.c | 4 ++-- src/wizcmds.c | 2 +- src/zap.c | 2 +- 20 files changed, 50 insertions(+), 46 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 185f271ee..7ed444fe6 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1535,6 +1535,8 @@ some messages by amorous demons and mail daemon were delivered as verbal ones travel couldn't find the vibrating square if it was covered by an object or a monster; it isn't really a trap so treat it as special terrain travel would stop one step in front of known vibrating square like other traps +fix bug which delayed burden changes due to monster actions (e.g. reverting to + natural form due to damage, changing carry capacity) for one turn Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/include/extern.h b/include/extern.h index 6fe005940..a5a7a76e7 100644 --- a/include/extern.h +++ b/include/extern.h @@ -2405,7 +2405,7 @@ extern int query_category(const char *, struct obj *, int, menu_item **, int) NO extern int query_objlist(const char *, struct obj **, int, menu_item **, int, boolean(*)(struct obj *)) NONNULLARG24; extern struct obj *pick_obj(struct obj *) NONNULLARG1; -extern int encumber_msg(void); +extern void encumber_msg(void); extern int container_at(coordxy, coordxy, boolean); extern int doloot(void); extern void observe_quantum_cat(struct obj *, boolean, boolean) NONNULLARG1; diff --git a/src/allmain.c b/src/allmain.c index f6d1ee503..cde778bfb 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -90,7 +90,7 @@ moveloop_preamble(boolean resuming) fix_shop_damage(); } - (void) encumber_msg(); /* in case they auto-picked up something */ + encumber_msg(); /* in case they auto-picked up something */ if (gd.defer_see_monsters) { gd.defer_see_monsters = FALSE; see_monsters(); @@ -197,7 +197,7 @@ moveloop_core(void) u.umovement -= NORMAL_SPEED; do { /* hero can't move this turn loop */ - mvl_wtcap = encumber_msg(); + encumber_msg(); svc.context.mon_moving = TRUE; do { @@ -207,6 +207,10 @@ moveloop_core(void) } while (monscanmove); svc.context.mon_moving = FALSE; + /* this needs to be after the monster movement loop in + case monster actions affected burden, e.g. rehumanize */ + mvl_wtcap = near_capacity(); + if (!monscanmove && u.umovement < NORMAL_SPEED) { /* both hero and monsters are out of steam this round */ struct monst *mtmp; @@ -392,7 +396,7 @@ moveloop_core(void) inventory may have changed in, e.g., nh_timeout(); we do need two checks here so that the player gets feedback immediately if their own action encumbered them */ - (void) encumber_msg(); + encumber_msg(); #ifdef STATUS_HILITES if (iflags.hilite_delta) diff --git a/src/attrib.c b/src/attrib.c index fc6a7aebf..405cbf7da 100644 --- a/src/attrib.c +++ b/src/attrib.c @@ -191,7 +191,7 @@ adjattrib( if (msgflg <= 0) You_feel("%s%s!", (incr > 1 || incr < -1) ? "very " : "", attrstr); if (program_state.in_moveloop && (ndx == A_STR || ndx == A_CON)) - (void) encumber_msg(); + encumber_msg(); return TRUE; } @@ -401,7 +401,7 @@ poisoned( /* "Poisoned by a poisoned ___" is redundant */ done(strstri(pkiller, "poison") ? DIED : POISONING); } - (void) encumber_msg(); + encumber_msg(); } void @@ -477,7 +477,7 @@ restore_attrib(void) } } if (disp.botl) - (void) encumber_msg(); + encumber_msg(); } #define AVAL 50 /* tune value for exercise gains */ @@ -511,7 +511,7 @@ exercise(int i, boolean inc_or_dec) (inc_or_dec) ? "inc" : "dec", AEXE(i)); } if (svm.moves > 0 && (i == A_STR || i == A_CON)) - (void) encumber_msg(); + encumber_msg(); } staticfn void @@ -753,7 +753,7 @@ redist_attr(void) if (ABASE(i) < ATTRMIN(i)) ABASE(i) = ATTRMIN(i); } - /* (void) encumber_msg(); -- caller needs to do this */ + /* encumber_msg(); -- caller needs to do this */ } /* apply minor variation to attributes */ diff --git a/src/ball.c b/src/ball.c index ebacb7e83..069a71ce4 100644 --- a/src/ball.c +++ b/src/ball.c @@ -34,7 +34,7 @@ ballrelease(boolean showmsg) /* [this used to test 'if (uwep != uball)' but that always passes after the setuwep() above] */ freeinv(uball); /* remove from inventory but don't place on floor */ - (void) encumber_msg(); + encumber_msg(); } } diff --git a/src/do.c b/src/do.c index ae26e7158..875d0c62f 100644 --- a/src/do.c +++ b/src/do.c @@ -839,7 +839,7 @@ dropz(struct obj *obj, boolean with_impact) map_object(obj, 0); newsym(u.ux, u.uy); /* remap location under self */ } - (void) encumber_msg(); + encumber_msg(); } /* when swallowed, move dropped object from OBJ_FREE to u.ustuck's inventory; @@ -2431,7 +2431,7 @@ set_wounded_legs(long side, int timex) direct assignment instead of bitwise-OR so getting wounded in one leg mysteriously healed the other */ EWounded_legs |= side; - (void) encumber_msg(); + encumber_msg(); } void @@ -2470,7 +2470,7 @@ heal_legs( more when steed becomes healthy, then possible floor feedback, then able to carry less when back on foot]. */ if (how == 0) - (void) encumber_msg(); + encumber_msg(); } } diff --git a/src/do_wear.c b/src/do_wear.c index 2cb86fa6d..2ee5b0785 100644 --- a/src/do_wear.c +++ b/src/do_wear.c @@ -671,7 +671,7 @@ Gloves_off(void) } setworn((struct obj *) 0, W_ARMG); svc.context.takeoff.cancelled_don = FALSE; - (void) encumber_msg(); /* immediate feedback for GoP */ + encumber_msg(); /* immediate feedback for GoP */ /* usually can't remove gloves when they're slippery but it can be done by having them fall off (polymorph), stolen, or diff --git a/src/dothrow.c b/src/dothrow.c index a7d4ffd68..e7bfd2789 100644 --- a/src/dothrow.c +++ b/src/dothrow.c @@ -268,7 +268,7 @@ throw_obj(struct obj *obj, int shotlimit) } freeinv(otmp); throwit(otmp, wep_mask, twoweap, oldslot); - (void) encumber_msg(); + encumber_msg(); } gm.m_shot.n = gm.m_shot.i = 0; gm.m_shot.o = STRANGE_OBJECT; @@ -1699,7 +1699,7 @@ throwit( if (!impaired && rn2(100)) { pline("%s to your hand!", Tobjnam(obj, "return")); obj = addinv_before(obj, oldslot); - (void) encumber_msg(); + encumber_msg(); /* addinv autoquivers an aklys if quiver is empty; if obj is quivered, remove it before wielding */ if (obj->owornmask & W_QUIVER) @@ -1886,7 +1886,7 @@ return_throw_to_inv( set_twoweap(TRUE); /* u.twoweap = TRUE */ } - (void) encumber_msg(); + encumber_msg(); return obj; } @@ -2124,7 +2124,7 @@ thitmonst( sho_obj_return_to_u(obj); obj = addinv(obj); /* back into your inventory */ nhUse(obj); - (void) encumber_msg(); + encumber_msg(); } return 1; /* caller doesn't need to place it */ } diff --git a/src/eat.c b/src/eat.c index b15f36ade..3074b9e10 100644 --- a/src/eat.c +++ b/src/eat.c @@ -130,7 +130,7 @@ init_uhunger(void) u.uhs = NOT_HUNGRY; if (ATEMP(A_STR) < 0) { ATEMP(A_STR) = 0; - (void) encumber_msg(); + encumber_msg(); } } diff --git a/src/invent.c b/src/invent.c index bd8c689c9..869c97c3e 100644 --- a/src/invent.c +++ b/src/invent.c @@ -1269,7 +1269,7 @@ hold_another_object( prinv(hold_msg, obj, oquan); /* obj made it into inventory and is staying there */ update_inventory(); - (void) encumber_msg(); + encumber_msg(); } } return obj; diff --git a/src/mkobj.c b/src/mkobj.c index a7687ca8a..77479c57b 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -1657,7 +1657,7 @@ shrink_glob( } if (updinv) { update_inventory(); - (void) encumber_msg(); + encumber_msg(); } } @@ -2892,7 +2892,7 @@ hornoplenty( /* item still in magic horn was weightless; when it's now in a carried container, hero's encumbrance could change */ if (carried(targetbox)) { - (void) encumber_msg(); + encumber_msg(); update_inventory(); /* for contents count or wizweight */ } } else { diff --git a/src/pickup.c b/src/pickup.c index b74119402..4a366f757 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -1972,10 +1972,9 @@ pickup_prinv( } /* - * prints a message if encumbrance changed since the last check and - * returns the new encumbrance value (from near_capacity()). + * prints a message if encumbrance changed since the last check */ -int +void encumber_msg(void) { int newcap = near_capacity(); @@ -2018,7 +2017,6 @@ encumber_msg(void) } go.oldcap = newcap; - return newcap; } /* Is there a container at x,y. Optional: return count of containers at x,y */ @@ -3821,7 +3819,7 @@ tipcontainer(struct obj *box) /* or bag */ if (targetbox) targetbox->owt = weight(targetbox); if (srcheld || dstheld) - (void) encumber_msg(); + encumber_msg(); } if (srcheld || dstheld) diff --git a/src/polyself.c b/src/polyself.c index 4ab183113..669f32da4 100644 --- a/src/polyself.c +++ b/src/polyself.c @@ -425,7 +425,7 @@ newman(void) done(DIED); /* must have been life-saved to get here */ newuhs(FALSE); - (void) encumber_msg(); /* used to be done by redist_attr() */ + encumber_msg(); /* used to be done by redist_attr() */ return; /* lifesaved */ } } @@ -454,7 +454,7 @@ newman(void) disp.botl = TRUE; see_monsters(); - (void) encumber_msg(); + encumber_msg(); retouch_equipment(2); if (!uarmg) @@ -1012,7 +1012,7 @@ polymon(int mntmp) disp.botl = TRUE; gv.vision_full_recalc = 1; see_monsters(); - (void) encumber_msg(); + encumber_msg(); retouch_equipment(2); /* this might trigger a recursive call to polymon() [stone golem @@ -1398,7 +1398,7 @@ rehumanize(void) disp.botl = TRUE; gv.vision_full_recalc = 1; - (void) encumber_msg(); + encumber_msg(); if (was_flying && !Flying && u.usteed) You("and %s return gently to the %s.", mon_nam(u.usteed), surface(u.ux, u.uy)); diff --git a/src/pray.c b/src/pray.c index 5f00487d1..0564f3672 100644 --- a/src/pray.c +++ b/src/pray.c @@ -550,7 +550,7 @@ fix_worst_trouble(int trouble) disp.botl = TRUE; } } - (void) encumber_msg(); + encumber_msg(); break; case TROUBLE_BLIND: { /* handles deafness as well as blindness */ char msgbuf[BUFSZ]; @@ -1263,7 +1263,7 @@ pleased(aligntyp g_align) if (ABASE(A_STR) < AMAX(A_STR)) { ABASE(A_STR) = AMAX(A_STR); disp.botl = TRUE; /* before potential message */ - (void) encumber_msg(); + encumber_msg(); } if (u.uhunger < 900) init_uhunger(); diff --git a/src/steal.c b/src/steal.c index f90d66a55..a5f771291 100644 --- a/src/steal.c +++ b/src/steal.c @@ -601,7 +601,7 @@ steal(struct monst *mtmp, char *objnambuf) && mtmp->data->mlet == S_NYMPH) ++named; urgent_pline("%s stole %s.", named ? "She" : Monnambuf, doname(otmp)); - (void) encumber_msg(); + encumber_msg(); could_petrify = (otmp->otyp == CORPSE && touch_petrifies(&mons[otmp->corpsenm])); otmp->how_lost = LOST_STOLEN; @@ -762,7 +762,7 @@ stealamulet(struct monst *mtmp) pline("%s steals %s!", Some_Monnam(mtmp), buf); if (can_teleport(mtmp->data) && !tele_restrict(mtmp)) (void) rloc(mtmp, RLOC_MSG); - (void) encumber_msg(); + encumber_msg(); } } @@ -799,7 +799,7 @@ maybe_absorb_item( otense(obj, "are"), hand_s); } freeinv(obj); - (void) encumber_msg(); + encumber_msg(); } else { /* not carried; presumably thrown or kicked */ if (canspotmon(mon)) diff --git a/src/steed.c b/src/steed.c index 48d537b99..7b28794ae 100644 --- a/src/steed.c +++ b/src/steed.c @@ -811,7 +811,7 @@ dismount_steed( (void) float_down(0L, W_SADDLE); gi.in_steed_dismounting = FALSE; disp.botl = TRUE; - (void) encumber_msg(); + encumber_msg(); gv.vision_full_recalc = 1; } else disp.botl = TRUE; diff --git a/src/trap.c b/src/trap.c index 46c0e1266..cfeec5ace 100644 --- a/src/trap.c +++ b/src/trap.c @@ -3907,7 +3907,7 @@ float_up(void) float_vs_flight(); /* set BFlying, also BLevitation if still trapped */ /* levitation gives maximum carrying capacity, so encumbrance state might be reduced */ - (void) encumber_msg(); + encumber_msg(); return; } @@ -3954,7 +3954,7 @@ float_down( : (u.utraptype == TT_BURIEDBALL) ? "chain" : (u.utraptype == TT_LAVA) ? "lava" : "ground"); /* TT_INFLOOR */ - (void) encumber_msg(); /* carrying capacity might have changed */ + encumber_msg(); /* carrying capacity might have changed */ return 0; } disp.botl = TRUE; @@ -3965,14 +3965,14 @@ float_down( * unless hero is stuck in floor */ if (Flying) { You("have stopped levitating and are now flying."); - (void) encumber_msg(); /* carrying capacity might have changed */ + encumber_msg(); /* carrying capacity might have changed */ return 1; } } if (u.uswallow) { You("float down, but you are still %s.", digests(u.ustuck->data) ? "swallowed" : "engulfed"); - (void) encumber_msg(); + encumber_msg(); return 1; } @@ -4056,7 +4056,7 @@ float_down( /* levitation gives maximum carrying capacity, so having it end potentially triggers greater encumbrance; do this after 'come down' messages, before trap activation or autopickup */ - (void) encumber_msg(); + encumber_msg(); /* can't rely on u.uz0 for detecting trap door-induced level change; it gets changed to reflect the new level before we can check it */ diff --git a/src/wield.c b/src/wield.c index 53b0aa94f..f24ae8e1e 100644 --- a/src/wield.c +++ b/src/wield.c @@ -958,7 +958,7 @@ chwepon(struct obj *otmp, int amount) if (otyp != STRANGE_OBJECT) makeknown(otyp); if (multiple) - (void) encumber_msg(); + encumber_msg(); return 1; } else if (uwep->otyp == CRYSKNIFE && amount < 0) { multiple = (uwep->quan > 1L); @@ -975,7 +975,7 @@ chwepon(struct obj *otmp, int amount) if (otyp != STRANGE_OBJECT && otmp->bknown) makeknown(otyp); if (multiple) - (void) encumber_msg(); + encumber_msg(); return 1; } diff --git a/src/wizcmds.c b/src/wizcmds.c index 6dd756880..8c14b98f7 100644 --- a/src/wizcmds.c +++ b/src/wizcmds.c @@ -37,7 +37,7 @@ wiz_wish(void) /* Unlimited wishes for debug mode by Paul Polderman */ flags.verbose = FALSE; makewish(); flags.verbose = save_verbose; - (void) encumber_msg(); + encumber_msg(); } else pline(unavailcmd, ecname_from_fn(wiz_wish)); return ECMD_OK; diff --git a/src/zap.c b/src/zap.c index 98b74b04a..16e18db34 100644 --- a/src/zap.c +++ b/src/zap.c @@ -1214,7 +1214,7 @@ unturn_dead(struct monst *mon) } } if (is_u && res) - (void) encumber_msg(); + encumber_msg(); return res; } From 9e08bf8159abae862c80c1d31c3f08d8f221124c Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 24 Nov 2025 08:45:45 -0500 Subject: [PATCH 114/442] submodules update for pdcurses --- submodules/pdcurses | 2 +- submodules/pdcursesmod | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/submodules/pdcurses b/submodules/pdcurses index 5b5c3d349..09cf16db2 160000 --- a/submodules/pdcurses +++ b/submodules/pdcurses @@ -1 +1 @@ -Subproject commit 5b5c3d349b9948cc37da39c985d65f395059e833 +Subproject commit 09cf16db29753305e4241d4ae609aac997fa730d diff --git a/submodules/pdcursesmod b/submodules/pdcursesmod index e2b2205da..dd794f6c9 160000 --- a/submodules/pdcursesmod +++ b/submodules/pdcursesmod @@ -1 +1 @@ -Subproject commit e2b2205da6b3256f9a66e60592853e6c1d9c4a59 +Subproject commit dd794f6c98b6d3d6acbe0910c754559177fb56a5 From 88256d895a052f495a2fc5c3f5220e538ed5cf6a Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 24 Nov 2025 08:54:32 -0500 Subject: [PATCH 115/442] update CHKSUMS to add entry for current Lua 5.4.8 --- submodules/CHKSUMS | 1 + 1 file changed, 1 insertion(+) diff --git a/submodules/CHKSUMS b/submodules/CHKSUMS index bea046547..e69f3f9b9 100644 --- a/submodules/CHKSUMS +++ b/submodules/CHKSUMS @@ -6,3 +6,4 @@ # shasum -a 256 FILETOCHECK >>THISFILE 7d5ea1b9cb6aa0b59ca3dde1c6adcb57ef83a1ba8e5432c0ecd06bf439b3ad88 lua-5.4.6.tar.gz 9fbf5e28ef86c69858f6d3d34eccc32e911c1a28b4120ff3e84aaa70cfbf1e30 lua-5.4.7.tar.gz +4f18ddae154e793e46eeab727c59ef1c0c0c2b744e7b94219710d76f530629ae lua-5.4.8.tar.gz From 7b5d7d7ae6d5b50de4023083abbcf25116aacb13 Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 24 Nov 2025 12:37:08 -0800 Subject: [PATCH 116/442] fix issue #1462 - objects embedded in trees Issue reported by chappg: on arboreal levels, when an object was located at a stone location treated as a tree location, examining the object would report it as embedded in stone. The Ranger quest has arboreal levels where STONE becomes TREE, and items that would become embedded in stone will be in trees instead. (Sometimes kicking a tree would drop fruit onto an adjacent tree, effectively embedding it. For testing, it's easier just to poly into a xorn, walk onto the tree spot, and drop something.) The item description code for farlook and quicklook wasn't checking for that. The fix also corrects another bug: an item located at a normal tree location would just be described as itself with no mention of the tree at all. Attempting to walk onto it would report the terrain and not let you move there (assuming not in xorn form), like trying to walk into a wall. Fixes #1462 --- doc/fixes3-7-0.txt | 6 +++++- include/extern.h | 3 ++- src/mkobj.c | 14 +++++++++++++- src/pager.c | 10 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 7ed444fe6..d0a068fce 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1,4 +1,4 @@ -NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1576 $ $NHDT-Date: 1763708572 2025/11/20 23:02:52 $ +NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1578 $ $NHDT-Date: 1764044196 2025/11/24 20:16:36 $ General Fixes and Modified Features ----------------------------------- @@ -1537,6 +1537,10 @@ travel couldn't find the vibrating square if it was covered by an object or travel would stop one step in front of known vibrating square like other traps fix bug which delayed burden changes due to monster actions (e.g. reverting to natural form due to damage, changing carry capacity) for one turn +on arboreal levels (Ranger quest) where STONE terrain is treated as TREE, an + object at a tree location would be described as "embedded in stone" +an item at an ordinary tree location, whether the level is arboreal or not, + would be described as itself with no mention of the tree Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/include/extern.h b/include/extern.h index a5a7a76e7..f86732ab4 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 extern.h $NHDT-Date: 1738638877 2025/02/03 19:14:37 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1476 $ */ +/* NetHack 3.7 extern.h $NHDT-Date: 1764044196 2025/11/24 20:16:36 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1509 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1657,6 +1657,7 @@ extern struct obj *mk_named_object(int, struct permonst *, coordxy, coordxy, const char *) ; extern struct obj *rnd_treefruit_at(coordxy, coordxy); +extern boolean is_treefruit(struct obj *) NONNULLARG1; extern void set_corpsenm(struct obj *, int) NONNULLARG1; extern long rider_revival_time(struct obj *, boolean) NONNULLARG1; extern void start_corpse_timeout(struct obj *) NONNULLARG1; diff --git a/src/mkobj.c b/src/mkobj.c index 77479c57b..7434981be 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mkobj.c $NHDT-Date: 1737528890 2025/01/21 22:54:50 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.315 $ */ +/* NetHack 3.7 mkobj.c $NHDT-Date: 1764044196 2025/11/24 20:16:36 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.326 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1979,6 +1979,18 @@ rnd_treefruit_at(coordxy x, coordxy y) return mksobj_at(ROLL_FROM(treefruits), x, y, TRUE, FALSE); } +/* for describing objects embedded in trees */ +boolean +is_treefruit(struct obj *otmp) +{ + int fruitidx; + + for (fruitidx = 0; fruitidx < SIZE(treefruits); ++fruitidx) + if (treefruits[fruitidx] == otmp->otyp) + return TRUE; + return FALSE; +} + /* create a stack of N gold pieces; never returns Null */ struct obj * mkgold(long amount, coordxy x, coordxy y) diff --git a/src/pager.c b/src/pager.c index 061937687..70eb1fe80 100644 --- a/src/pager.c +++ b/src/pager.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 pager.c $NHDT-Date: 1737013431 2025/01/15 23:43:51 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.287 $ */ +/* NetHack 3.7 pager.c $NHDT-Date: 1764044196 2025/11/24 20:16:36 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.292 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2018. */ /* NetHack may be freely redistributed. See license for details. */ @@ -394,11 +394,17 @@ look_at_object( otmp->where = OBJ_FREE; /* object_from_map set it to OBJ_FLOOR */ dealloc_obj(otmp), otmp = NULL; /* has no contents */ } - } else + } else { Strcpy(buf, something); /* sanity precaution */ + } if (otmp && otmp->where == OBJ_BURIED) Strcat(buf, " (buried)"); + /* check TREE before STONE due to level.flags.arboreal */ + else if (IS_TREE(levl[x][y].typ)) + /* "dangling": "hanging" could imply that it's growing on this tree */ + Snprintf(eos(buf), BUFSZ - strlen(buf), " %s in a tree", + (otmp && is_treefruit(otmp)) ? "dangling" : "stuck"); else if (levl[x][y].typ == STONE || levl[x][y].typ == SCORR) Strcat(buf, " embedded in stone"); else if (IS_WALL(levl[x][y].typ) || levl[x][y].typ == SDOOR) From 53247ec474a45aa0e800a21bc5990bf37ed95859 Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 24 Nov 2025 19:27:59 -0500 Subject: [PATCH 117/442] Makefile.nmake update --- 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 083049d22..85772ed51 100644 --- a/sys/windows/Makefile.nmake +++ b/sys/windows/Makefile.nmake @@ -9,7 +9,7 @@ # # Visual Studio Compilers Tested: # - Microsoft Visual Studio Community 2022 v 17.14.21 -# - Microsoft Visual Studio Community 2026 v 18.0.1 +# - Microsoft Visual Studio Community 2026 v 18.0.2 # #============================================================================== # This is used for building two distinct executables of NetHack: @@ -1186,7 +1186,7 @@ rc=Rc.exe # Recently tested versions: TESTEDVS2022 = 14.44.35221.0 # Other versions: -TESTEDVS2026 = 14.50.35718.0 +TESTEDVS2026 = 14.50.35719.0 VS20261ST = 1450000000 VS2026CUR = $(TESTEDVS2026:.=) @@ -2042,7 +2042,7 @@ fetch-Lua: fetch-actual-Lua fetch-actual-Lua: @if not exist $(LIBDIR)*.* mkdir $(R_LIBDIR) cd $(LIBDIR) - curl -R -O http://www.lua.org/ftp/lua-$(LUAVER).tar.gz + curl -L -R -O http://www.lua.org/ftp/lua-$(LUAVER).tar.gz tar zxf lua-$(LUAVER).tar.gz if exist lua-$(LUAVER).tar.gz del lua-$(LUAVER).tar.gz if exist lua-$(LUAVER).tar del lua-$(LUAVER).tar From 10a5e6747846ff4f855de381a5f7572524937e68 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Tue, 25 Nov 2025 21:05:22 +0000 Subject: [PATCH 118/442] Some types of shopkeeper start with a scroll of charging This is partially for the pun, and partially because the "wish for scrolls of charging to identify them" strategy has been nerfed in previous commits and this offers an opportunity to discover what scrolls of charging are without randomly encountering one. --- doc/fixes3-7-0.txt | 3 ++- src/shknam.c | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index d0a068fce..76b5eac0d 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1,4 +1,4 @@ -NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1578 $ $NHDT-Date: 1764044196 2025/11/24 20:16:36 $ +NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1580 $ $NHDT-Date: 1764109066 2025/11/25 22:17:46 $ General Fixes and Modified Features ----------------------------------- @@ -1541,6 +1541,7 @@ on arboreal levels (Ranger quest) where STONE terrain is treated as TREE, an object at a tree location would be described as "embedded in stone" an item at an ordinary tree location, whether the level is arboreal or not, would be described as itself with no mention of the tree +some types of shopkeeper now start with a scroll of charging Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/shknam.c b/src/shknam.c index ecf12f938..657487ed8 100644 --- a/src/shknam.c +++ b/src/shknam.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 shknam.c $NHDT-Date: 1736530208 2025/01/10 09:30:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.82 $ */ +/* NetHack 3.7 shknam.c $NHDT-Date: 1764109114 2025/11/25 22:18:34 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.86 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -682,6 +682,10 @@ shkinit(const struct shclass *shp, struct mkroom *sroom) mkmonmoney(shk, 1000L + 30L * (long) rnd(100)); /* initial capital */ if (shp->shknms == shkrings) (void) mongets(shk, TOUCHSTONE); + if (shp->shknms == shktools || shp->shknms == shkwands || + (shp->shknms == shkrings && rn2(2)) || + (shp->shknms == shkgeneral && rn2(5))) + (void) mongets(shk, SCR_CHARGING); nameshk(shk, shp->shknms); return sh; From 8c29b20010654a7320a4bab0cd135efe46ad5a72 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Tue, 25 Nov 2025 22:42:38 +0000 Subject: [PATCH 119/442] Accurately track which items have been discovered, even if not #named This fixes a couple of bugs: a long-standing bug in which writing a scroll by label could fail even if you've already seen a scroll with that label (due to the game not tracking whether or not you've seen a scroll if it doesn't have a name); and a somewhat newer bug in which spellbooks auto-identified by Wizard knowledge were marked as having been encountered (rather than as known but not encountered). Breaks save file compatibility, but not bones files. --- doc/fixes3-7-0.txt | 3 +++ include/extern.h | 3 ++- include/hack.h | 2 +- include/obj.h | 4 +++- include/objclass.h | 4 ++-- include/patchlevel.h | 2 +- src/apply.c | 2 +- src/artifact.c | 8 +++---- src/detect.c | 20 ++++++++--------- src/display.c | 4 ++-- src/do_name.c | 2 +- src/do_wear.c | 4 ++-- src/eat.c | 18 ++++++++++----- src/end.c | 17 ++++++++------ src/files.c | 2 +- src/fountain.c | 3 ++- src/invent.c | 9 ++++---- src/mhitu.c | 2 +- src/mkobj.c | 3 +++ src/mthrowu.c | 4 ++-- src/muse.c | 12 +++++----- src/nhlobj.c | 2 +- src/o_init.c | 53 ++++++++++++++++++++++++++------------------ src/objnam.c | 7 ++++-- src/pager.c | 4 ++-- src/pickup.c | 2 +- src/polyself.c | 4 ++-- src/potion.c | 10 ++++----- src/pray.c | 11 ++++----- src/quest.c | 2 +- src/shk.c | 2 +- src/sit.c | 3 ++- src/spell.c | 7 +++--- src/trap.c | 8 +++---- src/u_init.c | 9 ++++---- src/uhitm.c | 2 +- src/write.c | 13 ++++------- src/zap.c | 10 ++++----- 38 files changed, 155 insertions(+), 122 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 76b5eac0d..4dd9c189a 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1542,6 +1542,9 @@ on arboreal levels (Ranger quest) where STONE terrain is treated as TREE, an an item at an ordinary tree location, whether the level is arboreal or not, would be described as itself with no mention of the tree some types of shopkeeper now start with a scroll of charging +objects are now accurately tracked as discovered even if not type-named nor + formally identified (fixing some bugs in scroll writing, and making + the discoveries list more accurate) Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/include/extern.h b/include/extern.h index f86732ab4..49b83f784 100644 --- a/include/extern.h +++ b/include/extern.h @@ -2155,7 +2155,8 @@ extern boolean objdescr_is(struct obj *, const char *) NONNULLARG2; extern void oinit(void); extern void savenames(NHFILE *) NONNULLARG1; extern void restnames(NHFILE *) NONNULLARG1; -extern void discover_object(int, boolean, boolean); +extern void observe_object(struct obj *) NONNULLARG1; +extern void discover_object(int, boolean, boolean, boolean); extern void undiscover_object(int); extern boolean interesting_to_discover(int); extern int choose_disco_sort(int); diff --git a/include/hack.h b/include/hack.h index 361b95cd3..e91df8365 100644 --- a/include/hack.h +++ b/include/hack.h @@ -1527,7 +1527,7 @@ typedef uint32_t mmflags_nht; /* makemon MM_ flags */ (objects[(obj)->otyp].a_ac + (obj)->spe \ - min((int) greatest_erosion(obj), objects[(obj)->otyp].a_ac)) -#define makeknown(x) discover_object((x), TRUE, TRUE) +#define makeknown(x) discover_object((x), TRUE, TRUE, TRUE) #define distu(xx, yy) dist2((coordxy) (xx), (coordxy) (yy), u.ux, u.uy) #define mdistu(mon) distu((mon)->mx, (mon)->my) #define onlineu(xx, yy) online2((coordxy)(xx), (coordxy)(yy), u.ux, u.uy) diff --git a/include/obj.h b/include/obj.h index e23c5f457..431094918 100644 --- a/include/obj.h +++ b/include/obj.h @@ -107,7 +107,9 @@ struct obj { * or enchantment); many items have this preset if * they lack anything interesting to discover */ Bitfield(dknown, 1); /* description known (item seen "up close"); - * some types of items always have dknown set */ + * some types of items always have dknown set; + * use observe_object to set to TRUE so that the + * discoveries list is still correct */ Bitfield(bknown, 1); /* BUC (blessed/uncursed/cursed) known */ Bitfield(rknown, 1); /* rustproofing status known */ Bitfield(cknown, 1); /* for containers (including statues): the contents diff --git a/include/objclass.h b/include/objclass.h index 3faf84a16..b3da30b49 100644 --- a/include/objclass.h +++ b/include/objclass.h @@ -54,8 +54,8 @@ struct objclass { * otherwise, obj->dknown and obj->bknown * tell all, and obj->known should always * be set for proper merging behavior. */ - Bitfield(oc_pre_discovered, 1); /* already known at start of game; flagged - * as such when discoveries are listed */ + Bitfield(oc_encountered, 1); /* hero has observed such an item at least + once (perhaps without naming it) */ Bitfield(oc_magic, 1); /* inherently magical object */ Bitfield(oc_charged, 1); /* may have +n or (n) charges */ Bitfield(oc_unique, 1); /* special one-of-a-kind object */ diff --git a/include/patchlevel.h b/include/patchlevel.h index ff02f26bc..42485fef6 100644 --- a/include/patchlevel.h +++ b/include/patchlevel.h @@ -17,7 +17,7 @@ * Incrementing EDITLEVEL can be used to force invalidation of old bones * and save files. */ -#define EDITLEVEL 130 +#define EDITLEVEL 131 /* * Development status possibilities. diff --git a/src/apply.c b/src/apply.c index 4e3788dd4..e8f1a339b 100644 --- a/src/apply.c +++ b/src/apply.c @@ -2683,7 +2683,7 @@ use_stone(struct obj *tstone) /* in case it was acquired while blinded */ if (!Blind) - tstone->dknown = 1; + observe_object(tstone); known = (tstone->otyp == TOUCHSTONE && tstone->dknown && objects[TOUCHSTONE].oc_name_known); Sprintf(stonebuf, "rub on the stone%s", plur(tstone->quan)); diff --git a/src/artifact.c b/src/artifact.c index e4f4c89bf..11db684f0 100644 --- a/src/artifact.c +++ b/src/artifact.c @@ -1570,7 +1570,7 @@ artifact_hit( } *dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER; pline("%s cuts %s in half!", wepdesc, mon_nam(mdef)); - otmp->dknown = TRUE; + observe_object(otmp); return TRUE; } else { if (bigmonst(gy.youmonst.data)) { @@ -1587,7 +1587,7 @@ artifact_hit( */ *dmgptr = 2 * (Upolyd ? u.mh : u.uhp) + FATAL_DAMAGE_MODIFIER; pline("%s cuts you in half!", wepdesc); - otmp->dknown = TRUE; + observe_object(otmp); return TRUE; } } else if (is_art(otmp, ART_VORPAL_BLADE) @@ -1617,7 +1617,7 @@ artifact_hit( mon_nam(mdef)); if (Hallucination && !flags.female) pline("Good job Henry, but that wasn't Anne."); - otmp->dknown = TRUE; + observe_object(otmp); return TRUE; } else { if (!has_head(gy.youmonst.data)) { @@ -1634,7 +1634,7 @@ artifact_hit( } *dmgptr = 2 * (Upolyd ? u.mh : u.uhp) + FATAL_DAMAGE_MODIFIER; pline(ROLL_FROM(behead_msg), wepdesc, "you"); - otmp->dknown = TRUE; + observe_object(otmp); /* Should amulets fall off? */ return TRUE; } diff --git a/src/detect.c b/src/detect.c index a74cdfea4..bf536e127 100644 --- a/src/detect.c +++ b/src/detect.c @@ -26,7 +26,7 @@ staticfn void reconstrain_map(void); staticfn void map_redisplay(void); staticfn void browse_map(unsigned, const char *); staticfn void map_monst(struct monst *, boolean); -staticfn void do_dknown_of(struct obj *); +staticfn void observe_recursively(struct obj *); staticfn boolean check_map_spot(coordxy, coordxy, char, unsigned); staticfn boolean clear_stale_map(char, unsigned); staticfn void sense_trap(struct trap *, coordxy, coordxy, int); @@ -246,14 +246,14 @@ o_material(struct obj *obj, unsigned material) } staticfn void -do_dknown_of(struct obj *obj) +observe_recursively(struct obj *obj) { struct obj *otmp; - obj->dknown = 1; + observe_object(obj); if (Has_contents(obj)) { for (otmp = obj->cobj; otmp; otmp = otmp->nobj) - do_dknown_of(otmp); + observe_recursively(otmp); } } @@ -638,7 +638,7 @@ object_detect(struct obj *detector, /* object doing the detecting */ if (do_dknown) for (obj = gi.invent; obj; obj = obj->nobj) - do_dknown_of(obj); + observe_recursively(obj); for (obj = fobj; obj; obj = obj->nobj) { if ((!class && !boulder) || o_in(obj, class) || o_in(obj, boulder)) { @@ -648,7 +648,7 @@ object_detect(struct obj *detector, /* object doing the detecting */ ct++; } if (do_dknown) - do_dknown_of(obj); + observe_recursively(obj); } for (obj = svl.level.buriedobjlist; obj; obj = obj->nobj) { @@ -659,7 +659,7 @@ object_detect(struct obj *detector, /* object doing the detecting */ ct++; } if (do_dknown) - do_dknown_of(obj); + observe_recursively(obj); } if (u.usteed) @@ -673,7 +673,7 @@ object_detect(struct obj *detector, /* object doing the detecting */ || o_in(obj, boulder)) ct++; if (do_dknown) - do_dknown_of(obj); + observe_recursively(obj); } if ((is_cursed && M_AP_TYPE(mtmp) == M_AP_OBJECT && (!class || class == objects[mtmp->mappearance].oc_class)) @@ -932,7 +932,7 @@ detect_obj_traps( } if (Is_box(otmp) && otmp->otrapped) { otmp->tknown = 1; - otmp->dknown = 1; + observe_object(otmp); result |= u_at(x, y) ? OTRAP_HERE : OTRAP_THERE; if (ft) { flash_glyph_at(x, y, trapglyph, FOUND_FLASH_COUNT); @@ -1508,7 +1508,7 @@ do_vicinity_map( unlike object detection, we don't notice buried items */ otmp = svl.level.objects[zx][zy]; if (extended) - otmp->dknown = 1; + observe_object(otmp); map_object(otmp, TRUE); newglyph = glyph_at(zx, zy); /* if otmp is underwater, we'll need to redisplay the water */ diff --git a/src/display.c b/src/display.c index ba1841cca..e3f8225f8 100644 --- a/src/display.c +++ b/src/display.c @@ -346,7 +346,7 @@ map_object(struct obj *obj, int show) neardist = (r * r) * 2 - r; /* same as r*r + r*(r-1) */ if (distu(x, y) <= neardist) { - obj->dknown = 1; + observe_object(obj); glyph = obj_to_glyph(obj, newsym_rn2); } } @@ -1585,7 +1585,7 @@ see_nearby_objects(void) if (!cansee(ix, iy) || distu(ix, iy) > neardist) continue; - obj->dknown = 1; /* near enough to see it */ + observe_object(obj); /* operate on remembered glyph rather than current one */ glyph = levl[ix][iy].glyph; if (glyph_is_generic_object(glyph)) diff --git a/src/do_name.c b/src/do_name.c index 9a2d2f54c..946fbdd4d 100644 --- a/src/do_name.c +++ b/src/do_name.c @@ -669,7 +669,7 @@ docall(struct obj *obj) undiscover_object(obj->otyp); } else { *uname_p = dupstr(buf); - discover_object(obj->otyp, FALSE, TRUE); /* possibly add to disco[] */ + discover_object(obj->otyp, FALSE, TRUE, TRUE); /* possibly add to disco[] */ } } diff --git a/src/do_wear.c b/src/do_wear.c index 2ee5b0785..bbbf5fbfd 100644 --- a/src/do_wear.c +++ b/src/do_wear.c @@ -1196,12 +1196,12 @@ learnring(struct obj *ring, boolean observed) mark this ring as having been seen (no need for makeknown); otherwise if we have seen this ring, discover its type */ if (objects[ringtype].oc_name_known) - ring->dknown = 1; + observe_object(ring); else if (ring->dknown) makeknown(ringtype); #if 0 /* see learnwand() */ else - ring->eknown = 1; + observe_object(ring); #endif } diff --git a/src/eat.c b/src/eat.c index 3074b9e10..5060b7b36 100644 --- a/src/eat.c +++ b/src/eat.c @@ -1547,7 +1547,8 @@ consume_tin(const char *mesg) mnum = tin->corpsenm; if (mnum == NON_PM) { pline("It turns out to be empty."); - tin->dknown = tin->known = 1; + observe_object(tin); + tin->known = 1; tin = costly_tin(COST_OPEN); use_up_tin(tin); if (always_eat) @@ -1579,8 +1580,10 @@ consume_tin(const char *mesg) if (y_n("Eat it?") == 'n') { if (flags.verbose) You("discard the open tin."); - if (!Hallucination) - tin->dknown = tin->known = 1; + if (!Hallucination) { + observe_object(tin); + tin->known = 1; + } tin = costly_tin(COST_OPEN); use_up_tin(tin); return; @@ -1594,7 +1597,8 @@ consume_tin(const char *mesg) eating_conducts(&mons[mnum]); - tin->dknown = tin->known = 1; + observe_object(tin); + tin->known = 1; /* charge for one at pre-eating cost */ tin = svc.context.tin.tin = costly_tin(COST_OPEN); @@ -1641,7 +1645,8 @@ consume_tin(const char *mesg) Blind ? "" : " ", Blind ? "" : hcolor(NH_GREEN)); } else { pline("It contains spinach."); - tin->dknown = tin->known = 1; + observe_object(tin); + tin->known = 1; } if (!always_eat && y_n("Eat it?") == 'n') { @@ -2265,7 +2270,8 @@ eataccessory(struct obj *otmp) if (u.uhp <= 0) return; /* died from sink fall */ } - otmp->known = otmp->dknown = 1; /* by taste */ + observe_object(otmp); + otmp->known = 1; /* by taste */ if (!rn2(otmp->oclass == RING_CLASS ? 3 : 5)) { switch (otmp->otyp) { default: diff --git a/src/end.c b/src/end.c index ba2ea4f19..30ba0e91d 100644 --- a/src/end.c +++ b/src/end.c @@ -921,7 +921,8 @@ artifact_score( if (counting) { u.urexp = nowrap_add(u.urexp, points); } else { - discover_object(otmp->otyp, TRUE, FALSE); + discover_object(otmp->otyp, TRUE, TRUE, FALSE); + /* not observe_object; dead characters don't observe */ otmp->known = otmp->dknown = otmp->bknown = otmp->rknown = 1; /* assumes artifacts don't have quan > 1 */ Sprintf(pbuf, "%s%s (worth %ld %s and %ld points)", @@ -1254,7 +1255,8 @@ really_done(int how) */ for (obj = gi.invent; obj; obj = nextobj) { nextobj = obj->nobj; - discover_object(obj->otyp, TRUE, FALSE); + discover_object(obj->otyp, TRUE, TRUE, FALSE); + /* observe_object not necessary after discover_object */ obj->known = obj->bknown = obj->dknown = obj->rknown = 1; set_cknown_lknown(obj); /* set flags when applicable */ /* we resolve Schroedinger's cat now in case of both @@ -1496,9 +1498,10 @@ really_done(int how) if (objects[typ].oc_class != GEM_CLASS || typ <= LAST_REAL_GEM) { otmp = mksobj(typ, FALSE, FALSE); - discover_object(otmp->otyp, TRUE, FALSE); - otmp->known = 1; /* for fake amulets */ + discover_object(otmp->otyp, TRUE, TRUE, FALSE); otmp->dknown = 1; /* seen it (blindness fix) */ + /* observe_object not necessary after discover_object */ + otmp->known = 1; /* for fake amulets */ if (has_oname(otmp)) free_oname(otmp); otmp->quan = count; @@ -1634,9 +1637,9 @@ container_contents( (boolean (*)(OBJ_P)) 0); for (srtc = sortedcobj; (obj = srtc->obj) != 0; ++srtc) { if (identified) { - discover_object(obj->otyp, TRUE, FALSE); - obj->known = obj->bknown = obj->dknown - = obj->rknown = 1; + discover_object(obj->otyp, TRUE, TRUE, FALSE); + obj->dknown = 1; /* observe_object unnecessary */ + obj->known = obj->bknown = obj->rknown = 1; if (Is_container(obj) || obj->otyp == STATUE) obj->cknown = obj->lknown = 1; } diff --git a/src/files.c b/src/files.c index c984c673e..3215449e7 100644 --- a/src/files.c +++ b/src/files.c @@ -2515,7 +2515,7 @@ wizkit_addinv(struct obj *obj) return; /* subset of starting inventory pre-ID */ - obj->dknown = 1; + observe_object(obj); if (Role_if(PM_CLERIC)) obj->bknown = 1; /* ok to bypass set_bknown() */ /* same criteria as lift_object()'s check for available inventory slot */ diff --git a/src/fountain.c b/src/fountain.c index 979bb0518..78925b04c 100644 --- a/src/fountain.c +++ b/src/fountain.c @@ -641,7 +641,8 @@ drinksink(void) otmp->cursed = otmp->blessed = 0; pline("Some %s liquid flows from the faucet.", Blind ? "odd" : hcolor(OBJ_DESCR(objects[otmp->otyp]))); - otmp->dknown = !(Blind || Hallucination); + if(!(Blind || Hallucination)) + observe_object(otmp); otmp->quan++; /* Avoid panic upon useup() */ otmp->fromsink = 1; /* kludge for docall() */ (void) dopotion(otmp); diff --git a/src/invent.c b/src/invent.c index 869c97c3e..abb7d7c39 100644 --- a/src/invent.c +++ b/src/invent.c @@ -178,7 +178,7 @@ loot_classify(Loot *sort_item, struct obj *obj) * will put lower valued ones before higher valued ones. */ if (!Blind) - obj->dknown = 1; /* xname(obj) does this; we want it sooner */ + observe_object(obj); /* xname(obj) does this; we want it sooner */ seen = obj->dknown ? TRUE : FALSE, /* class order */ classorder = flags.sortpack ? flags.inv_order : def_srt_order; @@ -1196,7 +1196,7 @@ hold_another_object( char buf[BUFSZ]; if (!Blind) - obj->dknown = 1; /* maximize mergeability */ + observe_object(obj); /* maximize mergeability */ if (obj->oartifact) { /* place_object may change these */ boolean crysknife = (obj->otyp == CRYSKNIFE); @@ -2546,7 +2546,8 @@ fully_identify_obj(struct obj *otmp) makeknown(otmp->otyp); if (otmp->oartifact) discover_artifact((xint16) otmp->oartifact); - otmp->known = otmp->dknown = otmp->bknown = otmp->rknown = 1; + observe_object(otmp); + otmp->known = otmp->bknown = otmp->rknown = 1; set_cknown_lknown(otmp); /* set otmp->{cknown,lknown} if applicable */ if (otmp->otyp == EGG && otmp->corpsenm != NON_PM) learn_egg_type(otmp->corpsenm); @@ -6123,7 +6124,7 @@ display_binventory(coordxy x, coordxy y, boolean as_if_seen) for (n = 0, obj = svl.level.buriedobjlist; obj; obj = obj->nobj) if (obj->ox == x && obj->oy == y) { if (as_if_seen) - obj->dknown = 1; + observe_object(obj); n++; } diff --git a/src/mhitu.c b/src/mhitu.c index 21622c012..8599136ed 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -2128,7 +2128,7 @@ doseduce(struct monst *mon) /* have her call your gloves by their correct name, possibly revealing them to you */ if (yourgloves) - yourgloves->dknown = 1; + observe_object(yourgloves); verbalize("Well, then you owe me %s%s!", yourgloves ? yname(yourgloves) : "twelve pairs of gloves", diff --git a/src/mkobj.c b/src/mkobj.c index 7434981be..31fe3c89a 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -834,6 +834,8 @@ static const char dknowns[] = { WAND_CLASS, RING_CLASS, POTION_CLASS, void clear_dknown(struct obj *obj) { + /* note: this is an unobserving not an observing, so don't call + observe_object even if dknown is being set to 1 */ obj->dknown = strchr(dknowns, obj->oclass) ? 0 : 1; if ((obj->otyp >= ELVEN_SHIELD && obj->otyp <= ORCISH_SHIELD) || obj->otyp == SHIELD_OF_REFLECTION @@ -960,6 +962,7 @@ mksobj_init(struct obj **obj, boolean artif) we initialize glob->owt explicitly so weight() doesn't need to perform any fix up and returns glob->owt as-is */ otmp->owt = objects[otmp->otyp].oc_weight; + /* dknown, but not observed */ otmp->known = otmp->dknown = 1; otmp->corpsenm = PM_GRAY_OOZE + (otmp->otyp - GLOB_OF_GRAY_OOZE); start_glob_timeout(otmp, 0L); diff --git a/src/mthrowu.c b/src/mthrowu.c index 7b2a23ca7..e19ca4ce9 100644 --- a/src/mthrowu.c +++ b/src/mthrowu.c @@ -334,7 +334,7 @@ ohitmon( ismimic = M_AP_TYPE(mtmp) && M_AP_TYPE(mtmp) != M_AP_MONSTER; vis = cansee(gb.bhitpos.x, gb.bhitpos.y); if (vis) - otmp->dknown = 1; + observe_object(otmp); tmp = 5 + find_mac(mtmp) + omon_adj(mtmp, otmp, FALSE); /* High level monsters will be more likely to hit */ @@ -652,7 +652,7 @@ m_throw( singleobj->ox = gb.bhitpos.x += dx; singleobj->oy = gb.bhitpos.y += dy; if (cansee(gb.bhitpos.x, gb.bhitpos.y)) - singleobj->dknown = 1; + observe_object(singleobj); mtmp = m_at(gb.bhitpos.x, gb.bhitpos.y); if (mtmp && shade_miss(mon, mtmp, singleobj, TRUE, TRUE)) { diff --git a/src/muse.c b/src/muse.c index 42243c832..f4d270e48 100644 --- a/src/muse.c +++ b/src/muse.c @@ -207,7 +207,7 @@ mplayhorn( ? "nearby" : "in the distance"); unknow_object(otmp); /* hero loses info when unseen obj is used */ } else if (self) { - otmp->dknown = 1; + observe_object(otmp); objnamp = xname(otmp); if (strlen(objnamp) >= QBUFSZ) objnamp = simpleonames(otmp); @@ -216,7 +216,7 @@ mplayhorn( pline("%s!", monverbself(mtmp, Monnam(mtmp), "play", objbuf)); makeknown(otmp->otyp); /* (wands handle this slightly differently) */ } else { - otmp->dknown = 1; + observe_object(otmp); objnamp = xname(otmp); if (strlen(objnamp) >= QBUFSZ) objnamp = simpleonames(otmp); @@ -242,7 +242,7 @@ mreadmsg(struct monst *mtmp, struct obj *otmp) if (!vismon && Deaf) return; /* no feedback */ - otmp->dknown = 1; /* seeing or hearing scroll read reveals its label */ + observe_object(otmp); /* seeing/hearing scroll read reveals its label */ Strcpy(onambuf, singular(otmp, vismon ? doname : ansimpleoname)); if (vismon) { @@ -291,7 +291,7 @@ staticfn void mquaffmsg(struct monst *mtmp, struct obj *otmp) { if (canseemon(mtmp)) { - otmp->dknown = 1; + observe_object(otmp); pline_mon(mtmp, "%s drinks %s!", Monnam(mtmp), singular(otmp, doname)); } else if (!Deaf) { Soundeffect(se_mon_chugging_potion, 25); @@ -1967,7 +1967,7 @@ use_offensive(struct monst *mtmp) * are not objects. Also set dknown in mthrowu.c. */ if (cansee(mtmp->mx, mtmp->my)) { - otmp->dknown = 1; + observe_object(otmp); pline_mon(mtmp, "%s hurls %s!", Monnam(mtmp), singular(otmp, doname)); } @@ -3129,7 +3129,7 @@ muse_unslime( vis |= canseemon(mon); /* burning potion may improve visibility */ if (vis) { if (!Unaware) - obj->dknown = 1; /* hero is watching mon drink obj */ + observe_object(obj); /* hero is watching mon drink obj */ pline("%s quaffs a burning %s", saw_lit ? upstart(strcpy(Pronoun, mhe(mon))) : Monnam(mon), simpleonames(obj)); diff --git a/src/nhlobj.c b/src/nhlobj.c index e06fc4b59..0579ddf47 100644 --- a/src/nhlobj.c +++ b/src/nhlobj.c @@ -210,7 +210,7 @@ l_obj_objects_to_table(lua_State *L) nhl_add_table_entry_int(L, "name_known", o->oc_name_known); nhl_add_table_entry_int(L, "merge", o->oc_merge); nhl_add_table_entry_int(L, "uses_known", o->oc_uses_known); - nhl_add_table_entry_int(L, "pre_discovered", o->oc_pre_discovered); + nhl_add_table_entry_int(L, "encountered", o->oc_encountered); nhl_add_table_entry_int(L, "magic", o->oc_magic); nhl_add_table_entry_int(L, "charged", o->oc_charged); nhl_add_table_entry_int(L, "unique", o->oc_unique); diff --git a/src/o_init.c b/src/o_init.c index 1185c177f..06b88e57e 100644 --- a/src/o_init.c +++ b/src/o_init.c @@ -377,7 +377,7 @@ savenames(NHFILE *nhfp) unsigned int len; if (update_file(nhfp)) { - for (i = 0; i < (MAXOCLASSES + 2); ++i) { + for (i = 0; i < (MAXOCLASSES + 2); ++i) { Sfo_int(nhfp, &svb.bases[i], "names-bases"); } for (i = 0; i < NUM_OBJECTS; ++i) { @@ -436,41 +436,50 @@ restnames(NHFILE *nhfp) } #ifndef SFCTOOL +/* make the object dknown and mark it as encountered */ +void +observe_object(struct obj *obj) +{ + obj->dknown = 1; + discover_object(obj->otyp, FALSE, TRUE, FALSE); +} + void discover_object( int oindx, boolean mark_as_known, + boolean mark_as_encountered, boolean credit_hero) { - if (!objects[oindx].oc_name_known + if ((!objects[oindx].oc_name_known && mark_as_known) + || (!objects[oindx].oc_encountered && mark_as_encountered) || (Role_if(PM_SAMURAI) && Japanese_item_name(oindx, (const char *) 0))) { int dindx, acls = objects[oindx].oc_class; /* Loop thru disco[] 'til we find the target (which may have been uname'd) or the next open slot; one or the other will be found - before we reach the next class... - */ + before we reach the next class... */ for (dindx = svb.bases[acls]; svd.disco[dindx] != 0; dindx++) if (svd.disco[dindx] == oindx) break; svd.disco[dindx] = oindx; - /* if already known, we forced an item with a Japanese name into - disco[] but don't want to exercise wisdom or update perminv */ - if (objects[oindx].oc_name_known) - return; + if (mark_as_encountered) + objects[oindx].oc_encountered = 1; - if (mark_as_known) { + if (!objects[oindx].oc_name_known && mark_as_known) { objects[oindx].oc_name_known = 1; if (credit_hero) exercise(A_WIS, TRUE); - } - /* !in_moveloop => initial inventory, gameover => final disclosure */ - if (program_state.in_moveloop && !program_state.gameover) { - if (objects[oindx].oc_class == GEM_CLASS) - gem_learned(oindx); /* could affect price of unpaid gems */ - update_inventory(); + + /* !in_moveloop => initial inventory, + gameover => final disclosure */ + if (program_state.in_moveloop && !program_state.gameover) { + if (objects[oindx].oc_class == GEM_CLASS) + gem_learned(oindx); /* could affect price of unpaid gems */ + update_inventory(); + } } } } @@ -514,9 +523,11 @@ interesting_to_discover(int i) if (Role_if(PM_SAMURAI) && Japanese_item_name(i, (const char *) 0)) return TRUE; - /* Pre-discovered objects are now printed with a '*' */ + /* Objects that were discovered without encountering them are now printed + with a '*' */ return (boolean) (objects[i].oc_uname != (char *) 0 - || (objects[i].oc_name_known + || ((objects[i].oc_name_known + || objects[i].oc_encountered) && OBJ_DESCR(objects[i]) != (char *) 0)); } @@ -550,7 +561,7 @@ sortloot_descr(int otyp, char *outbuf) o = cg.zeroobj; o.otyp = otyp; o.oclass = objects[otyp].oc_class; - o.dknown = 1; + o.dknown = 1; /* not observe_object, this isn't a real object */ o.known = (objects[otyp].oc_name_known || !objects[otyp].oc_uses_known) ? 1 : 0; o.corpsenm = NON_PM; /* suppress statue and figurine details */ @@ -784,7 +795,7 @@ dodiscovered(void) /* free after Robert Viduya */ prev_class = oclass; } } - Strcpy(buf, objects[dis].oc_pre_discovered ? "* " : " "); + Strcpy(buf, objects[dis].oc_encountered ? " " : "* "); if (lootsort) (void) sortloot_descr(dis, &buf[2]); disco_append_typename(buf, dis); @@ -1011,7 +1022,7 @@ doclassdisco(void) ++i) { if ((dis = svd.disco[i]) != 0 && interesting_to_discover(dis)) { ++ct; - Strcpy(buf, objects[dis].oc_pre_discovered ? "* " : " "); + Strcpy(buf, objects[dis].oc_encountered ? " " : "* "); if (lootsort) (void) sortloot_descr(dis, &buf[2]); disco_append_typename(buf, dis); @@ -1113,7 +1124,7 @@ rename_disco(void) odummy.oclass = objects[dis].oc_class; odummy.quan = 1L; odummy.known = !objects[dis].oc_uses_known; - odummy.dknown = 1; + odummy.dknown = 1; /* not observe_object: it isn't real */ docall(&odummy); } } diff --git a/src/objnam.c b/src/objnam.c index e05ea1c12..ee9a29836 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -625,7 +625,7 @@ xname_flags( if (!nn && ocl->oc_uses_known && ocl->oc_unique) obj->known = 0; if (!Blind && !gd.distantname) - obj->dknown = 1; + observe_object(obj); if (Role_if(PM_CLERIC)) obj->bknown = 1; /* avoid set_bknown() to bypass update_inventory() */ @@ -1057,6 +1057,8 @@ minimal_xname(struct obj *obj) bareobj = cg.zeroobj; bareobj.otyp = otyp; bareobj.oclass = obj->oclass; + /* not observe_object, either the hero observed the object already or this + is overriding ID and shouldn't discover the object */ bareobj.dknown = (obj->dknown || iflags.override_ID) ? 1 : 0; /* suppress known except for amulets (needed for fakes and real A-of-Y) */ bareobj.known = (obj->oclass == AMULET_CLASS) @@ -1945,7 +1947,8 @@ killer_xname(struct obj *obj) save_oname = ONAME(obj); /* killer name should be more specific than general xname; however, exact - info like blessed/cursed and rustproof makes things be too verbose */ + info like blessed/cursed and rustproof makes things be too verbose; set + dknown (not observe_object) because dead characters don't observe */ obj->known = obj->dknown = 1; obj->bknown = obj->rknown = obj->greased = 0; /* if character is a priest[ess], bknown will get toggled back on */ diff --git a/src/pager.c b/src/pager.c index 70eb1fe80..9f9ba087b 100644 --- a/src/pager.c +++ b/src/pager.c @@ -366,11 +366,11 @@ object_from_map( && (fakeobj || otmp->where == OBJ_FLOOR) /* not buried */ /* terrain mode views what's already known, doesn't learn new stuff */ && !iflags.terrainmode) /* so don't set dknown when in terrain mode */ - otmp->dknown = 1; /* if a pile, clearly see the top item only */ + observe_object(otmp); /* if a pile, clearly see the top item only */ if (fakeobj && mtmp && mimic_obj && (otmp->dknown || (M_AP_FLAG(mtmp) & M_AP_F_DKNOWN))) { mtmp->m_ap_type |= M_AP_F_DKNOWN; - otmp->dknown = 1; + observe_object(otmp); } *obj_p = otmp; return fakeobj; /* when True, caller needs to dealloc *obj_p */ diff --git a/src/pickup.c b/src/pickup.c index 4a366f757..26655a6c6 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -1816,7 +1816,7 @@ pickup_object( /* In case of auto-pickup, where we haven't had a chance to look at it yet; affects docall(SCR_SCARE_MONSTER). */ if (!Blind) - obj->dknown = 1; + observe_object(obj); if (obj == uchain) { /* do not pick up attached chain */ return 0; diff --git a/src/polyself.c b/src/polyself.c index 669f32da4..14fe3f1cc 100644 --- a/src/polyself.c +++ b/src/polyself.c @@ -646,7 +646,7 @@ polyself(int psflags) re-converting scales to mail poses risk of evaporation due to over enchanting */ uarm->otyp += GRAY_DRAGON_SCALES - GRAY_DRAGON_SCALE_MAIL; - uarm->dknown = 1; + observe_object(uarm); disp.botl = TRUE; /* AC is changing */ } uskin = uarm; @@ -1371,7 +1371,7 @@ rehumanize(void) return; /* don't rehumanize after all */ } else if (uamul && uamul->otyp == AMULET_OF_UNCHANGING) { Your("%s %s!", simpleonames(uamul), otense(uamul, "fail")); - uamul->dknown = 1; + observe_object(uamul); makeknown(AMULET_OF_UNCHANGING); } } diff --git a/src/potion.c b/src/potion.c index d537daf5c..ca7a2eaa5 100644 --- a/src/potion.c +++ b/src/potion.c @@ -2733,10 +2733,10 @@ potion_dip(struct obj *obj, struct obj *potion) else singlepotion->cursed = obj->cursed; /* odiluted left as-is */ singlepotion->bknown = FALSE; - if (Blind) { - singlepotion->dknown = FALSE; - } else { - singlepotion->dknown = !Hallucination; + singlepotion->dknown = FALSE; /* provisionally */ + if (!Blind) { + if (!Hallucination) + observe_object(singlepotion); *newbuf = '\0'; if (mixture == POT_WATER && singlepotion->dknown) Sprintf(newbuf, "clears"); @@ -2756,7 +2756,7 @@ potion_dip(struct obj *obj, struct obj *potion) struct obj fakeobj; fakeobj = cg.zeroobj; - fakeobj.dknown = 1; + fakeobj.dknown = 1; /* no need to observe_object */ fakeobj.otyp = old_otyp; fakeobj.oclass = POTION_CLASS; docall(&fakeobj); diff --git a/src/pray.c b/src/pray.c index 0564f3672..b515374d7 100644 --- a/src/pray.c +++ b/src/pray.c @@ -879,7 +879,7 @@ gcrownu(void) * even if hero doesn't know book */ bless(obj); obj->bknown = 1; /* ok to skip set_bknown() */ - obj->dknown = 1; + observe_object(obj); at_your_feet(upstart(ansimpleoname(obj))); dropy(obj); u.ugifts++; @@ -923,7 +923,7 @@ gcrownu(void) ; /* already got bonus above */ } else if (obj && in_hand) { Your("%s goes snicker-snack!", xname(obj)); - obj->dknown = 1; + observe_object(obj); } else if (!already_exists) { obj = mksobj(LONG_SWORD, FALSE, FALSE); obj = oname(obj, artiname(ART_VORPAL_BLADE), @@ -949,7 +949,7 @@ gcrownu(void) ; /* already got bonus above */ } else if (obj && in_hand) { Your("%s hums ominously!", swordbuf); - obj->dknown = 1; + observe_object(obj); } else if (!already_exists) { obj = mksobj(RUNESWORD, FALSE, FALSE); obj = oname(obj, artiname(ART_STORMBRINGER), @@ -1052,7 +1052,8 @@ give_spell(void) } obfree(otmp, (struct obj *) 0); /* discard the book */ } else { - otmp->dknown = 1; /* not bknown */ + observe_object(otmp); + /* don't set bknown */ /* discovering blank paper will make it less likely to be given again; small chance to arbitrarily discover some other book type without having to read it first */ @@ -1824,7 +1825,7 @@ bestow_artifact(uchar max_giftvalue) /* make sure we can use this weapon */ unrestrict_weapon_skill(weapon_type(otmp)); if (!Hallucination && !Blind) { - otmp->dknown = 1; + observe_object(otmp); makeknown(otmp->otyp); discover_artifact(otmp->oartifact); } diff --git a/src/quest.c b/src/quest.c index b554a664c..a436e2787 100644 --- a/src/quest.c +++ b/src/quest.c @@ -127,7 +127,7 @@ artitouch(struct obj *obj) if (!Qstat(touched_artifact)) { /* in case we haven't seen the item yet (ie, currently blinded), this quest message describes it by name so mark it as seen */ - obj->dknown = 1; + observe_object(obj); /* only give this message once */ Qstat(touched_artifact) = TRUE; qt_pager("gotit"); diff --git a/src/shk.c b/src/shk.c index d493caefb..4b1aedc1e 100644 --- a/src/shk.c +++ b/src/shk.c @@ -3360,7 +3360,7 @@ shk_names_obj( char *obj_name, fmtbuf[BUFSZ]; boolean was_unknown = !obj->dknown; - obj->dknown = TRUE; + observe_object(obj); /* Use real name for ordinary weapons/armor, and spell-less * scrolls/books (that is, blank and mail), but only if the * object is within the shk's area of interest/expertise. diff --git a/src/sit.c b/src/sit.c index 40628c1b0..62fc1f017 100644 --- a/src/sit.c +++ b/src/sit.c @@ -371,7 +371,8 @@ lay_an_egg(void) uegg->owt = weight(uegg); /* this sets hatch timers if appropriate */ set_corpsenm(uegg, egg_type_from_parent(u.umonnum, FALSE)); - uegg->known = uegg->dknown = 1; + uegg->known = 1; + observe_object(uegg); You("%s an egg.", eggs_in_water(gy.youmonst.data) ? "spawn" : "lay"); dropy(uegg); stackobj(uegg); diff --git a/src/spell.c b/src/spell.c index 03f5ee788..1f2c6a823 100644 --- a/src/spell.c +++ b/src/spell.c @@ -234,7 +234,7 @@ deadbook(struct obj *book2) You("turn the pages of the Book of the Dead..."); makeknown(SPE_BOOK_OF_THE_DEAD); - book2->dknown = 1; /* in case blind now and hasn't been seen yet */ + observe_object(book2); /* in case blind now and hasn't been seen yet */ /* KMH -- Need ->known to avoid "_a_ Book of the Dead" */ book2->known = 1; if (invocation_pos(u.ux, u.uy) && !On_stairs(u.ux, u.uy)) { @@ -896,8 +896,9 @@ skill_based_spellbook_id(void) } if (objects[booktype].oc_level <= known_up_to_level) - /* makeknown(booktype) but don't exercise Wisdom */ - discover_object(booktype, TRUE, FALSE); + /* makeknown(booktype) but don't exercise Wisdom or mark as + encountered */ + discover_object(booktype, TRUE, FALSE, FALSE); } } diff --git a/src/trap.c b/src/trap.c index cfeec5ace..a5602cd5a 100644 --- a/src/trap.c +++ b/src/trap.c @@ -1206,7 +1206,7 @@ trapeffect_arrow_trap( } else { place_object(otmp, u.ux, u.uy); if (!Blind) - otmp->dknown = 1; + observe_object(otmp); stackobj(otmp); newsym(u.ux, u.uy); } @@ -1276,7 +1276,7 @@ trapeffect_dart_trap( } else { place_object(otmp, u.ux, u.uy); if (!Blind) - otmp->dknown = 1; + observe_object(otmp); stackobj(otmp); newsym(u.ux, u.uy); } @@ -1352,7 +1352,7 @@ trapeffect_rocktrap( harmless = TRUE; } if (!Blind) - otmp->dknown = 1; + observe_object(otmp); stackobj(otmp); newsym(u.ux, u.uy); /* map the rock */ @@ -5738,7 +5738,7 @@ untrap_box( else pline("There's a trap on %s.", the(xname(box))); box->tknown = 1; - box->dknown = 1; + observe_object(box); if (!confused) exercise(A_WIS, TRUE); diff --git a/src/u_init.c b/src/u_init.c index e4d60dd62..72bf3043c 100644 --- a/src/u_init.c +++ b/src/u_init.c @@ -562,8 +562,8 @@ knows_object(int obj, boolean override_pauper) { if (u.uroleplay.pauper && !override_pauper) return; - discover_object(obj, TRUE, FALSE); - objects[obj].oc_pre_discovered = 1; /* not a "discovery" */ + /* mark as known, but not yet encountered */ + discover_object(obj, TRUE, FALSE, FALSE); } /* Know ordinary (non-magical) objects of a certain class, @@ -1207,6 +1207,7 @@ ini_inv_adjust_obj(struct trobj *trop, struct obj *obj) } else { if (objects[obj->otyp].oc_uses_known) obj->known = 1; + /* not observe_object during startup, that's handled later */ obj->dknown = obj->bknown = obj->rknown = 1; if (Is_container(obj) || obj->otyp == STATUE) { obj->cknown = obj->lknown = 1; @@ -1246,9 +1247,9 @@ ini_inv_use_obj(struct obj *obj) { /* Make the type known if necessary */ if (OBJ_DESCR(objects[obj->otyp]) && obj->known) - discover_object(obj->otyp, TRUE, FALSE); + discover_object(obj->otyp, TRUE, TRUE, FALSE); if (obj->otyp == OIL_LAMP) - discover_object(POT_OIL, TRUE, FALSE); + discover_object(POT_OIL, TRUE, TRUE, FALSE); if (obj->oclass == ARMOR_CLASS) { if (is_shield(obj) && !uarms && !(uwep && bimanual(uwep))) { diff --git a/src/uhitm.c b/src/uhitm.c index 160d2e0a2..316858896 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -1133,7 +1133,7 @@ hmon_hitmon_misc_obj( corpse_xname(obj, (const char *) 0, obj->dknown ? CXN_PFX_THE : CXN_ARTICLE)); - obj->dknown = 1; + observe_object(obj); if (!munstone(mon, TRUE)) minstapetrify(mon, TRUE); if (resists_ston(mon)) diff --git a/src/write.c b/src/write.c index fd967685e..312072a58 100644 --- a/src/write.c +++ b/src/write.c @@ -143,7 +143,7 @@ dowrite(struct obj *pen) return ECMD_OK; } } - paper->dknown = 1; + observe_object(paper); if (paper->otyp != SCR_BLANK_PAPER && paper->otyp != SPE_BLANK_PAPER) { pline("That %s is not blank!", typeword); exercise(A_WIS, FALSE); @@ -326,13 +326,8 @@ dowrite(struct obj *pen) * * Writing by description requires that the hero knows the * description (a scroll's label, that is, since books by_descr - * are rejected above). BUG: We can only do this for known - * scrolls and for the case where the player has assigned a - * name to put it onto the discoveries list; we lack a way to - * track other scrolls which have been seen closely enough to - * read the label without then being ID'd or named. The only - * exception is for currently carried inventory, where we can - * check for one [with its dknown bit set] of the same type. + * are rejected above). This is done by checking to see if a + * scroll with the same description has been encountered. * * Normal requirements can be overridden if hero is Lucky. */ @@ -345,7 +340,7 @@ dowrite(struct obj *pen) /* if known, then either by-name or by-descr works */ if (!objects[new_obj->otyp].oc_name_known /* else if named, then only by-descr works */ - && !(by_descr && label_known(new_obj->otyp, gi.invent)) + && !(by_descr && objects[new_obj->otyp].oc_encountered) /* else fresh knowledge of the spell works */ && spell_knowledge != spe_Fresh /* and Luck might override after previous checks have failed */ diff --git a/src/zap.c b/src/zap.c index 16e18db34..8849fb75a 100644 --- a/src/zap.c +++ b/src/zap.c @@ -132,14 +132,14 @@ learnwand(struct obj *obj) /* if type already discovered, treat this item has having been seen even if hero is currently blinded (skips redundant makeknown) */ if (objects[obj->otyp].oc_name_known) { - obj->dknown = 1; /* will usually be set already */ + observe_object(obj); /* will usually be dknown already */ /* otherwise discover it if item itself has been or can be seen */ } else { /* in case it was picked up while blind and then zapped without examining inventory after regaining sight (bypassing xname) */ if (!Blind) - obj->dknown = 1; + observe_object(obj); /* make the discovery iff we know what we're manipulating */ if (obj->dknown) makeknown(obj->otyp); @@ -610,7 +610,7 @@ staticfn void probe_objchain(struct obj *otmp) { for (; otmp; otmp = otmp->nobj) { - otmp->dknown = 1; /* treat as "seen" */ + observe_object(otmp); /* treat as "seen" */ if (Is_container(otmp) || otmp->otyp == STATUE) { otmp->lknown = 1; if (!SchroedingersBox(otmp)) @@ -2220,7 +2220,7 @@ bhito(struct obj *obj, struct obj *otmp) case WAN_PROBING: res = !obj->dknown; /* target object has now been "seen (up close)" */ - obj->dknown = 1; + observe_object(obj); if (Is_container(obj) || obj->otyp == STATUE) { obj->cknown = obj->lknown = 1; if (Is_box(obj) && !obj->tknown) { @@ -2249,7 +2249,7 @@ bhito(struct obj *obj, struct obj *otmp) /* view contents (not recursively) */ for (o = obj->cobj; o; o = o->nobj) - o->dknown = 1; /* "seen", even if blind */ + observe_object(o); /* "seen", even if blind */ (void) display_cinventory(obj); } res = 1; From 6eb2eb7df7c84b5faaa1e3598ce4f4b2ce48d300 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Tue, 25 Nov 2025 23:01:46 +0000 Subject: [PATCH 120/442] Accurate dknown when writing scrolls while blind If you write the scroll by description, you obviously know what the label is because you specified the label (even if you didn't know what the scroll was). When writing an unidentified scroll by type, though, and getting lucky, you don't necessarily know the label of the resulting scroll. --- src/write.c | 36 +++++------------------------------- 1 file changed, 5 insertions(+), 31 deletions(-) diff --git a/src/write.c b/src/write.c index 312072a58..538406f34 100644 --- a/src/write.c +++ b/src/write.c @@ -4,7 +4,6 @@ #include "hack.h" staticfn int cost(struct obj *) NONNULLARG1; -staticfn boolean label_known(int, struct obj *) NO_NNARGS; staticfn int write_ok(struct obj *) NO_NNARGS; staticfn char *new_book_description(int, char *) NONNULL NONNULLPTRS; @@ -57,34 +56,6 @@ cost(struct obj *otmp) return 1000; } -/* decide whether the hero knowns a particular scroll's label; - unfortunately, we can't track things that haven't been added to - the discoveries list and aren't present in current inventory, - so some scrolls with ought to yield True will end up False */ -staticfn boolean -label_known(int scrolltype, struct obj *objlist) -{ - struct obj *otmp; - - /* only scrolls */ - if (objects[scrolltype].oc_class != SCROLL_CLASS) - return FALSE; - /* type known implies full discovery; otherwise, - user-assigned name implies partial discovery */ - if (objects[scrolltype].oc_name_known || objects[scrolltype].oc_uname) - return TRUE; - /* check inventory, including carried containers with known contents */ - for (otmp = objlist; otmp; otmp = otmp->nobj) { - if (otmp->otyp == scrolltype && otmp->dknown) - return TRUE; - if (Has_contents(otmp) && otmp->cknown - && label_known(scrolltype, otmp->cobj)) - return TRUE; - } - /* not found */ - return FALSE; -} - /* getobj callback for object to write on */ staticfn int write_ok(struct obj *obj) @@ -399,8 +370,11 @@ dowrite(struct obj *pen) /* unlike alchemy, for example, a successful result yields the specifically chosen item so hero recognizes it even if blind; the exception is for being lucky writing an undiscovered scroll, - where the label associated with the type-name isn't known yet */ - new_obj->dknown = label_known(new_obj->otyp, gi.invent) ? 1 : 0; + where the label associated with the type-name isn't known yet; + but if writing by description, the description is always known */ + new_obj->dknown = FALSE; + if (objects[new_obj->otyp].oc_name_known || by_descr) + observe_object(new_obj); new_obj = hold_another_object(new_obj, "Oops! %s out of your grasp!", The(aobjnam(new_obj, "slip")), From 4d55e1de79073e8bff6cf6d345f3cecab8932eea Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Wed, 26 Nov 2025 05:45:45 +0000 Subject: [PATCH 121/442] Make saving grace also work against repeated damage sources For example, being hit by the bounce of a wand of fire means that the main character could take damage twice in a turn, which would kill even through saving grace; and scrolls and potions could burn up after that and finish off the last HP, even if the wand only hit once. This commit changes it to track all damage done during the turn, and prevent HP dropping below 1 from damage until the next player action or the next turn boundary, whichever comes first. --- include/decl.h | 6 ++++++ src/allmain.c | 5 +++++ src/decl.c | 4 ++++ src/hack.c | 25 +++++++++++++++++++++++-- 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/include/decl.h b/include/decl.h index 8487ae6fc..1a048b007 100644 --- a/include/decl.h +++ b/include/decl.h @@ -838,6 +838,9 @@ struct instance_globals_r { struct instance_globals_s { + /* allmain.c */ + boolean saving_grace_turn; /* saving grace was triggered this turn */ + /* artifact.c */ int spec_dbon_applies; /* coordinate effects from spec_dbon() with messages in artifact_hit() */ @@ -969,6 +972,9 @@ struct instance_globals_t { struct instance_globals_u { + /* allmain.c */ + int uhp_at_start_of_monster_turn; + /* botl.c */ boolean update_all; diff --git a/src/allmain.c b/src/allmain.c index cde778bfb..b4d7158f4 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -200,6 +200,7 @@ moveloop_core(void) encumber_msg(); svc.context.mon_moving = TRUE; + gu.uhp_at_start_of_monster_turn = u.uhp; do { monscanmove = movemon(); if (u.umovement >= NORMAL_SPEED) @@ -272,6 +273,8 @@ moveloop_core(void) if (u.ublesscnt) u.ublesscnt--; + gs.saving_grace_turn = FALSE; + /* One possible result of prayer is healing. Whether or * not you get healed depends on your current hit points. * If you are allowed to regenerate during the prayer, @@ -423,6 +426,8 @@ moveloop_core(void) else if (!u.umoved) (void) pooleffects(FALSE); + gs.saving_grace_turn = FALSE; + /* vision while buried or underwater is updated here */ if (Underwater) under_water(0); diff --git a/src/decl.c b/src/decl.c index 04f55eb99..e6fd152f0 100644 --- a/src/decl.c +++ b/src/decl.c @@ -674,6 +674,8 @@ static const struct instance_globals_r g_init_r = { }; static const struct instance_globals_s g_init_s = { + /* allmain.c */ + FALSE, /* saving_grace_turn */ /* artifact.c */ 0, /* spec_dbon_applies */ /* decl.c */ @@ -764,6 +766,8 @@ static const struct instance_globals_t g_init_t = { }; static const struct instance_globals_u g_init_u = { + /* allmain.c */ + 0, /* uhp_at_start_of_monster_turn */ /* botl.c */ FALSE, /* update_all */ /* decl.c */ diff --git a/src/hack.c b/src/hack.c index 9c0a56712..fb356ab78 100644 --- a/src/hack.c +++ b/src/hack.c @@ -4126,7 +4126,25 @@ saving_grace(int dmg) return 0; } - if (!u.usaving_grace && dmg >= u.uhp && (u.uhp * 100 / u.uhpmax) > 90) { + if (!svc.context.mon_moving) { + /* saving grace doesn't protect you from your own actions */ + return dmg; + } + + if (dmg < u.uhp || u.uhp <= 0) { + /* no need for saving grace */ + return dmg; + } + + if (gs.saving_grace_turn) { + /* saving grace already triggered and prevents HP reducing below 1 + this turn (specifically: until the next player action or turn + boundary), don't print further messages or livelog entries */ + return u.uhp - 1; + } + + if (!u.usaving_grace && + (gu.uhp_at_start_of_monster_turn * 100 / u.uhpmax) >= 90) { /* saving_grace doesn't have it's own livelog classification; we might invent one, or perhaps use LL_LIFESAVE, but surviving certain death (or preserving worn amulet of life saving) via @@ -4135,11 +4153,14 @@ saving_grace(int dmg) from #chronicle during play but show it to livelog observers */ livelog_printf(LL_CONDUCT | LL_SPOILER, "%s (%d damage, %d/%d HP)", "survived one-shot death via saving-grace", - dmg, u.uhp, u.uhpmax); + /* include damage that happened earlier this turn */ + gu.uhp_at_start_of_monster_turn - u.uhp + dmg, + gu.uhp_at_start_of_monster_turn, u.uhpmax); /* note: this could reduce dmg to 0 if u.uhpmax==1 */ dmg = u.uhp - 1; u.usaving_grace = 1; /* used up */ + gs.saving_grace_turn = TRUE; end_running(TRUE); if (u.usleep) unmul("Suddenly you wake up!"); From b08fbef739d242a2973472e60912f075be6cb26e Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Thu, 27 Nov 2025 23:14:17 +0000 Subject: [PATCH 122/442] Followup to erinyes changes / offering while impaired The important part of the "don't offer while impaired" change was to prevent offering while confused. However, it was also extended to other status conditions: stunning seems fine, but hallucination was problematic (both because it makes a large number of messages inaccessible, and because hallucination is more of a long-term status effect than the other two and players may sometimes choose to play with it for a large portion of the game). So make the change trigger only on stunning and confusion, not hallucination. This also updates the changelog for the change, because while connected to the erinys changes, it's technically separate and is relevant even in games where erinyes are never summoned. --- doc/fixes3-7-0.txt | 1 + src/pray.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 4dd9c189a..fcb28a135 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1545,6 +1545,7 @@ some types of shopkeeper now start with a scroll of charging objects are now accurately tracked as discovered even if not type-named nor formally identified (fixing some bugs in scroll writing, and making the discoveries list more accurate) +you cannot sacrifice objects/corpses while stunned or confused Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/pray.c b/src/pray.c index b515374d7..075711400 100644 --- a/src/pray.c +++ b/src/pray.c @@ -1861,7 +1861,7 @@ dosacrifice(void) You("are not %s an altar.", (Levitation || Flying) ? "over" : "on"); return ECMD_OK; - } else if (Confusion || Stunned || Hallucination) { + } else if (Confusion || Stunned) { You("are too impaired to perform the rite."); return ECMD_OK; } From 94e5f7b8618e944964ad2216c8e33afbfc1a4a6a Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 28 Nov 2025 15:13:01 -0800 Subject: [PATCH 123/442] part of issue #1463: // vs lootabc Issue reported by ashleyharvey in a comment to issue #1463: with the 'lootabc' option set, using the '/' command wouldn't accept a second slash to look at things on the map (nor a question mark to type in text to look up). Not a bug since '//' is not documented as the way to look at the map and '/a' works for lootabc, but '//' is useful so add support for it. The '/' menu already uses 'y' and 'n' as unshown synonyms for looking at the map and for looking up words. Those now only work for the '!lootabc' setting since lootabc can't assign multiple group accelerators to the relevant choices. Many of the other !lootabc choice letters now work as unshown synonyms for lootabc choices, but not all. Feeding 'i' and 'e' to the menu as group accelerators would interfere with using them as ordinary abc choices (at least for tty). --- doc/fixes3-7-0.txt | 3 +++ src/pager.c | 64 ++++++++++++++++++++++++++++++++++------------ 2 files changed, 51 insertions(+), 16 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index fcb28a135..36add38e3 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1546,6 +1546,9 @@ objects are now accurately tracked as discovered even if not type-named nor formally identified (fixing some bugs in scroll writing, and making the discoveries list more accurate) you cannot sacrifice objects/corpses while stunned or confused +'whatis' actions // and /? didn't work when the lootabc option was on, they + required /a and /c instead; add '/' and '?' as group accelerators so + that they work; /y and /n for them now only work when lootabc is off Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/pager.c b/src/pager.c index 9f9ba087b..c5a122401 100644 --- a/src/pager.c +++ b/src/pager.c @@ -1706,61 +1706,93 @@ do_look(int mode, coord *click_cc) any = cg.zeroany; win = create_nhwindow(NHW_MENU); start_menu(win, MENU_BEHAVE_STANDARD); + + /* + * Originally this was just a y|n question about whether to + * use the cursor or to type a word. When other choices were + * added, it was changed to be a menu. Using 'y' and 'n' as + * unshown accelerators keeps backwards compatibility with + * the old y|n behavior. + * + * Initially the menu included a third choice and always used + * 'a', 'b', and 'c'. Then it was changed to be controlled by + * the 'lootabc' option instead, defaulting to '/', 'i', '?' + * when that's false. Eventually additional entries have been + * introduced. + * + * When lootabc is set, abandon the 'y'|'n' compatibility in + * favor of newer '/' and '?' compatobility instead. + */ + any.a_char = '/'; - /* 'y' and 'n' to keep backwards compatibility with previous - versions: "Specify unknown object by cursor?" */ add_menu(win, &nul_glyphinfo, &any, - flags.lootabc ? 0 : any.a_char, 'y', ATR_NONE, + flags.lootabc ? 0 : any.a_char, + flags.lootabc ? '/' : 'y', ATR_NONE, clr, "something on the map", MENU_ITEMFLAGS_NONE); any.a_char = 'i'; add_menu(win, &nul_glyphinfo, &any, + /* [don't use 'i' as lootabc group accelerator because + it will make the regular 'i' choice inaccessible] */ flags.lootabc ? 0 : any.a_char, 0, ATR_NONE, clr, "something you're carrying", MENU_ITEMFLAGS_NONE); any.a_char = '?'; add_menu(win, &nul_glyphinfo, &any, - flags.lootabc ? 0 : any.a_char, 'n', ATR_NONE, + flags.lootabc ? 0 : any.a_char, + flags.lootabc ? '?' : 'n', ATR_NONE, clr, "something else (by symbol or name)", MENU_ITEMFLAGS_NONE); if (!u.uswallow && !Hallucination) { any = cg.zeroany; add_menu_str(win, ""); - /* these options work sensibly for the swallowed case, - but there's no reason for the player to use them then; + /* these options work sensibly for swallowed case, but + there's no reason for player to use them then because + the swallowed display hides all applicable targets; objects work fine when hallucinating, but screen symbol/monster class letter doesn't match up with bogus monster type, so suppress when hallucinating */ any.a_char = 'm'; add_menu(win, &nul_glyphinfo, &any, - flags.lootabc ? 0 : any.a_char, 0, ATR_NONE, + flags.lootabc ? 0 : any.a_char, + flags.lootabc ? any.a_char : 0, ATR_NONE, clr, "nearby monsters", MENU_ITEMFLAGS_NONE); any.a_char = 'M'; add_menu(win, &nul_glyphinfo, &any, - flags.lootabc ? 0 : any.a_char, 0, ATR_NONE, clr, - "all monsters shown on map", MENU_ITEMFLAGS_NONE); + flags.lootabc ? 0 : any.a_char, + flags.lootabc ? any.a_char : 0, ATR_NONE, + clr, "all monsters shown on map", + MENU_ITEMFLAGS_NONE); any.a_char = 'o'; add_menu(win, &nul_glyphinfo, &any, - flags.lootabc ? 0 : any.a_char, 0, ATR_NONE, + flags.lootabc ? 0 : any.a_char, + flags.lootabc ? any.a_char : 0, ATR_NONE, clr, "nearby objects", MENU_ITEMFLAGS_NONE); any.a_char = 'O'; add_menu(win, &nul_glyphinfo, &any, - flags.lootabc ? 0 : any.a_char, 0, ATR_NONE, clr, - "all objects shown on map", MENU_ITEMFLAGS_NONE); + flags.lootabc ? 0 : any.a_char, + flags.lootabc ? any.a_char : 0, ATR_NONE, + clr, "all objects shown on map", + MENU_ITEMFLAGS_NONE); any.a_char = 't'; add_menu(win, &nul_glyphinfo, &any, - flags.lootabc ? 0 : any.a_char, '^', ATR_NONE, + flags.lootabc ? 0 : any.a_char, + flags.lootabc ? any.a_char : '^', ATR_NONE, clr, "nearby traps", MENU_ITEMFLAGS_NONE); any.a_char = 'T'; add_menu(win, &nul_glyphinfo, &any, - flags.lootabc ? 0 : any.a_char, '\"', ATR_NONE, + flags.lootabc ? 0 : any.a_char, + flags.lootabc ? any.a_char : '\"', ATR_NONE, clr, "all seen or remembered traps", MENU_ITEMFLAGS_NONE); any.a_char = 'e'; add_menu(win, &nul_glyphinfo, &any, - flags.lootabc ? 0 : any.a_char, '`', ATR_NONE, + flags.lootabc ? 0 : any.a_char, + /* [don't use 'e' as lootabc group accelerator] */ + flags.lootabc ? 0 : '`', ATR_NONE, clr, "nearby engravings", MENU_ITEMFLAGS_NONE); any.a_char = 'E'; add_menu(win, &nul_glyphinfo, &any, - flags.lootabc ? 0 : any.a_char, '|', ATR_NONE, + flags.lootabc ? 0 : any.a_char, + flags.lootabc ? any.a_char : '|', ATR_NONE, clr, "all seen or remembered engravings", MENU_ITEMFLAGS_NONE); } From d41cea828601a8802525b6a7ed2aedf1b26c4d70 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Sun, 30 Nov 2025 02:59:23 +0000 Subject: [PATCH 124/442] Make the inventory-initialisation arrays constants This is helpful for if we ever allow playing multiple games in the same session: because the arrays are not modified, they can now be used more than once. --- src/u_init.c | 391 +++++++++++++++++++++++++++------------------------ 1 file changed, 204 insertions(+), 187 deletions(-) diff --git a/src/u_init.c b/src/u_init.c index 72bf3043c..f84ab1ab2 100644 --- a/src/u_init.c +++ b/src/u_init.c @@ -9,17 +9,19 @@ struct trobj { short trotyp; schar trspe; char trclass; - Bitfield(trquan, 6); - Bitfield(trbless, 2); + char trquan_min; + char trquan_max; + char trbless; }; +staticfn long trquan(const struct trobj *); staticfn struct obj *ini_inv_mkobj_filter(int, boolean); -staticfn short ini_inv_obj_substitution(struct trobj *, - struct obj *) NONNULLPTRS; -staticfn void ini_inv_adjust_obj(struct trobj *, - struct obj *) NONNULLPTRS; +staticfn short ini_inv_obj_substitution(const struct trobj *, + struct obj *) NONNULLPTRS; +staticfn boolean ini_inv_adjust_obj(const struct trobj *, + struct obj *) NONNULLPTRS; staticfn void ini_inv_use_obj(struct obj *) NONNULLARG1; -staticfn void ini_inv(struct trobj *) NONNULLARG1; +staticfn void ini_inv(const struct trobj *) NONNULLARG1; staticfn void knows_object(int, boolean); staticfn void knows_class(char); staticfn void u_init_role(void); @@ -36,177 +38,188 @@ staticfn boolean restricted_spell_discipline(int); * Initial inventory for the various roles. */ -static struct trobj Archeologist[] = { +static const struct trobj Archeologist[] = { /* if adventure has a name... idea from tan@uvm-gen */ - { BULLWHIP, 2, WEAPON_CLASS, 1, UNDEF_BLESS }, - { LEATHER_JACKET, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, - { FEDORA, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, - { FOOD_RATION, 0, FOOD_CLASS, 3, 0 }, - { PICK_AXE, UNDEF_SPE, TOOL_CLASS, 1, UNDEF_BLESS }, - { TINNING_KIT, UNDEF_SPE, TOOL_CLASS, 1, UNDEF_BLESS }, - { TOUCHSTONE, 0, GEM_CLASS, 1, 0 }, - { SACK, 0, TOOL_CLASS, 1, 0 }, - { 0, 0, 0, 0, 0 } + { BULLWHIP, 2, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { LEATHER_JACKET, 0, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { FEDORA, 0, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { FOOD_RATION, 0, FOOD_CLASS, 3, 3, 0 }, + { PICK_AXE, UNDEF_SPE, TOOL_CLASS, 1, 1, UNDEF_BLESS }, + { TINNING_KIT, UNDEF_SPE, TOOL_CLASS, 1, 1, UNDEF_BLESS }, + { TOUCHSTONE, 0, GEM_CLASS, 1, 1, 0 }, + { SACK, 0, TOOL_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; -static struct trobj Barbarian[] = { -#define B_MAJOR 0 /* two-handed sword or battle-axe */ -#define B_MINOR 1 /* matched with axe or short sword */ - { TWO_HANDED_SWORD, 0, WEAPON_CLASS, 1, UNDEF_BLESS }, - { AXE, 0, WEAPON_CLASS, 1, UNDEF_BLESS }, - { RING_MAIL, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, - { FOOD_RATION, 0, FOOD_CLASS, 1, 0 }, - { 0, 0, 0, 0, 0 } +static const struct trobj Barbarian_0[] = { + { TWO_HANDED_SWORD, 0, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { AXE, 0, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { RING_MAIL, 0, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { FOOD_RATION, 0, FOOD_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; -static struct trobj Cave_man[] = { -#define C_AMMO 2 - { CLUB, 1, WEAPON_CLASS, 1, UNDEF_BLESS }, - { SLING, 2, WEAPON_CLASS, 1, UNDEF_BLESS }, - { FLINT, 0, GEM_CLASS, 15, UNDEF_BLESS }, /* quan is variable */ - { ROCK, 0, GEM_CLASS, 3, 0 }, /* yields 18..33 */ - { LEATHER_ARMOR, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, - { 0, 0, 0, 0, 0 } +static const struct trobj Barbarian_1[] = { + { BATTLE_AXE, 0, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { SHORT_SWORD, 0, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { RING_MAIL, 0, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { FOOD_RATION, 0, FOOD_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; -static struct trobj Healer[] = { - { SCALPEL, 0, WEAPON_CLASS, 1, UNDEF_BLESS }, - { LEATHER_GLOVES, 1, ARMOR_CLASS, 1, UNDEF_BLESS }, - { STETHOSCOPE, 0, TOOL_CLASS, 1, 0 }, - { POT_HEALING, 0, POTION_CLASS, 4, UNDEF_BLESS }, - { POT_EXTRA_HEALING, 0, POTION_CLASS, 4, UNDEF_BLESS }, - { WAN_SLEEP, UNDEF_SPE, WAND_CLASS, 1, UNDEF_BLESS }, +static const struct trobj Cave_man[] = { + { CLUB, 1, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { SLING, 2, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { FLINT, 0, GEM_CLASS, 10, 20, UNDEF_BLESS }, + { ROCK, 0, GEM_CLASS, 3, 3, 0 }, /* yields 18..33 */ + { LEATHER_ARMOR, 0, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { 0, 0, 0, 0, 0, 0 } +}; +static const struct trobj Healer[] = { + { SCALPEL, 0, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { LEATHER_GLOVES, 1, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { STETHOSCOPE, 0, TOOL_CLASS, 1, 1, 0 }, + { POT_HEALING, 0, POTION_CLASS, 4, 4, UNDEF_BLESS }, + { POT_EXTRA_HEALING, 0, POTION_CLASS, 4, 4, UNDEF_BLESS }, + { WAN_SLEEP, UNDEF_SPE, WAND_CLASS, 1, 1, UNDEF_BLESS }, /* always blessed, so it's guaranteed readable */ - { SPE_HEALING, 0, SPBOOK_CLASS, 1, 1 }, - { SPE_EXTRA_HEALING, 0, SPBOOK_CLASS, 1, 1 }, - { SPE_STONE_TO_FLESH, 0, SPBOOK_CLASS, 1, 1 }, - { APPLE, 0, FOOD_CLASS, 5, 0 }, - { 0, 0, 0, 0, 0 } + { SPE_HEALING, 0, SPBOOK_CLASS, 1, 1, 1 }, + { SPE_EXTRA_HEALING, 0, SPBOOK_CLASS, 1, 1, 1 }, + { SPE_STONE_TO_FLESH, 0, SPBOOK_CLASS, 1, 1, 1 }, + { APPLE, 0, FOOD_CLASS, 5, 5, 0 }, + { 0, 0, 0, 0, 0, 0 } }; -static struct trobj Knight[] = { - { LONG_SWORD, 1, WEAPON_CLASS, 1, UNDEF_BLESS }, - { LANCE, 1, WEAPON_CLASS, 1, UNDEF_BLESS }, - { RING_MAIL, 1, ARMOR_CLASS, 1, UNDEF_BLESS }, - { HELMET, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, - { SMALL_SHIELD, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, - { LEATHER_GLOVES, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, - { APPLE, 0, FOOD_CLASS, 10, 0 }, - { CARROT, 0, FOOD_CLASS, 10, 0 }, - { 0, 0, 0, 0, 0 } +static const struct trobj Knight[] = { + { LONG_SWORD, 1, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { LANCE, 1, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { RING_MAIL, 1, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { HELMET, 0, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { SMALL_SHIELD, 0, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { LEATHER_GLOVES, 0, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { APPLE, 0, FOOD_CLASS, 10, 10, 0 }, + { CARROT, 0, FOOD_CLASS, 10, 10, 0 }, + { 0, 0, 0, 0, 0, 0 } }; -static struct trobj Monk[] = { -#define M_BOOK 2 - { LEATHER_GLOVES, 2, ARMOR_CLASS, 1, UNDEF_BLESS }, - { ROBE, 1, ARMOR_CLASS, 1, UNDEF_BLESS }, - { UNDEF_TYP, UNDEF_SPE, SPBOOK_CLASS, 1, 1 }, - { UNDEF_TYP, UNDEF_SPE, SCROLL_CLASS, 1, UNDEF_BLESS }, - { POT_HEALING, 0, POTION_CLASS, 3, UNDEF_BLESS }, - { FOOD_RATION, 0, FOOD_CLASS, 3, 0 }, - { APPLE, 0, FOOD_CLASS, 5, UNDEF_BLESS }, - { ORANGE, 0, FOOD_CLASS, 5, UNDEF_BLESS }, +static const struct trobj Monk[] = { + { LEATHER_GLOVES, 2, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { ROBE, 1, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { UNDEF_TYP, UNDEF_SPE, SCROLL_CLASS, 1, 1, UNDEF_BLESS }, + { POT_HEALING, 0, POTION_CLASS, 3, 3, UNDEF_BLESS }, + { FOOD_RATION, 0, FOOD_CLASS, 3, 3, 0 }, + { APPLE, 0, FOOD_CLASS, 5, 5, UNDEF_BLESS }, + { ORANGE, 0, FOOD_CLASS, 5, 5, UNDEF_BLESS }, /* Yes, we know fortune cookies aren't really from China. They were - * invented by George Jung in Los Angeles, California, USA in 1916. - */ - { FORTUNE_COOKIE, 0, FOOD_CLASS, 3, UNDEF_BLESS }, - { 0, 0, 0, 0, 0 } + invented by George Jung in Los Angeles, California, USA in 1916. */ + { FORTUNE_COOKIE, 0, FOOD_CLASS, 3, 3, UNDEF_BLESS }, + { 0, 0, 0, 0, 0, 0 } }; -static struct trobj Priest[] = { - { MACE, 1, WEAPON_CLASS, 1, 1 }, - { ROBE, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, - { SMALL_SHIELD, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, - { POT_WATER, 0, POTION_CLASS, 4, 1 }, /* holy water */ - { CLOVE_OF_GARLIC, 0, FOOD_CLASS, 1, 0 }, - { SPRIG_OF_WOLFSBANE, 0, FOOD_CLASS, 1, 0 }, - { UNDEF_TYP, UNDEF_SPE, SPBOOK_CLASS, 2, UNDEF_BLESS }, - { 0, 0, 0, 0, 0 } +static const struct trobj Priest[] = { + { MACE, 1, WEAPON_CLASS, 1, 1, 1 }, + { ROBE, 0, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { SMALL_SHIELD, 0, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { POT_WATER, 0, POTION_CLASS, 4, 4, 1 }, /* holy water */ + { CLOVE_OF_GARLIC, 0, FOOD_CLASS, 1, 1, 0 }, + { SPRIG_OF_WOLFSBANE, 0, FOOD_CLASS, 1, 1, 0 }, + { UNDEF_TYP, UNDEF_SPE, SPBOOK_CLASS, 2, 2, UNDEF_BLESS }, + { 0, 0, 0, 0, 0, 0 } }; -static struct trobj Ranger[] = { -#define RAN_BOW 1 -#define RAN_TWO_ARROWS 2 -#define RAN_ZERO_ARROWS 3 - { DAGGER, 1, WEAPON_CLASS, 1, UNDEF_BLESS }, - { BOW, 1, WEAPON_CLASS, 1, UNDEF_BLESS }, - { ARROW, 2, WEAPON_CLASS, 50, UNDEF_BLESS }, - { ARROW, 0, WEAPON_CLASS, 30, UNDEF_BLESS }, - { CLOAK_OF_DISPLACEMENT, 2, ARMOR_CLASS, 1, UNDEF_BLESS }, - { CRAM_RATION, 0, FOOD_CLASS, 4, 0 }, - { 0, 0, 0, 0, 0 } +static const struct trobj Ranger[] = { + { DAGGER, 1, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { BOW, 1, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { ARROW, 2, WEAPON_CLASS, 50, 59, UNDEF_BLESS }, + { ARROW, 0, WEAPON_CLASS, 30, 39, UNDEF_BLESS }, + { CLOAK_OF_DISPLACEMENT, 2, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { CRAM_RATION, 0, FOOD_CLASS, 4, 4, 0 }, + { 0, 0, 0, 0, 0, 0 } }; -static struct trobj Rogue[] = { -#define R_DAGGERS 1 - { SHORT_SWORD, 0, WEAPON_CLASS, 1, UNDEF_BLESS }, - { DAGGER, 0, WEAPON_CLASS, 10, 0 }, /* quan is variable */ - { LEATHER_ARMOR, 1, ARMOR_CLASS, 1, UNDEF_BLESS }, - { POT_SICKNESS, 0, POTION_CLASS, 1, 0 }, - { LOCK_PICK, 0, TOOL_CLASS, 1, 0 }, - { SACK, 0, TOOL_CLASS, 1, 0 }, - { 0, 0, 0, 0, 0 } +static const struct trobj Rogue[] = { + { SHORT_SWORD, 0, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { DAGGER, 0, WEAPON_CLASS, 6, 15, 0 }, + { LEATHER_ARMOR, 1, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { POT_SICKNESS, 0, POTION_CLASS, 1, 1, 0 }, + { LOCK_PICK, 0, TOOL_CLASS, 1, 1, 0 }, + { SACK, 0, TOOL_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; -static struct trobj Samurai[] = { -#define S_ARROWS 3 - { KATANA, 0, WEAPON_CLASS, 1, UNDEF_BLESS }, - { SHORT_SWORD, 0, WEAPON_CLASS, 1, UNDEF_BLESS }, /* wakizashi */ - { YUMI, 0, WEAPON_CLASS, 1, UNDEF_BLESS }, - { YA, 0, WEAPON_CLASS, 25, UNDEF_BLESS }, /* variable quan */ - { SPLINT_MAIL, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, - { 0, 0, 0, 0, 0 } +static const struct trobj Samurai[] = { + { KATANA, 0, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { SHORT_SWORD, 0, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, /* wakizashi */ + { YUMI, 0, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { YA, 0, WEAPON_CLASS, 26, 45, UNDEF_BLESS }, + { SPLINT_MAIL, 0, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { 0, 0, 0, 0, 0, 0 } }; -static struct trobj Tourist[] = { -#define T_DARTS 0 - { DART, 2, WEAPON_CLASS, 25, UNDEF_BLESS }, /* quan is variable */ - { UNDEF_TYP, UNDEF_SPE, FOOD_CLASS, 10, 0 }, - { POT_EXTRA_HEALING, 0, POTION_CLASS, 2, UNDEF_BLESS }, - { SCR_MAGIC_MAPPING, 0, SCROLL_CLASS, 4, UNDEF_BLESS }, - { HAWAIIAN_SHIRT, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, - { EXPENSIVE_CAMERA, UNDEF_SPE, TOOL_CLASS, 1, 0 }, - { CREDIT_CARD, 0, TOOL_CLASS, 1, 0 }, - { 0, 0, 0, 0, 0 } +static const struct trobj Tourist[] = { + { DART, 2, WEAPON_CLASS, 21, 40, UNDEF_BLESS }, + { UNDEF_TYP, UNDEF_SPE, FOOD_CLASS, 10, 10, 0 }, + { POT_EXTRA_HEALING, 0, POTION_CLASS, 2, 2, UNDEF_BLESS }, + { SCR_MAGIC_MAPPING, 0, SCROLL_CLASS, 4, 4, UNDEF_BLESS }, + { HAWAIIAN_SHIRT, 0, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { EXPENSIVE_CAMERA, UNDEF_SPE, TOOL_CLASS, 1, 1, 0 }, + { CREDIT_CARD, 0, TOOL_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; -static struct trobj Valkyrie[] = { - { SPEAR, 1, WEAPON_CLASS, 1, UNDEF_BLESS }, - { DAGGER, 0, WEAPON_CLASS, 1, UNDEF_BLESS }, - { SMALL_SHIELD, 3, ARMOR_CLASS, 1, UNDEF_BLESS }, - { FOOD_RATION, 0, FOOD_CLASS, 1, 0 }, - { 0, 0, 0, 0, 0 } +static const struct trobj Valkyrie[] = { + { SPEAR, 1, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { DAGGER, 0, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { SMALL_SHIELD, 3, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { FOOD_RATION, 0, FOOD_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; -static struct trobj Wizard[] = { - { QUARTERSTAFF, 1, WEAPON_CLASS, 1, 1 }, - { CLOAK_OF_MAGIC_RESISTANCE, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, - { UNDEF_TYP, UNDEF_SPE, WAND_CLASS, 1, UNDEF_BLESS }, - { UNDEF_TYP, UNDEF_SPE, RING_CLASS, 2, UNDEF_BLESS }, - { UNDEF_TYP, UNDEF_SPE, POTION_CLASS, 3, UNDEF_BLESS }, - { UNDEF_TYP, UNDEF_SPE, SCROLL_CLASS, 3, UNDEF_BLESS }, - { SPE_FORCE_BOLT, 0, SPBOOK_CLASS, 1, 1 }, - { UNDEF_TYP, UNDEF_SPE, SPBOOK_CLASS, 1, UNDEF_BLESS }, - { MAGIC_MARKER, 19, TOOL_CLASS, 1, 0 }, /* actually spe = 18 + d4 */ - { 0, 0, 0, 0, 0 } +static const struct trobj Wizard[] = { + { QUARTERSTAFF, 1, WEAPON_CLASS, 1, 1, 1 }, + { CLOAK_OF_MAGIC_RESISTANCE, 0, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { UNDEF_TYP, UNDEF_SPE, WAND_CLASS, 1, 1, UNDEF_BLESS }, + { UNDEF_TYP, UNDEF_SPE, RING_CLASS, 2, 2, UNDEF_BLESS }, + { UNDEF_TYP, UNDEF_SPE, POTION_CLASS, 3, 3, UNDEF_BLESS }, + { UNDEF_TYP, UNDEF_SPE, SCROLL_CLASS, 3, 3, UNDEF_BLESS }, + { SPE_FORCE_BOLT, 0, SPBOOK_CLASS, 1, 1, 1 }, + { UNDEF_TYP, UNDEF_SPE, SPBOOK_CLASS, 1, 1, UNDEF_BLESS }, + { MAGIC_MARKER, 19, TOOL_CLASS, 1, 1, 0 }, /* actually spe = 18 + d4 */ + { 0, 0, 0, 0, 0, 0 } }; /* * Optional extra inventory items. */ -static struct trobj Tinopener[] = { { TIN_OPENER, 0, TOOL_CLASS, 1, 0 }, - { 0, 0, 0, 0, 0 } }; -static struct trobj Magicmarker[] = { { MAGIC_MARKER, 19, TOOL_CLASS, 1, 0 }, - { 0, 0, 0, 0, 0 } }; -static struct trobj Lamp[] = { { OIL_LAMP, 1, TOOL_CLASS, 1, 0 }, - { 0, 0, 0, 0, 0 } }; -static struct trobj Blindfold[] = { { BLINDFOLD, 0, TOOL_CLASS, 1, 0 }, - { 0, 0, 0, 0, 0 } }; -static struct trobj Instrument[] = { { WOODEN_FLUTE, 0, TOOL_CLASS, 1, 0 }, - { 0, 0, 0, 0, 0 } }; -static struct trobj Xtra_food[] = { { UNDEF_TYP, UNDEF_SPE, FOOD_CLASS, 2, 0}, - { 0, 0, 0, 0, 0 } }; -static struct trobj Leash[] = { { LEASH, 0, TOOL_CLASS, 1, 0 }, - { 0, 0, 0, 0, 0 } }; -static struct trobj Towel[] = { { TOWEL, 0, TOOL_CLASS, 1, 0 }, - { 0, 0, 0, 0, 0 } }; -static struct trobj Wishing[] = { { WAN_WISHING, 3, WAND_CLASS, 1, 0 }, - { 0, 0, 0, 0, 0 } }; -static struct trobj Money[] = { { GOLD_PIECE, 0, COIN_CLASS, 1, 0 }, - { 0, 0, 0, 0, 0 } }; +static const struct trobj Healing_book[] = + { { SPE_HEALING, UNDEF_SPE, SPBOOK_CLASS, 1, 1, 1 }, + { 0, 0, 0, 0, 0, 0 } }; +static const struct trobj Protection_book[] = + { { SPE_PROTECTION, UNDEF_SPE, SPBOOK_CLASS, 1, 1, 1 }, + { 0, 0, 0, 0, 0, 0 } }; +static const struct trobj Confuse_monster_book[] = + { { SPE_CONFUSE_MONSTER, UNDEF_SPE, SPBOOK_CLASS, 1, 1, 1 }, + { 0, 0, 0, 0, 0, 0 } }; +static const struct trobj Tinopener[] = + { { TIN_OPENER, 0, TOOL_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; +static const struct trobj Magicmarker[] = + { { MAGIC_MARKER, 19, TOOL_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; +static const struct trobj Lamp[] = + { { OIL_LAMP, 1, TOOL_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; +static const struct trobj Blindfold[] = + { { BLINDFOLD, 0, TOOL_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; +static const struct trobj Xtra_food[] = + { { UNDEF_TYP, UNDEF_SPE, FOOD_CLASS, 2, 2, 0}, + { 0, 0, 0, 0, 0, 0 } }; +static const struct trobj Leash[] = + { { LEASH, 0, TOOL_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; +static const struct trobj Towel[] = + { { TOWEL, 0, TOOL_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; +static const struct trobj Wishing[] = + { { WAN_WISHING, 3, WAND_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; +static const struct trobj Money[] = + { { GOLD_PIECE, 0, COIN_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; /* race-based substitutions for initial inventory; the weaker cloak for elven rangers is intentional--they shoot better */ -static struct inv_sub { +static const struct inv_sub { short race_pm, item_otyp, subs_otyp; } inv_subs[] = { { PM_ELF, DAGGER, ELVEN_DAGGER }, @@ -645,10 +658,10 @@ u_init_role(void) break; case PM_BARBARIAN: if (rn2(100) >= 50) { /* see above comment */ - Barbarian[B_MAJOR].trotyp = BATTLE_AXE; - Barbarian[B_MINOR].trotyp = SHORT_SWORD; + ini_inv(Barbarian_0); + } else { + ini_inv(Barbarian_1); } - ini_inv(Barbarian); if (!rn2(6)) ini_inv(Lamp); knows_class(WEAPON_CLASS); /* excluding polearms */ @@ -656,7 +669,6 @@ u_init_role(void) skill_init(Skill_B); break; case PM_CAVE_DWELLER: - Cave_man[C_AMMO].trquan = rn1(11, 10); /* 10..20 */ ini_inv(Cave_man); skill_init(Skill_C); break; @@ -677,12 +689,12 @@ u_init_role(void) skill_init(Skill_K); break; case PM_MONK: { - static short M_spell[] = { - SPE_HEALING, SPE_PROTECTION, SPE_CONFUSE_MONSTER + static const struct trobj *M_spell[] = { + Healing_book, Protection_book, Confuse_monster_book }; - Monk[M_BOOK].trotyp = M_spell[rn2(90) / 30]; /* [0..2] */ ini_inv(Monk); + ini_inv(M_spell[rn2(90) / 30]); /* [0..2] */ if (!rn2(4)) ini_inv(Magicmarker); else if (!rn2(10)) @@ -710,14 +722,11 @@ u_init_role(void) */ break; case PM_RANGER: - Ranger[RAN_TWO_ARROWS].trquan = rn1(10, 50); - Ranger[RAN_ZERO_ARROWS].trquan = rn1(10, 30); ini_inv(Ranger); knows_class(WEAPON_CLASS); /* bows, arrows, spears only */ skill_init(Skill_Ran); break; case PM_ROGUE: - Rogue[R_DAGGERS].trquan = rn1(10, 6); u.umoney0 = 0; ini_inv(Rogue); if (!rn2(5)) @@ -729,7 +738,6 @@ u_init_role(void) skill_init(Skill_R); break; case PM_SAMURAI: - Samurai[S_ARROWS].trquan = rn1(20, 26); ini_inv(Samurai); if (!rn2(5)) ini_inv(Blindfold); @@ -748,7 +756,6 @@ u_init_role(void) skill_init(Skill_S); break; case PM_TOURIST: - Tourist[T_DARTS].trquan = rn1(20, 21); u.umoney0 = rnd(1000); ini_inv(Tourist); if (!rn2(25)) @@ -797,9 +804,12 @@ u_init_race(void) * get only non-magic instruments. */ if (Role_if(PM_CLERIC) || Role_if(PM_WIZARD)) { - static int trotyp[] = { WOODEN_FLUTE, TOOLED_HORN, WOODEN_HARP, - BELL, BUGLE, LEATHER_DRUM }; - Instrument[0].trotyp = ROLL_FROM(trotyp); + static const int trotyp[] = + { WOODEN_FLUTE, TOOLED_HORN, WOODEN_HARP, + BELL, BUGLE, LEATHER_DRUM }; + const struct trobj Instrument[] = + { { ROLL_FROM(trotyp), 0, TOOL_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; ini_inv(Instrument); } @@ -1109,6 +1119,15 @@ restricted_spell_discipline(int otyp) return TRUE; } +/* randomizes the quantity given a trobj description */ +staticfn long +trquan(const struct trobj *trop) +{ + if (!trop->trquan_min) + return 1; + return trop->trquan_min + rn2(trop->trquan_max - trop->trquan_min + 1); +} + /* create random object of certain class, filtering out too powerful items */ staticfn struct obj * ini_inv_mkobj_filter(int oclass, boolean got_level1_spellbook) @@ -1175,7 +1194,7 @@ ini_inv_mkobj_filter(int oclass, boolean got_level1_spellbook) /* substitute object with something else based on race. only changes otyp, and returns it. */ staticfn short -ini_inv_obj_substitution(struct trobj *trop, struct obj *obj) +ini_inv_obj_substitution(const struct trobj *trop, struct obj *obj) { if (gu.urace.mnum != PM_HUMAN) { int i; @@ -1198,9 +1217,12 @@ ini_inv_obj_substitution(struct trobj *trop, struct obj *obj) return obj->otyp; } -staticfn void -ini_inv_adjust_obj(struct trobj *trop, struct obj *obj) +/* returns: TRUE to stop generating items from this trobj, + FALSE for normal behaviour */ +staticfn boolean +ini_inv_adjust_obj(const struct trobj *trop, struct obj *obj) { + boolean stop = FALSE; if (trop->trclass == COIN_CLASS) { /* no "blessed" or "identified" money */ obj->quan = u.umoney0; @@ -1217,8 +1239,8 @@ ini_inv_adjust_obj(struct trobj *trop, struct obj *obj) if (obj->opoisoned && u.ualign.type != A_CHAOTIC) obj->opoisoned = 0; if (obj->oclass == WEAPON_CLASS || obj->oclass == TOOL_CLASS) { - obj->quan = (long) trop->trquan; - trop->trquan = 1; + obj->quan = trquan(trop); + stop = TRUE; } else if (obj->oclass == GEM_CLASS && is_graystone(obj) && obj->otyp != FLINT) { obj->quan = 1L; @@ -1239,6 +1261,7 @@ ini_inv_adjust_obj(struct trobj *trop, struct obj *obj) } /* defined after setting otyp+quan + blessedness */ obj->owt = weight(obj); + return stop; } /* initial inventory: wear, wield, learn the spell/obj */ @@ -1290,15 +1313,17 @@ ini_inv_use_obj(struct obj *obj) } staticfn void -ini_inv(struct trobj *trop) +ini_inv(const struct trobj *trop) { struct obj *obj; int otyp; boolean got_sp1 = FALSE; /* got a level 1 spellbook? */ + long quan; if (u.uroleplay.pauper) /* pauper gets no items */ return; + quan = trquan(trop); while (trop->trclass) { otyp = (int) trop->trotyp; if (otyp != UNDEF_TYP) { @@ -1340,7 +1365,8 @@ ini_inv(struct trobj *trop) continue; } - ini_inv_adjust_obj(trop, obj); + if (ini_inv_adjust_obj(trop, obj)) + quan = 1; obj = addinv(obj); ini_inv_use_obj(obj); @@ -1349,24 +1375,15 @@ ini_inv(struct trobj *trop) if (obj->oclass == SPBOOK_CLASS && objects[obj->otyp].oc_level == 1) got_sp1 = TRUE; - if (--trop->trquan) + if (--quan) continue; /* make a similar object */ trop++; + quan = trquan(trop); } } #undef UNDEF_TYP #undef UNDEF_SPE #undef UNDEF_BLESS -#undef B_MAJOR -#undef B_MINOR -#undef C_AMMO -#undef M_BOOK -#undef RAN_BOW -#undef RAN_TWO_ARROWS -#undef RAN_ZERO_ARROWS -#undef R_DAGGERS -#undef S_ARROWS -#undef T_DARTS /*u_init.c*/ From e428046ffadca3f1048bf0df4e5bfa956a93d221 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Sun, 30 Nov 2025 04:52:13 +0000 Subject: [PATCH 125/442] Restructure u_init to allow for inventory rerolling This doesn't implement inventory rerolling, just adds the infrastructure: it's now possible to call u_init_inventory_attrs multiple times and the starting inventory/attributes replace those from the previous call rather than compounding. --- include/extern.h | 4 +- src/allmain.c | 7 +-- src/u_init.c | 128 ++++++++++++++++++++++++++++++----------------- 3 files changed, 88 insertions(+), 51 deletions(-) diff --git a/include/extern.h b/include/extern.h index 49b83f784..831e209db 100644 --- a/include/extern.h +++ b/include/extern.h @@ -3304,7 +3304,9 @@ extern void trap_sanity_check(void); /* ### u_init.c ### */ -extern void u_init(void); +extern void u_init_misc(void); +extern void u_init_inventory_attrs(void); +extern void u_init_skills_discoveries(void); /* ### uhitm.c ### */ diff --git a/src/allmain.c b/src/allmain.c index b4d7158f4..6ced3e372 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -786,7 +786,7 @@ newgame(void) * in hero's initial inventory */ init_artifacts(); /* before u_init() in case $WIZKIT specifies * any artifacts */ - u_init(); + u_init_misc(); l_nhcore_init(); /* create a Lua state that lasts until end of game */ reset_glyphmap(gm_newgame); @@ -801,14 +801,15 @@ newgame(void) mklev(); u_on_upstairs(); - if (wizard) - obj_delivery(FALSE); /* finish wizkit */ vision_reset(); /* set up internals for level (after mklev) */ check_special_room(FALSE); if (MON_AT(u.ux, u.uy)) mnexto(m_at(u.ux, u.uy), RLOC_NOMSG); (void) makedog(); + + u_init_inventory_attrs(); + u_init_skills_discoveries(); docrt(); if (flags.legacy) { diff --git a/src/u_init.c b/src/u_init.c index f84ab1ab2..e0c7cdca3 100644 --- a/src/u_init.c +++ b/src/u_init.c @@ -27,6 +27,7 @@ staticfn void knows_class(char); staticfn void u_init_role(void); staticfn void u_init_race(void); staticfn void pauper_reinit(void); +staticfn const struct def_skill *skills_for_role(void); staticfn void u_init_carry_attr_boost(void); staticfn boolean restricted_spell_discipline(int); @@ -625,7 +626,11 @@ knows_class(char sym) } } -/* role-specific initializations */ +/* role-specific initializations, mostly inventory + + other things may be initialised here, but the function might run more than + once, so any non-inventory initialisations should be nonrandom and + idempotent (i.e. doing them twice is OK) */ staticfn void u_init_role(void) { @@ -654,7 +659,6 @@ u_init_role(void) knows_object(TOUCHSTONE, FALSE); /* FALSE: don't override pauper here, * but TOUCHSTONE will be made known * in pauper_reinit() */ - skill_init(Skill_A); break; case PM_BARBARIAN: if (rn2(100) >= 50) { /* see above comment */ @@ -666,11 +670,9 @@ u_init_role(void) ini_inv(Lamp); knows_class(WEAPON_CLASS); /* excluding polearms */ knows_class(ARMOR_CLASS); - skill_init(Skill_B); break; case PM_CAVE_DWELLER: ini_inv(Cave_man); - skill_init(Skill_C); break; case PM_HEALER: u.umoney0 = rn1(1000, 1001); @@ -678,7 +680,6 @@ u_init_role(void) if (!rn2(25)) ini_inv(Lamp); knows_object(POT_FULL_HEALING, FALSE); - skill_init(Skill_H); break; case PM_KNIGHT: ini_inv(Knight); @@ -686,7 +687,6 @@ u_init_role(void) knows_class(ARMOR_CLASS); /* give knights chess-like mobility--idea from wooledge@..cwru.edu */ HJumping |= FROMOUTSIDE; - skill_init(Skill_K); break; case PM_MONK: { static const struct trobj *M_spell[] = { @@ -702,7 +702,6 @@ u_init_role(void) knows_class(ARMOR_CLASS); /* sufficiently martial-arts oriented item to ignore language issue */ knows_object(SHURIKEN, FALSE); - skill_init(Skill_Mon); break; } case PM_CLERIC: /* priest/priestess */ @@ -712,7 +711,6 @@ u_init_role(void) else if (!rn2(10)) ini_inv(Lamp); knows_object(POT_WATER, TRUE); /* override pauper */ - skill_init(Skill_P); /* KMH, conduct -- * Some may claim that this isn't agnostic, since they * are literally "priests" and they have holy water. @@ -724,7 +722,6 @@ u_init_role(void) case PM_RANGER: ini_inv(Ranger); knows_class(WEAPON_CLASS); /* bows, arrows, spears only */ - skill_init(Skill_Ran); break; case PM_ROGUE: u.umoney0 = 0; @@ -735,7 +732,6 @@ u_init_role(void) * but sack will be made known in * pauper_reinit() */ knows_class(WEAPON_CLASS); /* daggers only */ - skill_init(Skill_R); break; case PM_SAMURAI: ini_inv(Samurai); @@ -753,7 +749,6 @@ u_init_role(void) samarai an advantage of knowing several items in advance */ knows_object(i, FALSE); } - skill_init(Skill_S); break; case PM_TOURIST: u.umoney0 = rnd(1000); @@ -766,7 +761,6 @@ u_init_role(void) ini_inv(Towel); else if (!rn2(20)) ini_inv(Magicmarker); - skill_init(Skill_T); break; case PM_VALKYRIE: ini_inv(Valkyrie); @@ -774,21 +768,24 @@ u_init_role(void) ini_inv(Lamp); knows_class(WEAPON_CLASS); /* excludes polearms */ knows_class(ARMOR_CLASS); - skill_init(Skill_V); break; case PM_WIZARD: ini_inv(Wizard); if (!rn2(5)) ini_inv(Blindfold); - skill_init(Skill_W); break; default: /* impossible */ break; } + + gn.nocreate = STRANGE_OBJECT; + gn.nocreate2 = STRANGE_OBJECT; + gn.nocreate3 = STRANGE_OBJECT; + gn.nocreate4 = STRANGE_OBJECT; } -/* race-specific initializations */ +/* race-specific initializations, same restrictions as u_init_role */ staticfn void u_init_race(void) { @@ -940,8 +937,9 @@ u_init_carry_attr_boost(void) } } +/* initialise u, except inventory, attributes, skills and discoveries */ void -u_init(void) +u_init_misc(void) { int i; struct u_roleplay tmpuroleplay = u.uroleplay; /* set by rcfile options */ @@ -1027,44 +1025,19 @@ u_init(void) if (u.uroleplay.blind) HBlinded |= FROMOUTSIDE; /* set PermaBlind */ - u_init_role(); - u_init_race(); - if (u.uroleplay.pauper) - pauper_reinit(); - /* roughly based on distribution in human population */ u.uhandedness = rn2(10) ? RIGHT_HANDED : LEFT_HANDED; - if (discover) - ini_inv(Wishing); - - if (wizard) - read_wizkit(); - - if (u.umoney0) - ini_inv(Money); - u.umoney0 += hidden_gold(TRUE); /* in case sack has gold in it */ - - find_ac(); /* get initial ac value */ - init_attr(75); /* init attribute values */ - vary_init_attr(); /* minor variation to attrs */ - u_init_carry_attr_boost(); max_rank_sz(); /* set max str size for class ranks */ - /* If we have at least one spell, force starting Pw to be enough, - so hero can cast the level 1 spell they should have */ - if (num_spells() && (u.uenmax < SPELL_LEV_PW(1))) - u.uen = u.uenmax = u.uenpeak = u.ueninc[u.ulevel] = SPELL_LEV_PW(1); - return; } -/* skills aren't initialized, so we use the role-specific skill lists */ -staticfn boolean -restricted_spell_discipline(int otyp) +/* the appropriate set of skills for the role */ +staticfn const struct def_skill * +skills_for_role(void) { const struct def_skill *skills; - int this_skill = spell_skilltype(otyp); switch (Role_switch) { case PM_ARCHEOLOGIST: @@ -1107,10 +1080,20 @@ restricted_spell_discipline(int otyp) skills = Skill_W; break; default: - skills = 0; /* lint suppression */ + panic("No skills found for role"); break; } + return skills; +} + +/* skills aren't initialized, so we use the role-specific skill lists */ +staticfn boolean +restricted_spell_discipline(int otyp) +{ + const struct def_skill *skills = skills_for_role(); + int this_skill = spell_skilltype(otyp); + while (skills && skills->skill != P_NONE) { if (skills->skill == this_skill) return FALSE; @@ -1369,8 +1352,6 @@ ini_inv(const struct trobj *trop) quan = 1; obj = addinv(obj); - ini_inv_use_obj(obj); - /* First spellbook should be level 1 - did we get it? */ if (obj->oclass == SPBOOK_CLASS && objects[obj->otyp].oc_level == 1) got_sp1 = TRUE; @@ -1382,6 +1363,59 @@ ini_inv(const struct trobj *trop) } } +/* initialise starting inventory and attributes + + this function can be run multiple times and will overwrite the effects of + previous runs */ +staticfn void +u_init_inventory_attrs(void) +{ + gl.lastinvnr = 51; + while (gi.invent) + useupall(gi.invent); + + u.umoney0 = 0; + u_init_role(); + u_init_race(); + + if (discover) + ini_inv(Wishing); + + if (wizard) { + read_wizkit(); + obj_delivery(FALSE); /* finish wizkit */ + } + + if (u.umoney0) + ini_inv(Money); + u.umoney0 += hidden_gold(TRUE); /* in case sack has gold in it */ + + init_attr(75); /* init attribute values */ + vary_init_attr(); /* minor variation to attrs */ + u_init_carry_attr_boost(); +} + +/* side effects of starting inventory (e.g. discovering it) and skills (both + those based on role and those based on starting inventory) */ +void +u_init_skills_discoveries(void) +{ + struct obj *otmp; + for (otmp = gi.invent; otmp; otmp = otmp->nobj) + ini_inv_use_obj(otmp); + + skill_init(skills_for_role()); + if (u.uroleplay.pauper) + pauper_reinit(); + + /* If we have at least one spell, force starting Pw to be enough, + so hero can cast the level 1 spell they should have */ + if (num_spells() && (u.uenmax < SPELL_LEV_PW(1))) + u.uen = u.uenmax = u.uenpeak = u.ueninc[u.ulevel] = SPELL_LEV_PW(1); + + find_ac(); /* get initial ac value */ +} + #undef UNDEF_TYP #undef UNDEF_SPE #undef UNDEF_BLESS From 656f58b099e1d318401d812303c66559d06590f6 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Sun, 30 Nov 2025 06:49:14 +0000 Subject: [PATCH 126/442] A basic version of starting character rerolling This adds a "reroll" option that lets players reroll their character's attributes and starting inventory. Although I generally think doing this makes the game worse, a) some players are going to do it regardless and b) if a player is going for a challenge game, rather than to win, it may be required. So in the absence of an option like this, players repeatedly start and quit games instead, creating a large number of junk logfile entries and generally causing problems for other players on the same shared machine (because repeatedly reloading the game is very CPU-intensive). This should in theory be windowport-agnostic (although in practice it may not be). Tested on tty, X11 and curses; on tty and X11 it works fine (although X11 treats the change in attributes as something that needs a status highlight), on curses it is slightly jankier in terms of what other windows are drawn in the background (but still plays correctly and I suspect this is a pre-existing bug). To form a complete implementation, we will need to consider the following: - Should there be a delay on a) starting the game and/or b) rerolling? If so, what should it be (maybe configurable via sysconf?) - Should we take more steps to discourage players from rerolling? It would be bad if players see the option exists and turn it on just because it exists, or (worse) treat it as condoning the particular style of play. - Should we take steps to detect that players are rerolling manually and a) tell them to use the option instead, b) tell them that this is not an intended way to play (and may make the game less enjoyable and/or prevent them getting the practice they need to eventually win)? Breaks save and bones files. --- dat/opthelp | 1 + doc/Guidebook.mn | 10 ++++++ include/extern.h | 2 ++ include/optlist.h | 3 ++ include/patchlevel.h | 2 +- include/you.h | 12 ++++--- src/allmain.c | 9 ++++-- src/botl.c | 3 +- src/insight.c | 6 ++++ src/invent.c | 75 ++++++++++++++++++++++++++++++++++++++++++++ src/topten.c | 4 +++ 11 files changed, 117 insertions(+), 10 deletions(-) diff --git a/dat/opthelp b/dat/opthelp index d36e5228b..1fcb803d9 100644 --- a/dat/opthelp +++ b/dat/opthelp @@ -56,6 +56,7 @@ pushweapon when wielding a new weapon, put your previously [False] quick_farsight usually skip the chance to browse the map when [False] randomly triggered clairvoyance takes place rawio allow the use of raw I/O [False] +reroll allow rerolling of starting inventory [False] rest_on_space count the space bar as a rest character [False] safe_pet prevent you from (knowingly) attacking your pet(s) [True] safe_wait require use of 'm' prefix before '.' or 's' to [True] diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 95bd7be35..72d3f3be3 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -4556,6 +4556,16 @@ If \f(CRrace\fP is not specified, there is no default value; player will be prompted unless role forces a choice for race. Cannot be set with the \(oq\f(CRO\fP\(cq command. Persistent. +.lp reroll +Allows rerolling your character's starting inventory and attributes (default +false). Persistent. +.lp "" +Note that rerolling your character is not a recommended way to play if aiming +merely to win (a lucky start has a much smaller influence on whether or not +you win the game than your actions later in the game). This option exists +partly as an acknowledgement that some players will insist on doing so anyway, +and partly because rerolling may be necessary for certain types of challenge +games. .lp rest_on_space Make the space bar a synonym for the \(oq.\(cq (#wait) command (default off). Persistent. diff --git a/include/extern.h b/include/extern.h index 831e209db..59e7188af 100644 --- a/include/extern.h +++ b/include/extern.h @@ -258,6 +258,7 @@ extern void free_ebones(struct monst *) NONNULLARG1; /* ### botl.c ### */ +extern char *get_strength_str(void); extern char *do_statusline1(void); extern void check_gold_symbol(void); extern char *do_statusline2(void); @@ -2406,6 +2407,7 @@ extern int query_category(const char *, struct obj *, int, menu_item **, int) NO /* dotypeinv() call query_objlist with NULL arg1 */ extern int query_objlist(const char *, struct obj **, int, menu_item **, int, boolean(*)(struct obj *)) NONNULLARG24; +extern boolean reroll_menu(void); extern struct obj *pick_obj(struct obj *) NONNULLARG1; extern void encumber_msg(void); extern int container_at(coordxy, coordxy, boolean); diff --git a/include/optlist.h b/include/optlist.h index b0bc715f3..0625b1e51 100644 --- a/include/optlist.h +++ b/include/optlist.h @@ -608,6 +608,9 @@ static int optfn_##a(int, int, boolean, char *, char *); Off, No, No, No, NoAlias, (boolean *) 0, Term_False, (char *)0) #endif + NHOPTB(reroll, Advanced, 0, opt_in, set_in_config, + Off, Yes, No, No, NoAlias, &u.uroleplay.reroll, Term_False, + "allow rerolling of starting inventory and items") NHOPTB(rest_on_space, Advanced, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias, &flags.rest_on_space, Term_False, "space bar is bound to the rest-command") diff --git a/include/patchlevel.h b/include/patchlevel.h index 42485fef6..8034d40d4 100644 --- a/include/patchlevel.h +++ b/include/patchlevel.h @@ -17,7 +17,7 @@ * Incrementing EDITLEVEL can be used to force invalidation of old bones * and save files. */ -#define EDITLEVEL 131 +#define EDITLEVEL 132 /* * Development status possibilities. diff --git a/include/you.h b/include/you.h index efa1baa69..a073e5c57 100644 --- a/include/you.h +++ b/include/you.h @@ -163,11 +163,13 @@ struct u_conduct { /* number of times... */ }; struct u_roleplay { - boolean blind; /* permanently blind */ - boolean nudist; /* has not worn any armor, ever */ - boolean deaf; /* permanently deaf */ - boolean pauper; /* no starting inventory */ - long numbones; /* # of bones files loaded */ + boolean blind; /* permanently blind */ + boolean nudist; /* has not worn any armor, ever */ + boolean deaf; /* permanently deaf */ + boolean pauper; /* no starting inventory */ + boolean reroll; /* starting inventory/attr rerolling enabled */ + long numbones; /* # of bones files loaded */ + long numrerolls; /* # of rerolls used */ }; /*** Unified structure containing role information ***/ diff --git a/src/allmain.c b/src/allmain.c index 6ced3e372..4f5e8cbb6 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -809,11 +809,16 @@ newgame(void) (void) makedog(); u_init_inventory_attrs(); - u_init_skills_discoveries(); docrt(); + flush_screen(1); + bot(); + while (u.uroleplay.reroll && reroll_menu()) { + u_init_inventory_attrs(); + bot(); + } + u_init_skills_discoveries(); if (flags.legacy) { - flush_screen(1); com_pager(u.uroleplay.pauper ? "pauper_legacy" : "legacy"); } diff --git a/src/botl.c b/src/botl.c index f3ad98aa5..f90812f53 100644 --- a/src/botl.c +++ b/src/botl.c @@ -19,9 +19,8 @@ const char *const enc_stat[] = { staticfn const char *rank(void); staticfn void bot_via_windowport(void); staticfn void stat_update_time(void); -staticfn char *get_strength_str(void); -staticfn char * +char * get_strength_str(void) { static char buf[32]; diff --git a/src/insight.c b/src/insight.c index 9f0f71059..9f3763619 100644 --- a/src/insight.c +++ b/src/insight.c @@ -2109,6 +2109,12 @@ show_conduct(int final) if (u.uroleplay.pauper) enl_msg(You_, gi.invent ? "started" : "are", "started out", " without possessions", ""); + if (u.uroleplay.reroll) { + Sprintf(buf, "rerolled your character %ld time%s", + u.uroleplay.numrerolls, plur(u.uroleplay.numrerolls)); + you_have_X(buf); + } + /* nudist is far more than a subset of possessionless, and a much more impressive accomplishment, but showing "started out without possessions" before "faithfully nudist" looks more logical */ diff --git a/src/invent.c b/src/invent.c index abb7d7c39..ddd7ffefb 100644 --- a/src/invent.c +++ b/src/invent.c @@ -2522,6 +2522,81 @@ askchain( return cnt; } + +/* The menu for rerolling attributes and inventory. + + This is similar to the other inventory menus, but simpler to help it fit on + the screen (there's more text around it and rerolling is difficult if you + can't see the whole list at once). + + Returns TRUE (and increases numrerolls) if a reroll was requested. */ +boolean +reroll_menu(void) +{ + winid win; + anything any; + menu_item *pick_list = NULL; + struct obj *otmp; + int tmpglyph; + glyph_info tmpglyphinfo; + char option; + char buf[BUFSZ]; + + win = create_nhwindow(NHW_MENU); + start_menu(win, MENU_BEHAVE_STANDARD); + any = cg.zeroany; + + any.a_char = 'n'; + add_menu(win, &nul_glyphinfo, &any, flags.lootabc ? 0 : 'p', 0, + ATR_NONE, NO_COLOR, "start the game with this character", + MENU_ITEMFLAGS_NONE); + any.a_char = 'y'; + add_menu(win, &nul_glyphinfo, &any, flags.lootabc ? 0 : 'r', 0, + ATR_NONE, NO_COLOR, "reroll another character", + MENU_ITEMFLAGS_NONE); + any.a_char = 0; + add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, NO_COLOR, "", + MENU_ITEMFLAGS_NONE); + + ++gd.distantname; /* avoid adding items to discoveries */ + ++iflags.override_ID; /* identify them */ + for (otmp = gi.invent; otmp; otmp = otmp->nobj) { + tmpglyph = obj_to_glyph(otmp, rn2_on_display_rng); + map_glyphinfo(0, 0, tmpglyph, 0U, &tmpglyphinfo); + add_menu(win, &tmpglyphinfo, &any, 0, 0, + ATR_NONE, NO_COLOR, doname(otmp), MENU_ITEMFLAGS_NONE); + } + --iflags.override_ID; + --gd.distantname; + + add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, NO_COLOR, "", + MENU_ITEMFLAGS_NONE); + Sprintf(buf, "St:%s Dx:%-1d Co:%-1d In:%-1d Wi:%-1d Ch:%-1d", + get_strength_str(), + ACURR(A_DEX), ACURR(A_CON), ACURR(A_INT), ACURR(A_WIS), + ACURR(A_CHA)); + add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, NO_COLOR, + buf, MENU_ITEMFLAGS_NONE); + + end_menu(win, "Reroll this character?"); + if (select_menu(win, PICK_ONE, &pick_list) > 0) { + option = pick_list[0].item.a_char; + free((genericptr_t) pick_list); + } else { + /* user closed the menu without selecting; unclear what their choice + is here so ask again; but (e.g. for hangup handling) stop asking if + the user cancels out again */ + option = y_n("Reroll this character?"); + } + destroy_nhwindow(win); + + if (option == 'y') { + ++u.uroleplay.numrerolls; + return TRUE; + } + return FALSE; +} + /* * Object identification routines: */ diff --git a/src/topten.c b/src/topten.c index cb968a726..d6567ea08 100644 --- a/src/topten.c +++ b/src/topten.c @@ -385,6 +385,7 @@ writexlentry(FILE *rfile, struct toptenentry *tt, int how) Fprintf(rfile, "%cwish_cnt=%ld", XLOG_SEP, u.uconduct.wishes); Fprintf(rfile, "%carti_wish_cnt=%ld", XLOG_SEP, u.uconduct.wisharti); Fprintf(rfile, "%cbones=%ld", XLOG_SEP, u.uroleplay.numbones); + Fprintf(rfile, "%crerolls=%ld", XLOG_SEP, u.uroleplay.numrerolls); Fprintf(rfile, "\n"); #undef XLOG_SEP } @@ -400,6 +401,8 @@ encodexlogflags(void) e |= 1L << 1; if (!u.uroleplay.numbones) e |= 1L << 2; + if (u.uroleplay.reroll) + e |= 1L << 3; return e; } @@ -601,6 +604,7 @@ encode_extended_conducts(char *buf) add_achieveX(buf, "pauper", u.uroleplay.pauper); add_achieveX(buf, "bonesless", !flags.bones); add_achieveX(buf, "petless", !u.uconduct.pets); + add_achieveX(buf, "unrerolled", !u.uroleplay.reroll); return buf; } From 77ac6e51d535dd4edf1134280438a0f95edb9cd6 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Sun, 30 Nov 2025 14:25:10 +0000 Subject: [PATCH 127/442] Remove stray "static" This is a globally-visible function, so it shouldn't be marked as static. --- src/u_init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/u_init.c b/src/u_init.c index e0c7cdca3..2270169ce 100644 --- a/src/u_init.c +++ b/src/u_init.c @@ -1367,7 +1367,7 @@ ini_inv(const struct trobj *trop) this function can be run multiple times and will overwrite the effects of previous runs */ -staticfn void +void u_init_inventory_attrs(void) { gl.lastinvnr = 51; From db5d77e8bead8dcc9bed07587f1d6a4d4718437b Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 30 Nov 2025 13:47:45 -0800 Subject: [PATCH 128/442] intemple() nitpick Alignment 'pious' is 20 but intemple() used that term for 14. Change intemple() to use the term 'devout' which is 14 (see enlightenment). Also, add a comment about verbalize() usage in priest_talk(). --- src/priest.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/priest.c b/src/priest.c index 10e83accc..9345fd94f 100644 --- a/src/priest.c +++ b/src/priest.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 priest.c $NHDT-Date: 1726862063 2024/09/20 19:54:23 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.103 $ */ +/* NetHack 3.7 priest.c $NHDT-Date: 1764567778 2025/11/30 21:42:58 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.106 $ */ /* Copyright (c) Izchak Miller, Steve Linhart, 1989. */ /* NetHack may be freely redistributed. See license for details. */ @@ -7,7 +7,7 @@ /* these match the categorizations shown by enlightenment */ #define ALGN_SINNED (-4) /* worse than strayed (-1..-3) */ -#define ALGN_PIOUS 14 /* better than fervent (9..13) */ +#define ALGN_DEVOUT 14 /* better than fervent (9..13) */ staticfn boolean histemple_at(struct monst *, coordxy, coordxy); staticfn boolean has_shrine(struct monst *); @@ -480,7 +480,7 @@ intemple(int roomno) other_time = &epri_p->peaceful_time; } else { msg1 = "experience %s sense of peace."; - msg2 = (u.ualign.record >= ALGN_PIOUS) ? "a" : "an unusual"; + msg2 = (u.ualign.record >= ALGN_DEVOUT) ? "a" : "an unusual"; this_time = &epri_p->peaceful_time; other_time = &epri_p->hostile_time; } @@ -561,6 +561,12 @@ priest_talk(struct monst *priest) boolean coaligned = p_coaligned(priest); boolean strayed = (u.ualign.record < 0); + /* + * Note: we won't be called if hero is Deaf [since dochat() will + * return before calling domonnoise()], so we don't need to check + * for that before the various calls to verbalize() here. + */ + /* KMH, conduct */ if (!u.uconduct.gnostic++) livelog_printf(LL_CONDUCT, @@ -898,6 +904,6 @@ restpriest(struct monst *mtmp, boolean ghostly) } #undef ALGN_SINNED -#undef ALGN_PIOUS +#undef ALGN_DEVOUT /*priest.c*/ From 56c10484898ea3250733ffd37e13ee2ced2f5785 Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 30 Nov 2025 14:43:43 -0800 Subject: [PATCH 129/442] pull request #1460 - lspo_gold() 'y' parameter Pull request by huttarl: fix a typo in lspo_gold() which was causing it to use the x coordinate for both x and y. It appears to only be used for the Fort Ludios level (knox.lua) which seems to be working as intended, so I'm not sure what is really going on. git decided to be a big hassle so I ended up just typing the one character change and ignore the commit(s). Issue #1461 is about the same situation. Fixes #1460 Fixes #1461 --- src/sp_lev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sp_lev.c b/src/sp_lev.c index 3bccc196c..21357eada 100644 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -4477,7 +4477,7 @@ lspo_gold(lua_State *L) if (argc == 3) { amount = luaL_checkinteger(L, 1); x = gldx = luaL_checkinteger(L, 2); - y = gldy = luaL_checkinteger(L, 2); + y = gldy = luaL_checkinteger(L, 3); } else if (argc == 2 && lua_type(L, 2) == LUA_TTABLE) { amount = luaL_checkinteger(L, 1); (void) get_coord(L, 2, &gldx, &gldy); From 089424fef26f3caaed81520da35e28b51f7b9743 Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 1 Dec 2025 09:01:33 -0500 Subject: [PATCH 130/442] update Makefile.nmake don't include sfctool unless explicitly sought via SFCTOOL=1 on the make command line. add [optional] nmake fetch-ctags nmake build-ctags --- sys/windows/Makefile.nmake | 62 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index 85772ed51..a2b6cd3c9 100644 --- a/sys/windows/Makefile.nmake +++ b/sys/windows/Makefile.nmake @@ -2065,6 +2065,54 @@ fetch-pdcurses: @echo $(PDCDIST) has been fetched into $(LIBDIR)$(PDCDIST) !ENDIF +#https://github.com/universal-ctags/ctags/archive/refs/tags/v6.2.1.zip +CTAGSVER=6.2.1 +CTAGSURL=https://github.com/universal-ctags/ctags/archive/refs/tags/v$(CTAGSVER).zip + +fetch-ctags: $(LIBDIR)ctags.zip + +build-ctags: $(LIBDIR)ctags\ctags-$(CTAGSVER)\ctags.exe + +$(LIBDIR)ctags\ctags-$(CTAGSVER)\ctags.exe: $(LIBDIR)ctags\ctags-$(CTAGSVER)\mk_mvc.mak + @ECHO on + cd $(LIBDIR) + cd ctags\ctags-$(CTAGSVER) + copy win32\config_mvc.h config.h + copy win32\gnulib_h\langinfo.h gnulib + copy win32\gnulib_h\fnmatch.h gnulib + nmake -f mk_mvc.mak + copy /Y ctags.exe ..\ctags.exe + @echo ctags has been compiled into $(LIBDIR)ctags + @ECHO off + cd ..\.. + cd $(R_SRC) + +$(LIBDIR)ctags.zip: + @if not exist $(LIBDIR)*.* mkdir $(R_LIBDIR) + cd $(LIBDIR) + curl -L -R $(CTAGSURL) -o ctags.zip + cd $(R_SRC) + @echo ctags source has been fetched into $@ + +$(LIBDIR)ctags\ctags-$(CTAGSVER)\mk_mvc.mak: $(LIBDIR)ctags.zip + @if not exist $(LIBDIR)*.* mkdir $(R_LIBDIR) + cd $(LIBDIR) + powershell -command "Expand-Archive -Path $(LIBDIR)ctags.zip -DestinationPath $(LIBDIR)ctags" + cd $(R_SRC) + @echo ctags source has been expanded from zip file into $(LIBDIR)\ctags\ctags-$(CTAGSVER) + +clean-ctags: + cd $(LIBDIR) + rd $(LIBDIR)\ctags\ctags-$(CTAGSVER) /S + cd $(R_SRC) + +spotless-ctags: + cd $(LIBDIR) + @if exist ctags\ctags.exe del ctags\ctags.exe + @if exist ctags.zip del ctags.zip + cd $(R_SRC) + + #========================================== # DLB utility and nhdatNNN file creation #========================================== @@ -2490,6 +2538,14 @@ $(DAT)bogusmon: $(U)makedefs.exe $(DAT)bogusmon.txt #=============================================================================== # sfutil #=============================================================================== +SFCTOOLBIN = $(BinDir)sfctool.exe +!IF "$(SFCTOOL)" == "1" +SFCTOOLPKG=$(SFCTOOLBIN) +!ELSE +SFCTOOLPKG= +!ENDIF + +!IF "$(SFCTOOLPKG)" != "" SFCTOOLOBJS = $(OUTL)sfctool.o \ $(OUTL)sf-alloc.o $(OUTL)sf-artifact.o $(OUTL)sfdata.o \ $(OUTL)sf-date.o $(OUTL)sf-decl.o $(OUTL)sf-calendar.o \ @@ -2503,7 +2559,6 @@ SFCTOOLOBJS = $(OUTL)sfctool.o \ $(OUTL)sf-sys.o $(OUTL)sf-timeout.o $(OUTL)sf-track.o \ $(OUTL)sf-version.o $(OUTL)sf-worm.o $(OUTL)sf-windsys.o -SFCTOOLBIN = $(BinDir)sfctool.exe SFFLAGS = -DSFCTOOL -DNOPANICTRACE -DNOCRASHREPORT -DNO_CHRONICLE #CTAGSCMD = $(LIB)\ctags\ctags.exe @@ -2610,7 +2665,7 @@ CTAGDEP = $(INCL)align.h $(INCL)artifact.h $(INCL)artilist.h \ # $(INCL)permonst.h CTAGSOPT = --language-force=c --sort=no -D"Bitfield(x,n)=unsigned x : n" --excmd=pattern # -$(UTIL)sf.tags: $(CTAGDEP) +$(UTIL)sf.tags: $(CTAGSCMD) $(CTAGDEP) $(CTAGSCMD) $(CTAGSOPT) -f $@ $(INCL)align.h $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)artifact.h $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(SRC)artifact.c @@ -2649,6 +2704,7 @@ $(UTIL)sf.tags: $(CTAGDEP) $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)you.h $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)onames.h $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)wintype.h +!ENDIF #=============================================================================== # Integrated sound files @@ -2725,7 +2781,7 @@ PKGFILES = nethackrc.template Guidebook.txt license NetHack.exe NetHack.txt \ FILESTOZIP = $(BinDir)nethackrc.template $(BinDir)Guidebook.txt $(BinDir)license \ $(BinDir)NetHack.exe $(BinDir)NetHack.txt $(BinDir)NetHackW.exe \ $(BinDir)opthelp $(BinDir)nhdat370 $(BinDir)record \ - $(BinDir)symbols $(BinDir)sysconf.template $(BinDir)sfctool.exe + $(BinDir)symbols $(BinDir)sysconf.template $(SFCTOOLPKG) DBGSYMS = NetHack.PDB NetHackW.PDB sfctool.PDB PDBTOZIP = $(BinDir)NetHack.PDB $(BinDir)NetHackW.PDB $(BinDir)sfctool.PDB From 44f6a779a76733423b8aa6dbb7cf920cd8bfa75d Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 2 Dec 2025 08:37:37 -0500 Subject: [PATCH 131/442] follow-up for Makefile.nmake Fix package error Closes #1468 --- sys/windows/Makefile.nmake | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index a2b6cd3c9..16cbfee41 100644 --- a/sys/windows/Makefile.nmake +++ b/sys/windows/Makefile.nmake @@ -2541,8 +2541,14 @@ $(DAT)bogusmon: $(U)makedefs.exe $(DAT)bogusmon.txt SFCTOOLBIN = $(BinDir)sfctool.exe !IF "$(SFCTOOL)" == "1" SFCTOOLPKG=$(SFCTOOLBIN) +SFCTOOLBASENAM=sfctool.exe +SFCTOOLPDBNAM=sfctool.PDB +SFCTOOLPDBBIN=$(BinDir)sfctool.PDB !ELSE SFCTOOLPKG= +SFCTOOLBASENAM= +SFCTOOLPDBNAM= +SFCTOOLPDBBIN= !ENDIF !IF "$(SFCTOOLPKG)" != "" @@ -2776,15 +2782,15 @@ $(SndWavDir)sa2_xplevelup.wav: $(SndWavDir)sa2_xplevelup.uu $(U)uudecode.exe #=============================================================================== PKGFILES = nethackrc.template Guidebook.txt license NetHack.exe NetHack.txt \ - NetHackW.exe opthelp nhdat370 record symbols sysconf.template \ - sfctool.exe + NetHackW.exe opthelp nhdat370 record symbols \ + sysconf.template $(SFCTOOLBASENAM) FILESTOZIP = $(BinDir)nethackrc.template $(BinDir)Guidebook.txt $(BinDir)license \ $(BinDir)NetHack.exe $(BinDir)NetHack.txt $(BinDir)NetHackW.exe \ $(BinDir)opthelp $(BinDir)nhdat370 $(BinDir)record \ $(BinDir)symbols $(BinDir)sysconf.template $(SFCTOOLPKG) -DBGSYMS = NetHack.PDB NetHackW.PDB sfctool.PDB -PDBTOZIP = $(BinDir)NetHack.PDB $(BinDir)NetHackW.PDB $(BinDir)sfctool.PDB +DBGSYMS = NetHack.PDB NetHackW.PDB $(SFCTOOLPDB) +PDBTOZIP = $(BinDir)NetHack.PDB $(BinDir)NetHackW.PDB $(SFCTOOLPDBBIN) MAINZIP = $(PkgDir)nethack-$(NHV)-win-$(TARGET_CPU).zip DBGSYMZIP = $(PkgDir)nethack-$(NHV)-win-$(TARGET_CPU)-debugsymbols.zip @@ -2805,7 +2811,7 @@ binary: envchk.tag libdir.tag ottydir$(TARGET_CPU).tag \ $(INCL)nhlua.h $(OUTL)utility.tag \ $(DAT)data $(DAT)rumors $(DAT)oracles $(DAT)engrave \ $(DAT)epitaph $(DAT)bogusmon $(GAMEDIR)NetHack.exe \ - $(GAMEDIR)NetHackW.exe $(GAMEDIR)sfctool.exe $(GAMEDIRDLLS) binary.tag + $(GAMEDIR)NetHackW.exe $(SFCTOOLPKG) $(GAMEDIRDLLS) binary.tag @echo NetHack is up to date. #=============================================================================== From 727608411ab47ddfdc5f197cde6ee0faaaf4c008 Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 2 Dec 2025 09:48:10 -0500 Subject: [PATCH 132/442] a follow-up bit package up debug symbols for sfctool if SFCTOOL=1 --- sys/windows/Makefile.nmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index 16cbfee41..7977841ce 100644 --- a/sys/windows/Makefile.nmake +++ b/sys/windows/Makefile.nmake @@ -2789,7 +2789,7 @@ FILESTOZIP = $(BinDir)nethackrc.template $(BinDir)Guidebook.txt $(BinDir)license $(BinDir)opthelp $(BinDir)nhdat370 $(BinDir)record \ $(BinDir)symbols $(BinDir)sysconf.template $(SFCTOOLPKG) -DBGSYMS = NetHack.PDB NetHackW.PDB $(SFCTOOLPDB) +DBGSYMS = NetHack.PDB NetHackW.PDB $(SFCTOOLPDBNAM) PDBTOZIP = $(BinDir)NetHack.PDB $(BinDir)NetHackW.PDB $(SFCTOOLPDBBIN) MAINZIP = $(PkgDir)nethack-$(NHV)-win-$(TARGET_CPU).zip DBGSYMZIP = $(PkgDir)nethack-$(NHV)-win-$(TARGET_CPU)-debugsymbols.zip From cfdee27498d9faed11de9e9d5641044267be6a61 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Tue, 2 Dec 2025 15:23:50 +0000 Subject: [PATCH 133/442] pauper versus wizard spellbook auto-ID These two enhancements were interacting with each other in weird ways (paupers would start with no auto-IDs but force bolt, but gain the level 1 auto-IDs upon training any skill). Change the interaction so that paupers don't get the level 1 auto-IDs (which wizards get to start with), but do get the level 3 auto-IDs in skills that they manage to advance. --- src/spell.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/spell.c b/src/spell.c index 1f2c6a823..847f53335 100644 --- a/src/spell.c +++ b/src/spell.c @@ -891,7 +891,9 @@ skill_based_spellbook_id(void) break; case P_UNSKILLED: default: - known_up_to_level = 1; + /* paupers need more skill than this to ID books, but most wizards + know the basics */ + known_up_to_level = u.uroleplay.pauper ? 0 : 1; break; } From 29df69d542320004b071e21723065061c7deef83 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Tue, 2 Dec 2025 18:14:40 +0000 Subject: [PATCH 134/442] Writing on an unidentified scroll of blank paper identifies paper The character doesn't know that an unlabeled scroll is blank until they look at the inside of the scroll, but that could be done either by reading or by writing. --- doc/fixes3-7-0.txt | 1 + src/write.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 36add38e3..61f00da9b 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1549,6 +1549,7 @@ you cannot sacrifice objects/corpses while stunned or confused 'whatis' actions // and /? didn't work when the lootabc option was on, they required /a and /c instead; add '/' and '?' as group accelerators so that they work; /y and /n for them now only work when lootabc is off +writing on an unidentified scroll of blank paper identifies blank paper Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/write.c b/src/write.c index 538406f34..f90c13b06 100644 --- a/src/write.c +++ b/src/write.c @@ -105,7 +105,7 @@ dowrite(struct obj *pen) : "scroll"; if (Blind) { if (!paper->dknown) { - You("don't know if that %s is blank or not.", typeword); + You("don't know whether that %s is blank or not.", typeword); return ECMD_OK; } else if (paper->oclass == SPBOOK_CLASS) { /* can't write a magic book while blind */ @@ -120,6 +120,7 @@ dowrite(struct obj *pen) exercise(A_WIS, FALSE); return ECMD_TIME; } + makeknown(SCR_BLANK_PAPER); /* what to write */ Sprintf(qbuf, "What type of %s do you want to write?", typeword); From a0ccb4b0cb82f4f2adf9236400155c9f0a340205 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Tue, 2 Dec 2025 19:19:18 +0000 Subject: [PATCH 135/442] Show spells and skills in the dumplog These are often an important part of a character's build. There's no purpose in listing them in disclose because the player generally already knows what spells and skills they had and doesn't need them identified, but they're useful when looking at someone else's game or reminiscing over a past game. --- doc/fixes3-7-0.txt | 1 + include/extern.h | 2 + src/end.c | 10 +-- src/spell.c | 26 ++++++- src/weapon.c | 170 +++++++++++++++++++++++++++------------------ 5 files changed, 135 insertions(+), 74 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 61f00da9b..bb0b879c9 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1550,6 +1550,7 @@ you cannot sacrifice objects/corpses while stunned or confused required /a and /c instead; add '/' and '?' as group accelerators so that they work; /y and /n for them now only work when lootabc is off writing on an unidentified scroll of blank paper identifies blank paper +dumplogs include spells and skills Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/include/extern.h b/include/extern.h index 59e7188af..8a5056d91 100644 --- a/include/extern.h +++ b/include/extern.h @@ -3041,6 +3041,7 @@ extern int spelleffects(int, boolean, boolean); extern int tport_spell(int); extern void losespells(void); extern int dovspell(void); +extern void show_spells(void); extern void initialspell(struct obj *) NONNULLARG1; extern int known_spell(short); extern int spell_idx(short); @@ -3673,6 +3674,7 @@ extern void dry_a_towel(struct obj *, int, boolean) NONNULLARG1; extern char *skill_level_name(int, char *) NONNULLARG2; extern const char *skill_name(int); extern boolean can_advance(int, boolean); +extern void show_skills(void); extern int enhance_weapon_skill(void); extern void unrestrict_weapon_skill(int); extern void use_skill(int, int); diff --git a/src/end.c b/src/end.c index 30ba0e91d..f7f61020e 100644 --- a/src/end.c +++ b/src/end.c @@ -599,14 +599,16 @@ dump_everything( /* overview of the game up to this point */ show_gamelog((how >= PANICKED) ? ENL_GAMEOVERALIVE : ENL_GAMEOVERDEAD); putstr(0, 0, ""); - list_vanquished('d', FALSE); /* 'd' => 'y' */ - putstr(0, 0, ""); - list_genocided('d', FALSE); /* 'd' => 'y' */ - putstr(0, 0, ""); + show_spells(); /* ends with a blank line */ + show_skills(); /* ends with a blank line */ show_conduct((how >= PANICKED) ? 1 : 2); putstr(0, 0, ""); show_overview((how >= PANICKED) ? 1 : 2, how); putstr(0, 0, ""); + list_vanquished('d', FALSE); /* 'd' => 'y' */ + putstr(0, 0, ""); + list_genocided('d', FALSE); /* 'd' => 'y' */ + putstr(0, 0, ""); dump_redirect(FALSE); #else nhUse(how); diff --git a/src/spell.c b/src/spell.c index 847f53335..2e64b2c17 100644 --- a/src/spell.c +++ b/src/spell.c @@ -5,6 +5,7 @@ #include "hack.h" /* spellmenu arguments; 0..n-1 used as svs.spl_book[] index when swapping */ +#define SPELLMENU_DUMP (-3) #define SPELLMENU_CAST (-2) #define SPELLMENU_VIEW (-1) #define SPELLMENU_SORT (MAXSPELL) /* special menu entry */ @@ -2043,13 +2044,27 @@ dovspell(void) DISABLE_WARNING_FORMAT_NONLITERAL +/* lists spells for endgame dumplog purposes */ +void +show_spells(void) +{ + int unused = SPELLMENU_DUMP; + if (spellid(0) == NO_SPELL) { + pline("You didn't know any spells."); + pline("%s", ""); + } else { + pline("Spells:"); + nhUse(dospellmenu("", SPELLMENU_DUMP, &unused)); + } +} + /* shows menu of known spells, with options to sort them. return FALSE on cancel, TRUE otherwise. spell_no is set to the internal spl_book index, if any selected */ staticfn boolean dospellmenu( const char *prompt, - int splaction, /* SPELLMENU_CAST, SPELLMENU_VIEW, or + int splaction, /* SPELLMENU_CAST, SPELLMENU_VIEW, SPELLMENU_DUMP or * svs.spl_book[] index */ int *spell_no) { @@ -2071,10 +2086,14 @@ dospellmenu( * (1) that the font is monospaced, and * (2) that selection letters are pre-pended to the * given string and are of the form "a - ". + * For SPELLMENU_DUMP, (2) is untrue, so four spaces + * need to be subtracted. */ if (!iflags.menu_tab_sep) { - Sprintf(buf, "%-20s Level %-12s Fail Retention", - " Name", "Category"); + Sprintf(buf, "%s%-20s Level %-12s Fail Retention", + splaction == SPELLMENU_DUMP ? "" : " ", + "Name", + "Category"); fmt = "%-20s %2d %-12s %3d%% %9s"; sep = ' '; } else { @@ -2102,6 +2121,7 @@ dospellmenu( ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE); } how = PICK_ONE; + how = PICK_NONE; if (splaction == SPELLMENU_VIEW) { if (spellid(1) == NO_SPELL) { /* only one spell => nothing to swap with */ diff --git a/src/weapon.c b/src/weapon.c index 59fa7c30d..317fd7c08 100644 --- a/src/weapon.c +++ b/src/weapon.c @@ -16,6 +16,7 @@ staticfn boolean could_advance(int); staticfn boolean peaked_skill(int); staticfn int slots_required(int); staticfn void skill_advance(int); +staticfn void add_skills_to_menu(winid, boolean, boolean); /* Categories whose names don't come from OBJ_NAME(objects[type]) */ @@ -1215,6 +1216,102 @@ static const struct skill_range { { P_FIRST_SPELL, P_LAST_SPELL, "Spellcasting Skills" }, }; +/* write a list of skills onto the given menu + + if selectable is set, give selection letters for skills that can be + advanced and leave room for them on skills that can't be advanced */ +void +add_skills_to_menu(winid win, boolean selectable, boolean speedy) +{ + int pass, i, len, longest; + anything any; + char buf[BUFSZ], sklnambuf[BUFSZ]; + const char *prefix; + int clr = NO_COLOR; + + /* Find the longest skill name. */ + for (longest = 0, i = 0; i < P_NUM_SKILLS; i++) { + if (P_RESTRICTED(i)) + continue; + if ((len = Strlen(P_NAME(i))) > longest) + longest = len; + } + + /* List the skills, making ones that could be advanced selectable if + selectable is set. List the miscellaneous skills first. Possible + future enhancement: list spell skills before weapon skills for + spellcaster roles. */ + for (pass = 0; pass < SIZE(skill_ranges); pass++) + for (i = skill_ranges[pass].first; i <= skill_ranges[pass].last; + i++) { + /* Print headings for skill types */ + any = cg.zeroany; + if (i == skill_ranges[pass].first) + add_menu_heading(win, skill_ranges[pass].name); + + if (P_RESTRICTED(i)) + continue; + /* + * Sigh, this assumes a monospaced font unless + * iflags.menu_tab_sep is set in which case it puts + * tabs between columns. + * The 12 is the longest skill level name. + * The " " is room for a selection letter and dash, "a - ". + */ + if (!selectable) + prefix = ""; + else if (can_advance(i, speedy)) + prefix = ""; /* will be preceded by menu choice */ + else if (could_advance(i)) + prefix = " * "; + else if (peaked_skill(i)) + prefix = " # "; + else + prefix = " "; + (void) skill_level_name(i, sklnambuf); + if (wizard) { + if (!iflags.menu_tab_sep) + Snprintf(buf, sizeof buf, + " %s%-*s %-12s %5d(%4d)", prefix, + longest, P_NAME(i), sklnambuf, P_ADVANCE(i), + practice_needed_to_advance(P_SKILL(i))); + else + Snprintf(buf, sizeof buf, + " %s%s\t%s\t%5d(%4d)", prefix, P_NAME(i), + sklnambuf, P_ADVANCE(i), + practice_needed_to_advance(P_SKILL(i))); + } else { + if (!iflags.menu_tab_sep) + Snprintf(buf, sizeof buf, + " %s %-*s [%s]", prefix, longest, + P_NAME(i), sklnambuf); + else + Snprintf(buf, sizeof buf, + " %s%s\t[%s]", prefix, P_NAME(i), + sklnambuf); + } + any.a_int = selectable && can_advance(i, speedy) ? i + 1 : 0; + add_menu(win, &nul_glyphinfo, &any, 0, 0, + ATR_NONE, clr, buf, MENU_ITEMFLAGS_NONE); + } +} + +/* Displays a skill list for dumplog purposes. */ +void +show_skills(void) +{ + winid win; + menu_item *selected; + + pline("Skills:"); + win = create_nhwindow(NHW_MENU); + start_menu(win, MENU_BEHAVE_STANDARD); + add_skills_to_menu(win, FALSE, FALSE); + end_menu(win, ""); + nhUse(select_menu(win, PICK_NONE, &selected)); + destroy_nhwindow(win); +} + /* * The `#enhance' extended command. What we _really_ would like is * to keep being able to pick things to advance until we couldn't any @@ -1226,14 +1323,11 @@ static const struct skill_range { int enhance_weapon_skill(void) { - int pass, i, n, len, longest, to_advance, eventually_advance, maxxed_cnt; - char buf[BUFSZ], sklnambuf[BUFSZ]; - const char *prefix; + int i, n, to_advance, eventually_advance, maxxed_cnt; + char buf[BUFSZ]; menu_item *selected; - anything any; winid win; boolean speedy = FALSE; - int clr = NO_COLOR; /* player knows about #enhance, don't show tip anymore */ svc.context.tips[TIP_ENHANCE] = TRUE; @@ -1242,13 +1336,11 @@ enhance_weapon_skill(void) speedy = TRUE; do { - /* find longest available skill name, count those that can advance */ + /* count advanceable skills */ to_advance = eventually_advance = maxxed_cnt = 0; - for (longest = 0, i = 0; i < P_NUM_SKILLS; i++) { + for (i = 0; i < P_NUM_SKILLS; i++) { if (P_RESTRICTED(i)) continue; - if ((len = Strlen(P_NAME(i))) > longest) - longest = len; if (can_advance(i, speedy)) to_advance++; else if (could_advance(i)) @@ -1280,64 +1372,8 @@ enhance_weapon_skill(void) add_menu_str(win, ""); } - /* List the skills, making ones that could be advanced - selectable. List the miscellaneous skills first. - Possible future enhancement: list spell skills before - weapon skills for spellcaster roles. */ - for (pass = 0; pass < SIZE(skill_ranges); pass++) - for (i = skill_ranges[pass].first; i <= skill_ranges[pass].last; - i++) { - /* Print headings for skill types */ - any = cg.zeroany; - if (i == skill_ranges[pass].first) - add_menu_heading(win, skill_ranges[pass].name); - - if (P_RESTRICTED(i)) - continue; - /* - * Sigh, this assumes a monospaced font unless - * iflags.menu_tab_sep is set in which case it puts - * tabs between columns. - * The 12 is the longest skill level name. - * The " " is room for a selection letter and dash, "a - ". - */ - if (can_advance(i, speedy)) - prefix = ""; /* will be preceded by menu choice */ - else if (could_advance(i)) - prefix = " * "; - else if (peaked_skill(i)) - prefix = " # "; - else - prefix = - (to_advance + eventually_advance + maxxed_cnt > 0) - ? " " - : ""; - (void) skill_level_name(i, sklnambuf); - if (wizard) { - if (!iflags.menu_tab_sep) - Snprintf(buf, sizeof buf, - " %s%-*s %-12s %5d(%4d)", prefix, - longest, P_NAME(i), sklnambuf, P_ADVANCE(i), - practice_needed_to_advance(P_SKILL(i))); - else - Snprintf(buf, sizeof buf, - " %s%s\t%s\t%5d(%4d)", prefix, P_NAME(i), - sklnambuf, P_ADVANCE(i), - practice_needed_to_advance(P_SKILL(i))); - } else { - if (!iflags.menu_tab_sep) - Snprintf(buf, sizeof buf, - " %s %-*s [%s]", prefix, longest, - P_NAME(i), sklnambuf); - else - Snprintf(buf, sizeof buf, - " %s%s\t[%s]", prefix, P_NAME(i), - sklnambuf); - } - any.a_int = can_advance(i, speedy) ? i + 1 : 0; - add_menu(win, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, buf, MENU_ITEMFLAGS_NONE); - } + add_skills_to_menu( + win, to_advance + eventually_advance + maxxed_cnt > 0, speedy); Strcpy(buf, (to_advance > 0) ? "Pick a skill to advance:" : "Current skills:"); From 373560c69a1eb69d86d27b0d01860cba47b32558 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Tue, 2 Dec 2025 19:57:49 +0000 Subject: [PATCH 136/442] Delete an accidentally left in line of code This wasn't meant to be there. --- src/spell.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/spell.c b/src/spell.c index 2e64b2c17..2626de50c 100644 --- a/src/spell.c +++ b/src/spell.c @@ -2121,7 +2121,6 @@ dospellmenu( ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE); } how = PICK_ONE; - how = PICK_NONE; if (splaction == SPELLMENU_VIEW) { if (spellid(1) == NO_SPELL) { /* only one spell => nothing to swap with */ From 621d21ebb9d1ff29506a9a6522840565283defb1 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Wed, 3 Dec 2025 21:35:17 +0000 Subject: [PATCH 137/442] Archeologists can decipher scroll labels Prior to this commit, Archeologists had an incredibly difficult start, worse than was intended. This is intended to make it a bit easier (whilst making the role more different from other roles) via allowing them to identify scroll types earlier in the game than the other roles are able to (they do it using a knowledge of ancient languages that lets them read scroll labels that other roles couldn't). --- doc/Guidebook.mn | 3 ++- doc/fixes3-7-0.txt | 9 +++++---- src/invent.c | 44 +++++++++++++++++++++++++++++++++++--------- 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 72d3f3be3..1094c6662 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -131,7 +131,8 @@ will vary with your background and training: .pg \fIArcheologists\fP understand dungeons pretty well; this enables them to move quickly and sneak up on the local nasties. They start equipped -with the tools for a proper scientific expedition. +with the tools for a proper scientific expedition, and are able to read +ancient languages. .pg \fIBarbarians\fP are warriors out of the hinterland, hardened to battle. They begin their quests with naught but uncommon strength, a trusty hauberk, diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index bb0b879c9..3729ff0b9 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1551,6 +1551,10 @@ you cannot sacrifice objects/corpses while stunned or confused that they work; /y and /n for them now only work when lootabc is off writing on an unidentified scroll of blank paper identifies blank paper dumplogs include spells and skills +uncursed genocide while hallucinating deliberately mis-reports hero's role as + target but was also inappropriately showing it to livelog/#chronicle +livelog/#chronicle for bribing a demon lord reported random monster and + currency if hero was hallucinating Fixes to 3.7.0-x General Problems Exposed Via git Repository @@ -2849,10 +2853,7 @@ archeologists' fedora is lucky telepathic hero can discern which particular monster just read a scroll add "make fetch-docs" to download pre-formatted documentation monsters will use throw-and-return weapons such as an aklys -uncursed genocide while hallucinating deliberately mis-reports hero's role as - target but was also inappropriately showing it to livelog/#chronicle -livelog/#chronicle for bribing a demon lord reported random monster and - currency if hero was hallucinating +archeologists can identify scrolls by deciphering their labels Platform- and/or Interface-Specific New Features diff --git a/src/invent.c b/src/invent.c index ddd7ffefb..f3c7f3803 100644 --- a/src/invent.c +++ b/src/invent.c @@ -965,8 +965,6 @@ merged(struct obj **potmp, struct obj **pobj) * This is called when adding objects to the hero's inventory normally (via * addinv) or when an object in the hero's inventory has been polymorphed * in-place. - * - * It may be valid to merge this code with addinv_core2(). */ void addinv_core1(struct obj *obj) @@ -1017,22 +1015,47 @@ addinv_core1(struct obj *obj) } /* - * Adjust hero intrinsics as if this object was being added to the hero's - * inventory. Called _after_ the object has been added to the hero's - * inventory. + * Adjust hero intrinsics (and perform other side effects) as if this + * object was being added to the hero's inventory. Called _after_ the + * object has been added to the hero's inventory. + * + * This can be used either for updating intrinsics, or to allow the hero to + * react to objects that are now in inventory. * * This is called when adding objects to the hero's inventory normally (via - * addinv) or when an object in the hero's inventory has been polymorphed - * in-place. + * addinv), when an object in the hero's inventory has been polymorphed + * in-place, or when the hero re-examines objects that they picked up while + * blind. + * + * This may occasionally be called on an item that was already in inventory, + * so it should be written to work even if called multiple times in a row + * (e.g. do not assume that the object was not in inventory already). */ void addinv_core2(struct obj *obj) { if (confers_luck(obj)) { /* new luckstone must be in inventory by this point - * for correct calculation */ + for correct calculation */ set_moreluck(); } + + /* Archeologists can decipher the writing on a scroll label to work out + what they are (exception: unlabeled scrolls don't have a label to + decipher) */ + if (Role_if(PM_ARCHEOLOGIST) && obj->oclass == SCROLL_CLASS && + obj->otyp != SCR_BLANK_PAPER && !Blind && + !objects[obj->otyp].oc_name_known) { + observe_object(obj); + pline("You decipher the label on %s.", yname(obj)); + makeknown(obj->otyp); + + /* conduct: this is avoidable via not picking up / wishing for + scrolls */ + if (!u.uconduct.literate++) + livelog_printf(LL_CONDUCT, + "became literate by deciphering a scroll label"); + } } /* @@ -2738,12 +2761,15 @@ learn_unseen_invent(void) return; /* sanity check */ for (otmp = gi.invent; otmp; otmp = otmp->nobj) { - if (otmp->dknown && (otmp->bknown || !Role_if(PM_CLERIC))) + if (otmp->dknown && (otmp->bknown || !Role_if(PM_CLERIC)) && + (otmp->oclass != SCROLL_CLASS || !Role_if(PM_ARCHEOLOGIST))) continue; /* already seen */ invupdated = TRUE; /* xname() will set dknown, perhaps bknown (for priest[ess]); result from xname() is immediately released for re-use */ maybereleaseobuf(xname(otmp)); + addinv_core2(otmp); /* you react to seeing the object */ + /* * If object->eknown gets implemented (see learnwand(zap.c)), * handle deferred discovery here. From 497c5dec902808dcd258f99c64fe28394ce74a14 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Wed, 3 Dec 2025 22:01:05 +0000 Subject: [PATCH 138/442] Do not count wizkits as part of starting inventory Counting wizkits as starting inventory causes side effects (giving you skills from them, making the wizkit items formally identified, etc.). Some of these side effects are undesirable (because, e.g., it can give you skill in a weapon you're restricted in), and the others are mostly neutral (because wizard-mode players can identify anything they want to). As such, it's better to treat the wizkit as something you obtain immediately after entering the dungeon, rather than something you had all along. --- src/allmain.c | 5 +++++ src/u_init.c | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/allmain.c b/src/allmain.c index 4f5e8cbb6..710063045 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -818,6 +818,11 @@ newgame(void) } u_init_skills_discoveries(); + if (wizard) { + read_wizkit(); + obj_delivery(FALSE); /* finish wizkit */ + } + if (flags.legacy) { com_pager(u.uroleplay.pauper ? "pauper_legacy" : "legacy"); } diff --git a/src/u_init.c b/src/u_init.c index 2270169ce..2ce7461bb 100644 --- a/src/u_init.c +++ b/src/u_init.c @@ -1381,11 +1381,6 @@ u_init_inventory_attrs(void) if (discover) ini_inv(Wishing); - if (wizard) { - read_wizkit(); - obj_delivery(FALSE); /* finish wizkit */ - } - if (u.umoney0) ini_inv(Money); u.umoney0 += hidden_gold(TRUE); /* in case sack has gold in it */ From 02ce11a7c6fafc07b1c1a4deddfa33f89834149c Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Wed, 3 Dec 2025 23:19:58 +0000 Subject: [PATCH 139/442] Move some chronicle-related fixes entries to the correct section --- doc/fixes3-7-0.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 3729ff0b9..f7998fdc4 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1551,10 +1551,6 @@ you cannot sacrifice objects/corpses while stunned or confused that they work; /y and /n for them now only work when lootabc is off writing on an unidentified scroll of blank paper identifies blank paper dumplogs include spells and skills -uncursed genocide while hallucinating deliberately mis-reports hero's role as - target but was also inappropriately showing it to livelog/#chronicle -livelog/#chronicle for bribing a demon lord reported random monster and - currency if hero was hallucinating Fixes to 3.7.0-x General Problems Exposed Via git Repository @@ -2165,6 +2161,13 @@ throwing crystal plate mail or helm of brilliance up against the ceiling could yn_function (used all over the place) sometimes triggered an impossible() during do-again (^A) processing [ongoing revisions; more are probably needed; listed here in case ^A behavior changes] +uncursed genocide while hallucinating deliberately mis-reports hero's role as + target but was also inappropriately showing it to livelog/#chronicle +livelog/#chronicle for bribing a demon lord reported random monster and + currency if hero was hallucinating +livelog/#chronicle for saving-grace: if saving-grace prevents hero's death, + report it [at present, it uses the livelog classification for breaking + a conduct] Fixes to 3.7.0-x Platform and/or Interface Problems Exposed Via git Repository @@ -2833,9 +2836,6 @@ wand of secret door detection, spell of detect unseen, and wizard mode ^E now flash the cursor at each location where detection finds something saving-grace: once per game if receiving a killing blow from full or nearly full HP, survive with 1 HP -livelog/#chronicle for saving-grace: if saving-grace prevents hero's death, - report it [at present, it uses the livelog classification for breaking - a conduct] enlightenment/attribute disclosure for saving-grace: include a line for have or haven't been saved (game in progress) or did or didn't get saved (game over) via saving-grace From 9321316634491eeb0f0abb354358560186b6a269 Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 5 Dec 2025 19:55:43 -0500 Subject: [PATCH 140/442] Guidebook date stamp to match most recent change --- doc/Guidebook.mn | 2 +- doc/Guidebook.tex | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 1094c6662..ac9fc70b5 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -47,7 +47,7 @@ .ds f0 \*(vr .ds f1 \" empty .\"DO NOT REMOVE NH_DATESUB .ds f2 Date(%B %-d, %Y) -.ds f2 October 24, 2025 +.ds f2 December 03, 2025 . .\" A note on some special characters: .\" \(lq = left double quote diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 4bd01c70c..bdc21ecc6 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -48,7 +48,7 @@ \author{Original version - Eric S. Raymond\\ (Edited and expanded for 3.7.0 by Mike Stephenson and others)} %DO NOT REMOVE NH_DATESUB \date{Date(%B %-d, %Y)} -\date{October 24, 2025} +\date{December 03, 2025} \maketitle From 80cb9d03ce865104cba81724cfb846a4529b7640 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 6 Dec 2025 08:48:48 -0500 Subject: [PATCH 141/442] updated Guidebook.txt --- doc/Guidebook.txt | 3078 ++++++++++++++++++++++----------------------- 1 file changed, 1539 insertions(+), 1539 deletions(-) diff --git a/doc/Guidebook.txt b/doc/Guidebook.txt index be96fb67d..d4a90d378 100644 --- a/doc/Guidebook.txt +++ b/doc/Guidebook.txt @@ -15,25 +15,25 @@ Original version - Eric S. Raymond (Edited and expanded for NetHack 3.7.0 by Mike Stephenson and others) - October 24, 2025 + December 05, 2025 1. Introduction Recently, you have begun to find yourself unfulfilled and distant - in your daily occupation. Strange dreams of prospecting, stealing, - crusading, and combat have haunted you in your sleep for many months, - but you aren't sure of the reason. You wonder whether you have in - fact been having those dreams all your life, and somehow managed to - forget about them until now. Some nights you awaken suddenly and cry - out, terrified at the vivid recollection of the strange and powerful - creatures that seem to be lurking behind every corner of the dungeon - in your dream. Could these details haunting your dreams be real? As + in your daily occupation. Strange dreams of prospecting, stealing, + crusading, and combat have haunted you in your sleep for many months, + but you aren't sure of the reason. You wonder whether you have in + fact been having those dreams all your life, and somehow managed to + forget about them until now. Some nights you awaken suddenly and cry + out, terrified at the vivid recollection of the strange and powerful + creatures that seem to be lurking behind every corner of the dungeon + in your dream. Could these details haunting your dreams be real? As each night passes, you feel the desire to enter the mysterious caverns near the ruins grow stronger. Each morning, however, you quickly put the idea out of your head as you recall the tales of those who entered - the caverns before you and did not return. Eventually you can resist + the caverns before you and did not return. Eventually you can resist the yearning to seek out the fantastic place in your dreams no longer. After all, when other adventurers came back this way after spending time in the caverns, they usually seemed better off than when they @@ -41,15 +41,15 @@ who did not return had not just kept going? Asking around, you hear about a bauble, called the Amulet of Yen- - dor by some, which, if you can find it, will bring you great wealth. - One legend you were told even mentioned that the one who finds the + dor by some, which, if you can find it, will bring you great wealth. + One legend you were told even mentioned that the one who finds the amulet will be granted immortality by the gods. The amulet is rumored to be somewhere beyond the Valley of Gehennom, deep within the Mazes of Menace. Upon hearing the legends, you immediately realize that there is some profound and undiscovered reason that you are to descend - into the caverns and seek out that amulet of which they spoke. Even - if the rumors of the amulet's powers are untrue, you decide that you - should at least be able to sell the tales of your adventures to the + into the caverns and seek out that amulet of which they spoke. Even + if the rumors of the amulet's powers are untrue, you decide that you + should at least be able to sell the tales of your adventures to the local minstrels for a tidy sum, especially if you encounter any of the terrifying and magical creatures of your dreams along the way. You spend one last night fortifying yourself at the local inn, becoming @@ -70,23 +70,24 @@ - ancient ruins that mark the entrance to the Mazes of Menace. It is - late at night, so you make camp at the entrance and spend the night - sleeping under the open skies. In the morning, you gather your gear, + ancient ruins that mark the entrance to the Mazes of Menace. It is + late at night, so you make camp at the entrance and spend the night + sleeping under the open skies. In the morning, you gather your gear, eat what may be your last meal outside, and enter the dungeon.... 2. What is going on here? - You have just begun a game of NetHack. Your goal is to grab as - much treasure as you can, retrieve the Amulet of Yendor, and escape + You have just begun a game of NetHack. Your goal is to grab as + much treasure as you can, retrieve the Amulet of Yendor, and escape the Mazes of Menace alive. - Your abilities and strengths for dealing with the hazards of + Your abilities and strengths for dealing with the hazards of adventure will vary with your background and training: - Archeologists understand dungeons pretty well; this enables them - to move quickly and sneak up on the local nasties. They start - equipped with the tools for a proper scientific expedition. + Archeologists understand dungeons pretty well; this enables them + to move quickly and sneak up on the local nasties. They start + equipped with the tools for a proper scientific expedition, and are + able to read ancient languages. Barbarians are warriors out of the hinterland, hardened to bat- tle. They begin their quests with naught but uncommon strength, a @@ -102,7 +103,7 @@ them quite reasonable amounts of money, with which they enter the dun- geon. - Knights are distinguished from the common skirmisher by their + Knights are distinguished from the common skirmisher by their devotion to the ideals of chivalry and by the surpassing excellence of their armor. @@ -125,8 +126,7 @@ employ to great advantage. - - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -145,28 +145,28 @@ era. Most monsters don't like being photographed. Valkyries are hardy warrior women. Their upbringing in the harsh - Northlands makes them strong, inures them to extremes of cold, and + Northlands makes them strong, inures them to extremes of cold, and instills in them stealth and cunning. Wizards start out with a knowledge of magic, a selection of magi- cal items, and a particular affinity for dweomercraft. Although seem- - ingly weak and easy to overcome at first sight, an experienced Wizard + ingly weak and easy to overcome at first sight, an experienced Wizard is a deadly foe. - You may also choose the race of your character (within limits; + You may also choose the race of your character (within limits; most roles have restrictions on which races are eligible for them): - Dwarves are smaller than humans or elves, but are stocky and - solid individuals. Dwarves' most notable trait is their great exper- - tise in mining and metalwork. Dwarvish armor is said to be second in + Dwarves are smaller than humans or elves, but are stocky and + solid individuals. Dwarves' most notable trait is their great exper- + tise in mining and metalwork. Dwarvish armor is said to be second in quality not even to the mithril armor of the Elves. - Elves are agile, quick, and perceptive; very little of what goes + Elves are agile, quick, and perceptive; very little of what goes on will escape an Elf. The quality of Elven craftsmanship often gives them an advantage in arms and armor. Gnomes are smaller than but generally similar to dwarves. Gnomes - are known to be expert miners, and it is known that a secret under- + are known to be expert miners, and it is known that a secret under- ground mine complex built by this race exists within the Mazes of Men- ace, filled with both riches and danger. @@ -176,14 +176,14 @@ Orcs are a cruel and barbaric race that hate every living thing (including other orcs). Above all others, Orcs hate Elves with a pas- - sion unequalled, and will go out of their way to kill one at any - opportunity. The armor and weapons fashioned by the Orcs are typi- + sion unequalled, and will go out of their way to kill one at any + opportunity. The armor and weapons fashioned by the Orcs are typi- cally of inferior quality. 3. What do all those things on the screen mean? - On the screen is kept a map of where you have been and what you - have seen on the current dungeon level; as you explore more of the + On the screen is kept a map of where you have been and what you + have seen on the current dungeon level; as you explore more of the level, it appears on the screen in front of you. When NetHack's ancestor rogue first appeared, its screen orienta- @@ -192,7 +192,7 @@ NetHack continues this fine tradition. Unlike text adventure games - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -205,10 +205,10 @@ that accept commands in pseudo-English sentences and explain the results in words, NetHack commands are all one or two keystrokes and the results are displayed graphically on the screen. A minimum screen - size of 24 lines by 80 columns is recommended; if the screen is + size of 24 lines by 80 columns is recommended; if the screen is larger, only a 21x80 section will be used for the map. - NetHack can even be played by blind players, with the assistance + NetHack can even be played by blind players, with the assistance of Braille readers or speech synthesisers. Instructions for configur- ing NetHack for the blind are included later in this document. @@ -216,24 +216,24 @@ authors still find it an entertaining and exciting game despite having won several times. - NetHack offers a variety of display options. The options avail- + NetHack offers a variety of display options. The options avail- able to you will vary from port to port, depending on the capabilities of your hardware and software, and whether various compile-time options were enabled when your executable was created. The three pos- - sible display options are: a monochrome character interface, a color - character interface, and a graphical interface using small pictures - called tiles. The two character interfaces allow fonts with other + sible display options are: a monochrome character interface, a color + character interface, and a graphical interface using small pictures + called tiles. The two character interfaces allow fonts with other characters to be substituted, but the default assignments use standard ASCII characters to represent everything. There is no difference between the various display options with respect to game play. Because we cannot reproduce the tiles or colors in the Guidebook, and because it is common to all ports, we will use the default ASCII char- - acters from the monochrome character display when referring to things + acters from the monochrome character display when referring to things you might see on the screen during your game. - In order to understand what is going on in NetHack, first you - must understand what NetHack is doing with the screen. The NetHack - screen replaces the "You see ..." descriptions of text adventure + In order to understand what is going on in NetHack, first you + must understand what NetHack is doing with the screen. The NetHack + screen replaces the "You see ..." descriptions of text adventure games. Figure 1 is a sample of what a NetHack screen might look like. The way the screen looks for you depends on your platform. @@ -258,7 +258,7 @@ - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -268,21 +268,22 @@ - +---------------------------------------------------------------+ - |The bat bites! | - | | - | ------ | - | |....| ---------- | - | |.<..|####...@...$.| | - | |....-# |...B....+ | - | |....| |.d......| | - | ------ -------|-- | - | | - | | - | | - |Player the Rambler St:12 Dx:7 Co:18 In:11 Wi:9 Ch:15 Neutral | - |Dlvl:1 $:993 HP:9(12) Pw:3(3) AC:10 Exp:1/19 T:752 Hungry Conf | - +---------------------------Figure-1----------------------------+ + +----------------------------------------------------------------+ + | The bat bites! | + | | + | ------ | + | |....| ---------- | + | |.<..|####...@...$.| | + | |....-# |...B....+ | + | |....| |.d......| | + | ------ -------|-- | + | | + | | + | | + | Player the Rambler St:12 Dx:7 Co:18 In:11 Wi:9 Ch:15 Neutral | + | Dlvl:1 $:993 HP:9(12) Pw:3(3) AC:10 Exp:1/19 T:752 Hungry Conf | + +----------------------------------------------------------------+ + Figure 1 @@ -323,8 +324,7 @@ - - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -334,11 +334,12 @@ - +---------------------------------------------------------------+ - |Player the Rambler St:12 Dx:7 Co:18 In:11 Wi:9 Ch:15 | - |Neutral $:993 HP:9(12) Pw:3(3) AC:10 Exp:1/19 Hungry | - |Dlvl:1 T:752 Conf | - +---------------------------Figure-2----------------------------+ + +----------------------------------------------------------------+ + | Player the Rambler St:12 Dx:7 Co:18 In:11 Wi:9 Ch:15 | + | Neutral $:993 HP:9(12) Pw:3(3) AC:10 Exp:1/19 Hungry | + | Dlvl:1 T:752 Conf | + +----------------------------------------------------------------+ + Figure 2 3.1. The status lines (bottom) @@ -346,11 +347,11 @@ cryptic pieces of information describing your current status. Figure 1 shows the traditional two-line status area below the map. Figure 2 shows just the status area, when the statuslines:3 option has been set - (not all interfaces support this option). If any status line becomes - wider than the screen, you might not see all of it due to truncation. - When the numbers grow bigger and multiple conditions are present, the - two-line format will run out of room on the second line, but sta- - tuslines:2 is the default because a basic 24-line terminal isn't tall + (not all interfaces support this option). If any status line becomes + wider than the screen, you might not see all of it due to truncation. + When the numbers grow bigger and multiple conditions are present, the + two-line format will run out of room on the second line, but sta- + tuslines:2 is the default because a basic 24-line terminal isn't tall enough for the third line. Here are explanations of what the various status items mean: @@ -364,19 +365,19 @@ attributes. A human character's attributes can range from 3 to 18 inclusive; non-humans may exceed these limits (occasionally you may get super-strengths of the form 18/xx, and magic can also - cause attributes to exceed the normal limits). The higher your - strength, the stronger you are. Strength affects how success- - fully you perform physical tasks, how much damage you do in com- + cause attributes to exceed the normal limits). The higher your + strength, the stronger you are. Strength affects how success- + fully you perform physical tasks, how much damage you do in com- bat, and how much loot you can carry. Dexterity - Dexterity affects your chances to hit in combat, to avoid traps, + Dexterity affects your chances to hit in combat, to avoid traps, and do other tasks requiring agility or manipulation of objects. Constitution - Constitution affects your ability to recover from injuries and - other strains on your stamina. When strength is low or modest, - constitution also affects how much you can carry. With suffi- + Constitution affects your ability to recover from injuries and + other strains on your stamina. When strength is low or modest, + constitution also affects how much you can carry. With suffi- ciently high strength, the contribution to carrying capacity from your constitution no longer matters. @@ -389,8 +390,7 @@ dealing with magic). It affects your magical energy. - - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -405,17 +405,17 @@ ticular, it can affect the prices shopkeepers offer you. Alignment - Lawful, Neutral, or Chaotic. Often, Lawful is taken as good and - Chaotic as evil, but legal and ethical do not always coincide. - Your alignment influences how other monsters react toward you. - Monsters of a like alignment are more likely to be non-aggres- - sive, while those of an opposing alignment are more likely to be + Lawful, Neutral, or Chaotic. Often, Lawful is taken as good and + Chaotic as evil, but legal and ethical do not always coincide. + Your alignment influences how other monsters react toward you. + Monsters of a like alignment are more likely to be non-aggres- + sive, while those of an opposing alignment are more likely to be seriously offended at your presence. Dungeon Level - How deep you are in the dungeon. You start at level one and the - number increases as you go deeper into the dungeon. Some levels - are special, and are identified by a name and not a number. The + How deep you are in the dungeon. You start at level one and the + number increases as you go deeper into the dungeon. Some levels + are special, and are identified by a name and not a number. The Amulet of Yendor is reputed to be somewhere beneath the twentieth level. @@ -426,12 +426,12 @@ Hit Points Your current and maximum hit points. Hit points indicate how much damage you can take before you die. The more you get hit in - a fight, the lower they get. You can regain hit points by rest- - ing, or by using certain magical items or spells. The number in + a fight, the lower they get. You can regain hit points by rest- + ing, or by using certain magical items or spells. The number in parentheses is the maximum number your hit points can reach. Power - Spell points. This tells you how much mystic energy (mana) you + Spell points. This tells you how much mystic energy (mana) you have available for spell casting. Again, resting will regenerate the amount available. @@ -444,8 +444,8 @@ Experience Your current experience level. If the showexp option is set, it will be followed by a slash and experience points. As you adven- - ture, you gain experience points. At certain experience point - totals, you gain an experience level. The more experienced you + ture, you gain experience points. At certain experience point + totals, you gain an experience level. The more experienced you are, the better you fight and withstand magical attacks. (By the time your level reaches double digits, the usefulness of showing the points with it has dropped significantly. You can use the @@ -456,7 +456,7 @@ The number of turns elapsed so far, displayed if you have the - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -479,14 +479,14 @@ Unencumbered. Fatal conditions: Stone (aka Petrifying, turning to stone), Slime - (turning into green slime), Strngl (being strangled), FoodPois - (suffering from acute food poisoning), TermIll (suffering from a + (turning into green slime), Strngl (being strangled), FoodPois + (suffering from acute food poisoning), TermIll (suffering from a terminal illness). - Non-fatal conditions: Blind (can't see), Deaf (can't hear), Stun + Non-fatal conditions: Blind (can't see), Deaf (can't hear), Stun (stunned), Conf (confused), Hallu (hallucinating). - Movement modifiers: Lev (levitating), Fly (flying), Ride (rid- + Movement modifiers: Lev (levitating), Fly (flying), Ride (rid- ing). Other conditions and modifiers exist, but there isn't enough room @@ -499,19 +499,19 @@ 3.2. The message line (top) The top line of the screen is reserved for messages that describe - things that are impossible to represent visually. If you see a - "--More--" on the top line, this means that NetHack has another mes- - sage to display on the screen, but it wants to make certain that - you've read the one that is there first. To read the next message, + things that are impossible to represent visually. If you see a + "--More--" on the top line, this means that NetHack has another mes- + sage to display on the screen, but it wants to make certain that + you've read the one that is there first. To read the next message, just press the space bar. - To change how and what messages are shown on the message line, + To change how and what messages are shown on the message line, see "Configuring Message Types" and the verbose option. 3.3. The map (rest of the screen) - The rest of the screen is the map of the level as you have - explored it so far. Each symbol on the screen represents something. + The rest of the screen is the map of the level as you have + explored it so far. Each symbol on the screen represents something. You can set various graphics options to change some of the symbols the game uses; otherwise, the game will use default symbols. Here is a list of what the default symbols mean: @@ -522,7 +522,7 @@ - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -548,7 +548,7 @@ < Stairs up: a way to the previous level. - + A closed door, or a spellbook containing a spell you may be able + + A closed door, or a spellbook containing a spell you may be able to learn. @ Your character or a human or an elf. @@ -579,7 +579,7 @@ ` A boulder or statue or an engraving on the floor of a room. - Note: statues are displayed as if they were the monsters they + Note: statues are displayed as if they were the monsters they depict so won't appear as a grave accent (aka back-tick). 0 An iron ball. @@ -588,7 +588,7 @@ - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -615,7 +615,7 @@ vicious. Sometimes, however, they can be helpful. I Rather than a specific type of monster, this marks the last known - location of an invisible or otherwise unseen monster. Note that + location of an invisible or otherwise unseen monster. Note that the monster could have moved. The `s', `F', and `m' commands may be useful here. @@ -632,20 +632,20 @@ Commands can be initiated by typing one or two characters to which the command is bound to, or typing the command name in the extended commands entry. Some commands, like "search", do not require - that any more information be collected by NetHack. Other commands - might require additional information, for example a direction, or an - object to be used. For those commands that require additional infor- + that any more information be collected by NetHack. Other commands + might require additional information, for example a direction, or an + object to be used. For those commands that require additional infor- mation, NetHack will present you with either a menu of choices or with a command line prompt requesting information. Which you are presented with will depend chiefly on how you have set the menustyle option. - For example, a common question, in the form "What do you want to - use? [a-zA-Z ?*]", asks you to choose an object you are carrying. - Here, "a-zA-Z" are the inventory letters of your possible choices. - Typing `?' gives you an inventory list of these items, so you can see - what each letter refers to. In this example, there is also a `*' - indicating that you may choose an object not on the list, if you - wanted to use something unexpected. Typing a `*' lists your entire + For example, a common question, in the form "What do you want to + use? [a-zA-Z ?*]", asks you to choose an object you are carrying. + Here, "a-zA-Z" are the inventory letters of your possible choices. + Typing `?' gives you an inventory list of these items, so you can see + what each letter refers to. In this example, there is also a `*' + indicating that you may choose an object not on the list, if you + wanted to use something unexpected. Typing a `*' lists your entire inventory, so you can see the inventory letters of every object you're carrying. Finally, if you change your mind and decide you don't want to do this command after all, you can press the ESC key to abort the @@ -654,7 +654,7 @@ - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -666,13 +666,13 @@ You can put a number before some commands to repeat them that many times; for example, "10s" will search ten times. If you have the - number_pad option set, you must type `n' to prefix a count, so the - example above would be typed "n10s" instead. Commands for which - counts make no sense ignore them. In addition, movement commands can - be prefixed for greater control (see below). To cancel a count or a + number_pad option set, you must type `n' to prefix a count, so the + example above would be typed "n10s" instead. Commands for which + counts make no sense ignore them. In addition, movement commands can + be prefixed for greater control (see below). To cancel a count or a prefix, press the ESC key. - The list of commands is rather long, but it can be read at any + The list of commands is rather long, but it can be read at any time during the game through the `?' command, which accesses a menu of helpful texts. Here are the default key bindings for your reference: @@ -683,7 +683,7 @@ word) to explain. Specifying a location is done by moving the cursor to a particular spot on the map and then pressing one of `.', `,', `;', or `:'. `.' will explain the symbol at the chosen - location, conditionally check for "More info?" depending upon + location, conditionally check for "More info?" depending upon whether the help option is on, and then you will be asked to pick another location; `,' will explain the symbol but skip any addi- tional information, then let you pick another location; `;' will @@ -695,32 +695,32 @@ If the autodescribe option is on, a short description of what you see at each location is shown as you move the cursor. Typing `#' - while picking a location will toggle that option on or off. The - whatis_coord option controls whether the short description + while picking a location will toggle that option on or off. The + whatis_coord option controls whether the short description includes map coordinates. - Specifying a name rather than a location always gives any addi- + Specifying a name rather than a location always gives any addi- tional information available about that name. - You may also request a description of nearby monsters, all mon- - sters currently displayed, nearby objects, or all objects. The - whatis_coord option controls which format of map coordinate is + You may also request a description of nearby monsters, all mon- + sters currently displayed, nearby objects, or all objects. The + whatis_coord option controls which format of map coordinate is included with their descriptions. & Tell what a command does. - < Go up to the previous level (if you are on a staircase or lad- + < Go up to the previous level (if you are on a staircase or lad- der). > Go down to the next level (if you are on a staircase or ladder). [yuhjklbn] - Go one step in the direction indicated (see Figure 3). If you - sense or remember a monster there, you will fight the monster - instead. Only these one-step movement commands cause you to + Go one step in the direction indicated (see Figure 3). If you + sense or remember a monster there, you will fight the monster + instead. Only these one-step movement commands cause you to - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -731,14 +731,15 @@ fight monsters; the others (below) are "safe." - +----------------------------------------------------------------+ - | y k u 7 8 9 | - | \ | / \ | / | - | h- . -l 4- . -6 | - | / | \ / | \ | - | b j n 1 2 3 | - | (number_pad off) (number_pad on) | - +---------------------------Figure-3-----------------------------+ + +-----------------------------------------------------+ + | y k u 7 8 9 | + | \ | / \ | / | + | h- . -l 4- . -6 | + | / | \ / | \ | + | b j n 1 2 3 | + | (number_pad off) (number_pad on) | + +-----------------------------------------------------+ + Figure 3 [YUHJKLBN] Go in that direction until you hit a wall or run into something. @@ -748,17 +749,17 @@ remember a monster there). A few non-movement commands use the `m' prefix to request operat- - ing via menu (to temporarily override the menustyle:traditional - option). Primarily useful for `,' (pickup) when there is only - one class of objects present (where there won't be any "what - kinds of objects?" prompt, so no opportunity to answer `m' at + ing via menu (to temporarily override the menustyle:traditional + option). Primarily useful for `,' (pickup) when there is only + one class of objects present (where there won't be any "what + kinds of objects?" prompt, so no opportunity to answer `m' at that prompt). The prefix will make "#travel" command show a menu of interesting targets in sight. It can also be used with the `\' (known, show a list of all discovered objects) and the ``' (knownclass, show a - list of discovered objects in a particular class) commands to - offer a menu of several sorting alternatives (which sets a new + list of discovered objects in a particular class) commands to + offer a menu of several sorting alternatives (which sets a new value for the sortdiscoveries option); also for "#vanquished" and "#genocided" commands to offer a sorting menu. @@ -783,10 +784,9 @@ Note: + means holding the or key down like while typing and releasing , then releas- - ing . ^ is used as shorthand elsewhere in the - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -796,6 +796,7 @@ + ing . ^ is used as shorthand elsewhere in the Guidebook to mean the same thing. Control characters are case- insensitive so ^x and ^X are the same. @@ -803,18 +804,18 @@ Old versions supported `M' as a movement prefix which combined the effect of `m' with +. That is no longer supported as a prefix but similar effect can be achieved by using - `m' and G in combination. m can also be used in com- + `m' and G in combination. m can also be used in com- bination with g, +, or +. _ Travel to a map location via a shortest-path algorithm. - The shortest path is computed over map locations the hero knows - about (e.g. seen or previously traversed). If there is no known - path, a guess is made instead. Stops on most of the same condi- - tions as the `G' prefix, but without picking up objects, so - implicitly forces the `m' prefix. For ports with mouse support, - the command is also invoked when a mouse-click takes place on a + The shortest path is computed over map locations the hero knows + about (e.g. seen or previously traversed). If there is no known + path, a guess is made instead. Stops on most of the same condi- + tions as the `G' prefix, but without picking up objects, so + implicitly forces the `m' prefix. For ports with mouse support, + the command is also invoked when a mouse-click takes place on a location other than the current position. . Wait or rest, do nothing for one turn. Precede with the `m' pre- @@ -851,8 +852,7 @@ - - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -892,9 +892,9 @@ specified values (so "!?" means `!' or `?'). If you specify more than one category, an inventory object must meet each of the cat- egory criteria (so "%u" means class `%' and unpaid `u'). Lastly, - you may specify multiple values within multiple categories: - "!?BU" will select all potions and scrolls which are known to be - blessed or uncursed. (In versions prior to 3.6, filter combina- + you may specify multiple values within multiple categories: + "!?BU" will select all potions and scrolls which are known to be + blessed or uncursed. (In versions prior to 3.6, filter combina- tions behaved differently.) ^D Kick something (usually a door). @@ -903,13 +903,13 @@ Normally checks for edible item(s) on the floor, then if none are found or none are chosen, checks for edible item(s) in inventory. - Precede `e' with the `m' prefix to bypass attempting to eat any- + Precede `e' with the `m' prefix to bypass attempting to eat any- thing off the floor. - If you attempt to eat while already satiated, you might choke to - death. If you risk it, you will be asked whether to "continue - eating?" if you survive the first bite. You can set the para- - noid_confirmation:eating option to require a response of yes + If you attempt to eat while already satiated, you might choke to + death. If you risk it, you will be asked whether to "continue + eating?" if you survive the first bite. You can set the para- + noid_confirmation:eating option to require a response of yes instead of just y. E Engrave a message on the floor. @@ -918,7 +918,7 @@ - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -928,7 +928,7 @@ - Engraving the word "Elbereth" will cause most monsters to not + Engraving the word "Elbereth" will cause most monsters to not attack you hand-to-hand (but if you attack, you will rub it out); this is often useful to give yourself a breather. @@ -941,16 +941,16 @@ instead of filling the quiver. This will also automatically use a polearm if wielded. If fireassist is true, firing will auto- matically try to wield a launcher (for example, a bow or a sling) - matching the ammo in the quiver; this might take multiple turns, - and get interrupted by a monster. Remember to swap back to your + matching the ammo in the quiver; this might take multiple turns, + and get interrupted by a monster. Remember to swap back to your main melee weapon afterwards. See also `t' (throw) for more general throwing and shooting. i List your inventory (everything you're carrying). - I List selected parts of your inventory, usually be specifying the - character for a particular set of objects, like `[' for armor or + I List selected parts of your inventory, usually be specifying the + character for a particular set of objects, like `[' for armor or `!' for potions. I* - list all gems in inventory; @@ -967,7 +967,7 @@ O Set options. - A menu showing the current option values will be displayed. You + A menu showing the current option values will be displayed. You can change most values simply by selecting the menu entry for the given option (ie, by typing its letter or clicking upon it, depending on your user interface). For the non-boolean choices, @@ -984,7 +984,7 @@ - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -1005,17 +1005,17 @@ This command may also be used to wear armor. The prompt for which inventory item to use will only list accessories, but choosing an unlisted item of armor will attempt to wear it. (See - the `W' command below. It lists armor as the inventory choices + the `W' command below. It lists armor as the inventory choices but will accept an accessory and attempt to put that on.) ^P Repeat previous message. - Subsequent `^P's repeat earlier messages. For some interfaces, + Subsequent `^P's repeat earlier messages. For some interfaces, the behavior can be varied via the msg_window option. q Quaff (drink) something (potion, water, etc). - When there is a fountain or sink present, it asks whether to + When there is a fountain or sink present, it asks whether to drink from that. If that is declined, then it offers a chance to choose a potion from inventory. Precede `q' with the `m' prefix to skip asking about drinking from a fountain or sink. @@ -1046,11 +1046,11 @@ search for a turn even next to a hostile monster, if safe_wait is on. - Can also be used to figure out whether there is still a monster + Can also be used to figure out whether there is still a monster at an adjacent "remembered, unseen monster" marker. - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -1060,18 +1060,18 @@ - S Save the game (which suspends play and exits the program). The - saved game will be restored automatically the next time you play + S Save the game (which suspends play and exits the program). The + saved game will be restored automatically the next time you play using the same character name. - In normal play, once a saved game is restored the file used to - hold the saved data is deleted. In explore mode, once restora- - tion is accomplished you are asked whether to keep or delete the - file. Keeping the file makes it feasible to play for a while + In normal play, once a saved game is restored the file used to + hold the saved data is deleted. In explore mode, once restora- + tion is accomplished you are asked whether to keep or delete the + file. Keeping the file makes it feasible to play for a while then quit without saving and later restore again. - There is no "save current game state and keep playing" command, - not even in explore mode where saved game files can be kept and + There is no "save current game state and keep playing" command, + not even in explore mode where saved game files can be kept and re-used. t Throw an object or shoot a projectile. @@ -1087,14 +1087,14 @@ T Take off armor. - If you're wearing more than one piece, you'll be prompted for + If you're wearing more than one piece, you'll be prompted for which one to take off. (Note that this treats a cloak covering a suit and/or a shirt, or a suit covering a shirt, as if the under- - lying items weren't there.) When you're only wearing one, then - by default it will be taken off without asking, but you can set + lying items weren't there.) When you're only wearing one, then + by default it will be taken off without asking, but you can set the paranoid_confirmation:Remove option to require a prompt. - This command may also be used to remove accessories. The prompt + This command may also be used to remove accessories. The prompt for which inventory item to take off only lists worn armor, but a worn accessory can be chosen. (See the `R' command above. It lists accessories as the inventory choices but will accept an @@ -1116,7 +1116,7 @@ - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -1131,11 +1131,11 @@ This command may also be used to put on an accessory (ring, amulet, or blindfold). The prompt for which inventory item to use will only list armor, but choosing an unlisted accessory will - attempt to put it on. (See the `P' command above. It lists - accessories as the inventory choices but will accept an item of + attempt to put it on. (See the `P' command above. It lists + accessories as the inventory choices but will accept an item of armor and attempt to wear it.) - x Exchange your wielded weapon with the item in your alternate + x Exchange your wielded weapon with the item in your alternate weapon slot. The latter is used as your secondary weapon when engaging in two- @@ -1151,11 +1151,11 @@ ^X Display basic information about your character. - Displays name, role, race, gender (unless role name makes that - redundant, such as Caveman or Priestess), and alignment, along - with your patron deity and his or her opposition. It also shows - most of the various items of information from the status line(s) - in a less terse form, including several additional things which + Displays name, role, race, gender (unless role name makes that + redundant, such as Caveman or Priestess), and alignment, along + with your patron deity and his or her opposition. It also shows + most of the various items of information from the status line(s) + in a less terse form, including several additional things which don't appear in the normal status display due to space considera- tions. @@ -1171,7 +1171,7 @@ Z. - to cast at yourself, use `.' for the direction. - ^Z Suspend the game (UNIX(R) versions with job control only). See + ^Z Suspend the game (UNIX(R) versions with job control only). See "#suspend" below for more details. : Look at what is here. @@ -1182,7 +1182,7 @@ (R)UNIX is a registered trademark of The Open Group. - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -1248,7 +1248,7 @@ menu_next_page, and menu_last_page keys (`^', `<', `>', `|' by - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -1272,10 +1272,10 @@ on others. It is sometimes displayed as ^? even though that is not an actual control character. - Many terminals have an option to swap the and - keys, so typing the key might not execute this - command. If that happens, you can use the extended command - "#terrain" instead. + Many terminals have an option to swap the and keys, so typing the key might not execute this com- + mand. If that happens, you can use the extended command "#ter- + rain" instead. # Perform an extended command. @@ -1283,20 +1283,20 @@ As you can see, the authors of NetHack used up all the letters, so this is a way to introduce the less frequently used commands. What - extended commands are available depends on what features the game was + extended commands are available depends on what features the game was compiled with. #adjust - Adjust inventory letters (most useful when the fixinv option is + Adjust inventory letters (most useful when the fixinv option is "on"). Autocompletes. Default key is `M-a'. - This command allows you to move an item from one particular - inventory slot to another so that it has a letter which is more - meaningful for you or that it will appear in a particular loca- - tion when inventory listings are displayed. You can move to a - currently empty slot, or if the destination is occupied--and - won't merge--the item there will swap slots with the one being - moved. "#adjust" can also be used to split a stack of objects; + This command allows you to move an item from one particular + inventory slot to another so that it has a letter which is more + meaningful for you or that it will appear in a particular loca- + tion when inventory listings are displayed. You can move to a + currently empty slot, or if the destination is occupied--and + won't merge--the item there will swap slots with the one being + moved. "#adjust" can also be used to split a stack of objects; when choosing the item to adjust, enter a count prior to its let- ter. @@ -1304,17 +1304,17 @@ when moving to the destination. That behavior has been changed; to gather compatible stacks, "#adjust" a stack into its own inventory slot. If it has a name assigned, other stacks with the - same name or with no name will merge provided that all their - other attributes match. If it does not have a name, only other + same name or with no name will merge provided that all their + other attributes match. If it does not have a name, only other stacks with no name are eligible. In either case, otherwise com- patible stacks with a different name will not be merged. This contrasts with using "#adjust" to move from one slot to a differ- - ent slot. In that situation, moving (no count given) a compati- - ble stack will merge if either stack has a name when the other - doesn't and give that name to the result, while splitting (count + ent slot. In that situation, moving (no count given) a compati- + ble stack will merge if either stack has a name when the other + doesn't and give that name to the result, while splitting (count - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -1324,13 +1324,13 @@ - given) will ignore the source stack's name when deciding whether + given) will ignore the source stack's name when deciding whether to merge with the destination stack. #annotate Allows you to specify one line of text to associate with the cur- rent dungeon level. All levels with annotations are displayed by - the "#overview" command. Autocompletes. Default key is `M-A', + the "#overview" command. Autocompletes. Default key is `M-A', and also `^N' if number_pad is on. #apply @@ -1340,7 +1340,7 @@ If the tool used acts on items on the floor, using the `m' prefix skips those items. - If used on a wand, that wand will be broken, releasing its magic + If used on a wand, that wand will be broken, releasing its magic in the process. Confirmation is required. #attributes @@ -1350,14 +1350,14 @@ Toggle the autopickup option on/off. Default key is `@'. #bugreport - Bring up a browser window to submit a report to the NetHack - Development Team. Can be disabled at the time the program is - built; when enabled, CRASHREPORTURL must be set in the system + Bring up a browser window to submit a report to the NetHack + Development Team. Can be disabled at the time the program is + built; when enabled, CRASHREPORTURL must be set in the system configuration file. #call - Call (name) a monster, or an object in inventory, on the floor, - or in the discoveries list, or add an annotation for the current + Call (name) a monster, or an object in inventory, on the floor, + or in the discoveries list, or add an annotation for the current level (same as "#annotate"). Default key is `C'. #cast @@ -1373,14 +1373,14 @@ Close a door. Default key is `c'. #conduct - List voluntary challenges you have maintained. Autocompletes. + List voluntary challenges you have maintained. Autocompletes. Default key is `M-C'. See the section below entitled "Conduct" for details. - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -1424,8 +1424,8 @@ Switch from normal play to non-scoring explore mode. Default key is `M-X'. - Requires confirmation; default response is n (no). To really - switch to explore mode, respond with y. You can set the para- + Requires confirmation; default response is n (no). To really + switch to explore mode, respond with y. You can set the para- noid_confirmation:quit option to require a response of yes instead. @@ -1441,12 +1441,12 @@ Force a lock. Autocompletes. Default key is `M-f'. #genocided - List any monster types which have been genocided. In explore - mode and debug mode it also shows types which have become + List any monster types which have been genocided. In explore + mode and debug mode it also shows types which have become extinct. - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -1473,16 +1473,16 @@ is `;'. #help - Show the help menu. Default key is `?', and also `h' if num- + Show the help menu. Default key is `?', and also `h' if num- ber_pad is on. #herecmdmenu - Show a menu of possible actions directed at your current loca- - tion. The menu is limited to a subset of the likeliest actions, + Show a menu of possible actions directed at your current loca- + tion. The menu is limited to a subset of the likeliest actions, not an exhaustive set of all possibilities. Autocompletes. - If mouse support is enabled and the herecmd_menu option is On, - clicking on the hero (or steed when mounted) will execute this + If mouse support is enabled and the herecmd_menu option is On, + clicking on the hero (or steed when mounted) will execute this command. #history @@ -1495,15 +1495,15 @@ Inventory specific item types. Default key is `I'. #invoke - Invoke an object's special powers. Autocompletes. Default key + Invoke an object's special powers. Autocompletes. Default key is `M-i'. #jump - Jump to another location. Autocompletes. Default key is `M-j', + Jump to another location. Autocompletes. Default key is `M-j', and also `j' if number_pad is on. #kick - Kick something. Default key is `^D', and `k' if number_pad is + Kick something. Default key is `^D', and `k' if number_pad is on. #known @@ -1512,7 +1512,7 @@ - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -1522,7 +1522,7 @@ - The `m' prefix allows assigning a new value to the sortdiscover- + The `m' prefix allows assigning a new value to the sortdiscover- ies option to control the order in which the discoveries are dis- played. @@ -1547,16 +1547,16 @@ #loot Loot a box or bag on the floor beneath you, or the saddle from a steed standing next to you. Autocompletes. Precede with the `m' - prefix to skip containers at your location and go directly to - removing a saddle. Default key is `M-l', and also `l' if num- + prefix to skip containers at your location and go directly to + removing a saddle. Default key is `M-l', and also `l' if num- ber_pad is on. #monster - Use a monster's special ability (when polymorphed into monster + Use a monster's special ability (when polymorphed into monster form). Autocompletes. Default key is `M-m'. #name - Name a monster, an individual object, or a type of object. Same + Name a monster, an individual object, or a type of object. Same as "#call". Autocompletes. Default keys are `N', `M-n', and `M- N'. @@ -1578,7 +1578,7 @@ - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -1622,10 +1622,10 @@ Pay your shopping bill. Default key is `p'. #perminv - If persistent inventory display is supported and enabled (with - the perm_invent option), interact with it instead of with the - map. You'll be prompted for menu scrolling keystrokes such as - `>' and `<'. Press Return or Escape to resume normal play. + If persistent inventory display is supported and enabled (with + the perm_invent option), interact with it instead of with the + map. You'll be prompted for menu scrolling keystrokes such as + `>' and `<'. Press Return or Escape to resume normal play. Default key is `|'. #pickup @@ -1644,7 +1644,7 @@ right away.) Since using this command by accident can cause - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -1710,7 +1710,7 @@ Default key is `M-R'. - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -1759,24 +1759,24 @@ #seearmor Show the armor currently worn. Default key is `['. - Will display worn armor in a menu even when there is only thing + Will display worn armor in a menu even when there is only thing worn. #seerings Show the ring(s) currently worn. Default key is `='. - Will display worn rings in a menu if there are two (or there is - just one and is a meat ring rather than a "real" ring). Use the + Will display worn rings in a menu if there are two (or there is + just one and is a meat ring rather than a "real" ring). Use the `m' prefix to force a menu for one ring. #seetools Show the tools currently in use. Default key is `('. - Will display the result in a message if there is one tool in use + Will display the result in a message if there is one tool in use (worn blindfold or towel or lenses, lit lamp(s) and/or candle(s), - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -1792,8 +1792,8 @@ #seeweapon Show the weapon currently wielded. Default key is `)'. - If dual-wielding, a separate message about the secondary weapon - will be given. Using the `m' prefix will force a menu and it + If dual-wielding, a separate message about the secondary weapon + will be given. Using the `m' prefix will force a menu and it will include primary weapon, alternate weapon even when not dual- wielding, and also whatever is currently assigned to the quiver slot. @@ -1802,22 +1802,22 @@ Do a shell escape, switching from NetHack to a subprocess. Can be disabled at the time the program is built. When enabled, access for specific users can be controlled by the system config- - uration file. Use the shell command `exit' to return to the + uration file. Use the shell command `exit' to return to the game. Default key is `!'. #showgold - Report the gold in your inventory, including gold you know about - in containers you're carrying. If you are inside a shop, report + Report the gold in your inventory, including gold you know about + in containers you're carrying. If you are inside a shop, report any credit or debt you have in that shop. Default key is `$'. #showspells List and reorder known spells. Default key is `+'. #showtrap - Describe an adjacent trap, possibly covered by objects or a mon- + Describe an adjacent trap, possibly covered by objects or a mon- ster. To be eligible, the trap must already be discovered. (The "#terrain" command can display your map with all objects and mon- - sters temporarily removed, making it possible to see all discov- + sters temporarily removed, making it possible to see all discov- ered traps.) Default key is `^'. #sit @@ -1827,10 +1827,10 @@ Show memory usage statistics. Autocompletes. Debug mode only. #suspend - Suspend the game, switching from NetHack to the terminal it was - started from without performing save-and-exit. Can be disabled - at the time the program is built. When enabled, mainly useful - for tty and curses interfaces on UNIX. Use the shell command + Suspend the game, switching from NetHack to the terminal it was + started from without performing save-and-exit. Can be disabled + at the time the program is built. When enabled, mainly useful + for tty and curses interfaces on UNIX. Use the shell command `fg' to return to the game. Default key is `^Z'. #swap @@ -1842,7 +1842,7 @@ - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -1859,9 +1859,9 @@ Teleport around the level. Default key is `^T'. #terrain - Show map without obstructions. In normal play you can view the - explored portion of the current level's map without monsters; - without monsters and objects; or without monsters, objects, and + Show map without obstructions. In normal play you can view the + explored portion of the current level's map without monsters; + without monsters and objects; or without monsters, objects, and traps. If there are visible clouds of gas in view, they are treated like @@ -1888,9 +1888,9 @@ #tip Tip over a container (bag or box) to pour out its contents. When - there are containers on the floor, the game will prompt to pick - one of them or "tip something being carried". If the latter is - chosen, there will be another prompt for which item from inven- + there are containers on the floor, the game will prompt to pick + one of them or "tip something being carried". If the latter is + chosen, there will be another prompt for which item from inven- tory to tip. The `m' prefix makes the command skip containers on the floor and @@ -1908,7 +1908,7 @@ line will show "(no travel path)" if your character does not know - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -1924,17 +1924,17 @@ Turn undead away. Autocompletes. Default key is `M-t'. #twoweapon - Toggle two-weapon combat on or off. Autocompletes. Default key + Toggle two-weapon combat on or off. Autocompletes. Default key is `X', and also `M-2' if number_pad is off. - Note that you must use suitable weapons for this type of combat, + Note that you must use suitable weapons for this type of combat, or it will be automatically turned off. #untrap - Untrap something (trap, door, or chest). Default key is `M-u', + Untrap something (trap, door, or chest). Default key is `M-u', and `u' if number_pad is on. - In some circumstances it can also be used to rescue trapped mon- + In some circumstances it can also be used to rescue trapped mon- sters. #up @@ -1943,16 +1943,16 @@ #vanquished List vanquished monsters by type and count. - Note that the vanquished monsters list includes all monsters - killed by traps and each other as well as by you, and omits any - which got removed from the game without being killed (perhaps by - genocide, or by a mollified shopkeeper dismissing summoned Kops) + Note that the vanquished monsters list includes all monsters + killed by traps and each other as well as by you, and omits any + which got removed from the game without being killed (perhaps by + genocide, or by a mollified shopkeeper dismissing summoned Kops) or were already corpses when placed on the map. - Using the "request menu" prefix prior to #vanquished brings up a - menu of sorting orders available (provided that the vanquished - monsters list contains at least two types of monsters). Which- - ever ordering is picked gets assigned to the sortvanquished + Using the "request menu" prefix prior to #vanquished brings up a + menu of sorting orders available (provided that the vanquished + monsters list contains at least two types of monsters). + Whichever ordering is picked gets assigned to the sortvanquished option so is remembered for subsequent #vanquished requests. The "#genocided" command shares this sorting order. @@ -1967,14 +1967,14 @@ The second paragraph lists the user interface(s) that are included. If there are more than one, you can use the windowtype - option in your run-time configuration file to select the one you + option in your run-time configuration file to select the one you want. Autocompletes. Default key is `M-v'. - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -1985,15 +1985,15 @@ #versionshort - Show the program's version number, plus the date and time that - the running copy was built from sources (not the version's + Show the program's version number, plus the date and time that + the running copy was built from sources (not the version's release date). Default key is `v'. #vision Show vision array. Autocompletes. Debug mode only. #wait - Rest one move while doing nothing. Default key is `.', and also + Rest one move while doing nothing. Default key is `.', and also ` ' if rest_on_space is on. #wear @@ -2003,7 +2003,7 @@ Tell what a key does. Default key is `&'. #whatis - Show what type of thing a symbol corresponds to. Default key is + Show what type of thing a symbol corresponds to. Default key is `/'. #wield @@ -2013,19 +2013,19 @@ Wipe off your face. Autocompletes. Default key is `M-w'. #wizborn - Show monster birth, death, genocide, and extinct statistics. + Show monster birth, death, genocide, and extinct statistics. Debug mode only. #wizbury - Bury objects under and around you. Autocompletes. Debug mode + Bury objects under and around you. Autocompletes. Debug mode only. #wizcast Cast any spell. Debug mode only. #wizdetect - Reveal hidden things (secret doors or traps or unseen monsters) - within a modest radius. No time elapses. Autocompletes. Debug + Reveal hidden things (secret doors or traps or unseen monsters) + within a modest radius. No time elapses. Autocompletes. Debug mode only. Default key is `^E'. #wizgenesis @@ -2040,7 +2040,7 @@ Set one or more intrinsic attributes. Autocompletes. Debug mode - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -2053,41 +2053,41 @@ only. #wizkill - Remove monsters from play by just pointing at them. By default - the hero gets credit or blame for killing the targets. Precede - this command with the `m' prefix to override that. Autocom- + Remove monsters from play by just pointing at them. By default + the hero gets credit or blame for killing the targets. Precede + this command with the `m' prefix to override that. Autocom- pletes. Debug mode only. #wizlevelport - Teleport to another level. Autocompletes. Debug mode only. + Teleport to another level. Autocompletes. Debug mode only. Default key is `^V'. #wizmap - Map the level. Autocompletes. Debug mode only. Default key is + Map the level. Autocompletes. Debug mode only. Default key is `^F'. #wizrumorcheck - Verify rumor boundaries by displaying first and last true rumors + Verify rumor boundaries by displaying first and last true rumors and first and last false rumors. - Also displays first, second, and last random engravings, epi- + Also displays first, second, and last random engravings, epi- taphs, and hallucinatory monsters. Autocompletes. Debug mode only. #wizseenv - Show map locations' seen vectors. Autocompletes. Debug mode + Show map locations' seen vectors. Autocompletes. Debug mode only. #wizsmell Smell monster. Autocompletes. Debug mode only. #wizwhere - Show locations of special levels. Autocompletes. Debug mode + Show locations of special levels. Autocompletes. Debug mode only. #wizwish - Wish for something. Autocompletes. Debug mode only. Default + Wish for something. Autocompletes. Debug mode only. Default key is `^W'. #wmode @@ -2101,12 +2101,12 @@ - If your keyboard has a meta key (which, when pressed in combina- - tion with another key, modifies it by setting the "meta" [8th, or - "high"] bit), you can invoke many extended commands by meta-ing the + If your keyboard has a meta key (which, when pressed in combina- + tion with another key, modifies it by setting the "meta" [8th, or + "high"] bit), you can invoke many extended commands by meta-ing the - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -2172,7 +2172,7 @@ - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -2238,7 +2238,7 @@ - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -2304,7 +2304,7 @@ - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -2370,7 +2370,7 @@ them). Some monsters who can open doors can also use unlocking tools. - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -2382,23 +2382,23 @@ And some (giants) can smash doors. - Secret doors are hidden and appear to be ordinary wall (from - inside a room) or solid rock (from outside). You can find them with - the `s' (search) command but it might take multiple tries (possibly - many tries if your luck is poor). Once found they are in all ways - equivalent to normal doors. Mapping magic does not reveal secret + Secret doors are hidden and appear to be ordinary wall (from + inside a room) or solid rock (from outside). You can find them with + the `s' (search) command but it might take multiple tries (possibly + many tries if your luck is poor). Once found they are in all ways + equivalent to normal doors. Mapping magic does not reveal secret doors. 5.2. Traps (`^') - There are traps throughout the dungeon to snare the unwary - intruder. For example, you may suddenly fall into a pit and be stuck + There are traps throughout the dungeon to snare the unwary + intruder. For example, you may suddenly fall into a pit and be stuck for a few turns trying to climb out (see below). A trap usually won't appear on your map until you trigger it by moving onto it, you see someone else trigger it, or you discover it with the `s' (search) com- - mand (multiple attempts are often needed; if your luck is poor, many - attempts might be needed). Wands of secret door detection and spell - of detect unseen also reveal traps within a modest radius but only if + mand (multiple attempts are often needed; if your luck is poor, many + attempts might be needed). Wands of secret door detection and spell + of detect unseen also reveal traps within a modest radius but only if the trap is also within line-of-sight (whether you can see at the time or not). There is also other magic which can reveal traps. @@ -2408,23 +2408,23 @@ moving onto a trap which is shown on your map if they have encountered that type of trap before. - Some traps such as pits, bear traps, and webs hold you in one - place. You can escape by simply trying to move to an adjacent spot + Some traps such as pits, bear traps, and webs hold you in one + place. You can escape by simply trying to move to an adjacent spot and repeat as needed; eventually you will get free. - Other traps can send you to different locations. Teleporters - send you elsewhere on the same dungeon level. Level teleporters send - you to a random dungeon level, the destination chosen from a few lev- - els lower all the way to the top. These traps choose a new destina- - tion each time they're activated. Trap doors and holes also send you - to another level, but one which is always below the current level. - Usually that will be the next level down but it can be farther. + Other traps can send you to different locations. Teleporters + send you elsewhere on the same dungeon level. Level teleporters send + you to a random dungeon level, the destination chosen from a few lev- + els lower all the way to the top. These traps choose a new destina- + tion each time they're activated. Trap doors and holes also send you + to another level, but one which is always below the current level. + Usually that will be the next level down but it can be farther. Unlike (level) teleporters, the destination level of a particular trap door or hole is persistent, so falling into one will bring you to the same level each time--though not necessarily the same spot on the level. Magic portals behave similarly, but with some additional vari- - ation. Some portals are two-way and their remote destination is - always the same: another portal which can take you back. Others are + ation. Some portals are two-way and their remote destination is + always the same: another portal which can take you back. Others are one-way and send you to a specific destination level but not necessar- ily to a specific location there. @@ -2433,10 +2433,10 @@ game, you operate as a warehouse worker who pushes crates around obstacles to position them at designated locations. In NetHack, the goal is to push boulders into pits or holes until those traps have all - been nullified, giving access to whatever is beyond them. In the + been nullified, giving access to whatever is beyond them. In the - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -2446,25 +2446,25 @@ - Sokoban game, you can only move in the four cardinal compass direc- - tions, and a crate in its final destination blocks further access to - that spot. In the Sokoban levels of NetHack, you can move diagonally - (unless that would let you pass between two neighboring boulders) but - you can only push boulders in the four cardinal directions, and a - boulder which fills a pit or hole removes both the boulder and the - trap so opens up normal access to that spot. With careful foresight, - it is possible to complete all of the levels according to the tradi- - tional rules of Sokoban. (Hint: to solve Sokoban puzzles, you often - need to move things away from their eventual destinations in order to - open up more room to maneuver.) Since NetHack does not support an - undo capability, some allowances are permitted in case you get stuck. - For example, each level has at least one extra boulder. Also, it is - possible to drop everything in order to be able to squeeze into the - same location as a boulder (and then presumably move past it), or to - destroy a boulder with magic or tools, or to create new boulders with - a scroll of earth. However, doing such things will lower your luck - without any specific message given about that. See the Conduct sec- - tion for information about getting feedback for your actions in + Sokoban game, you can only move in the four cardinal compass direc- + tions, and a crate in its final destination blocks further access to + that spot. In the Sokoban levels of NetHack, you can move diagonally + (unless that would let you pass between two neighboring boulders) but + you can only push boulders in the four cardinal directions, and a + boulder which fills a pit or hole removes both the boulder and the + trap so opens up normal access to that spot. With careful foresight, + it is possible to complete all of the levels according to the tradi- + tional rules of Sokoban. (Hint: to solve Sokoban puzzles, you often + need to move things away from their eventual destinations in order to + open up more room to maneuver.) Since NetHack does not support an + undo capability, some allowances are permitted in case you get stuck. + For example, each level has at least one extra boulder. Also, it is + possible to drop everything in order to be able to squeeze into the + same location as a boulder (and then presumably move past it), or to + destroy a boulder with magic or tools, or to create new boulders with + a scroll of earth. However, doing such things will lower your luck + without any specific message given about that. See the Conduct sec- + tion for information about getting feedback for your actions in Sokoban. 5.3. Stairs and ladders (`<', `>') @@ -2475,18 +2475,18 @@ early in the dungeon you will find a level with two down staircases, one continuing into the dungeon and the other branching into an area known as the Gnomish Mines. Those mines eventually hit a dead end, so - after exploring them (if you choose to do so), you'll need to climb + after exploring them (if you choose to do so), you'll need to climb back up to the main dungeon. - When you traverse a set of stairs, or trigger a trap which sends + When you traverse a set of stairs, or trigger a trap which sends you to another level, the level you're leaving will be deactivated and stored in a file on disk. If you're moving to a previously visited level, it will be loaded from its file on disk and reactivated. If you're moving to a level which has not yet been visited, it will be created (from scratch for most random levels, from a template for some - "special" levels, or loaded from the remains of an earlier game for a - "bones" level as briefly described below). Monsters are only active - on the current level; those on other levels are essentially placed + "special" levels, or loaded from the remains of an earlier game for a + "bones" level as briefly described below). Monsters are only active + on the current level; those on other levels are essentially placed into stasis. Ordinarily when you climb a set of stairs, you will arrive on the @@ -2502,7 +2502,7 @@ play. - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -2517,33 +2517,33 @@ Occasionally you will run across a room with a shopkeeper near the door and many items lying on the floor. You can buy items by picking them up and then using the `p' command. You can inquire about - the price of an item prior to picking it up by using the "#chat" com- - mand while standing on it. Using an item prior to paying for it will - incur a charge, and the shopkeeper won't allow you to leave the shop + the price of an item prior to picking it up by using the "#chat" com- + mand while standing on it. Using an item prior to paying for it will + incur a charge, and the shopkeeper won't allow you to leave the shop until you have paid any debt you owe. - You can sell items to a shopkeeper by dropping them to the floor + You can sell items to a shopkeeper by dropping them to the floor while inside a shop. You will either be offered an amount of gold and asked whether you're willing to sell, or you'll be told that the shop- - keeper isn't interested (generally, your item needs to be compatible + keeper isn't interested (generally, your item needs to be compatible with the type of merchandise carried by the shop). - If you drop something in a shop by accident, the shopkeeper will - usually claim ownership without offering any compensation. You'll + If you drop something in a shop by accident, the shopkeeper will + usually claim ownership without offering any compensation. You'll have to buy it back if you want to reclaim it. Shopkeepers sometime run out of money. When that happens, you'll be offered credit instead of gold when you try to sell something. Credit can be used to pay for purchases, but it is only good in the shop where it was obtained; other shopkeepers won't honor it. (If you - happen to find a "credit card" in the dungeon, don't bother trying to + happen to find a "credit card" in the dungeon, don't bother trying to use it in shops; shopkeepers will not accept it.) - The `$' command, which reports the amount of gold you are carry- - ing, will also show current shop debt or credit, if any. The "Iu" - command lists unpaid items (those which still belong to the shop) if - you are carrying any. The "Ix" command shows an inventory-like dis- - play of any unpaid items which have been used up, along with other + The `$' command, which reports the amount of gold you are carry- + ing, will also show current shop debt or credit, if any. The "Iu" + command lists unpaid items (those which still belong to the shop) if + you are carrying any. The "Ix" command shows an inventory-like dis- + play of any unpaid items which have been used up, along with other shop fees, if any. 5.4.1. Shop idiosyncrasies @@ -2552,23 +2552,23 @@ * The price of a given item can vary due to a variety of factors. - * A shopkeeper treats the spot immediately inside the door as if it + * A shopkeeper treats the spot immediately inside the door as if it were outside the shop. - * While the shopkeeper watches you like a hawk, he or she will gener- + * While the shopkeeper watches you like a hawk, he or she will gener- ally ignore any other customers. - * If a shop is "closed for inventory," it will not open of its own + * If a shop is "closed for inventory," it will not open of its own accord. - * Shops do not get restocked with new items, regardless of inventory + * Shops do not get restocked with new items, regardless of inventory depletion. - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -2580,25 +2580,25 @@ 5.5. Movement feedback - Moving around the map usually provides no feedback--other than - drawing the hero at the new location--unless you step on an object or - pile of objects, or on a trap, or attempt to move onto a spot where a - monster is located. There are several options which can be used to + Moving around the map usually provides no feedback--other than + drawing the hero at the new location--unless you step on an object or + pile of objects, or on a trap, or attempt to move onto a spot where a + monster is located. There are several options which can be used to augment the normal feedback. - The pile_limit option controls how many objects can be in a - pile--sharing the same map location--for the game to state "there are - objects here" instead of listing them. The default is 5. Setting it - to 1 would always give that message instead of listing any objects. - Setting it to 0 is a special case which will always list all objects + The pile_limit option controls how many objects can be in a + pile--sharing the same map location--for the game to state "there are + objects here" instead of listing them. The default is 5. Setting it + to 1 would always give that message instead of listing any objects. + Setting it to 0 is a special case which will always list all objects no matter how big a pile is. Note that the number refers to the count of separate stacks of objects present rather than the sum of the quan- - tities of those stacks (so 7 arrows or 25 gold pieces will each count - as 1 rather than as 7 and 25, respectively, and total to 2 when both + tities of those stacks (so 7 arrows or 25 gold pieces will each count + as 1 rather than as 7 and 25, respectively, and total to 2 when both are at the same location). - The "nopickup" command prefix (default `m') can be used before a - movement direction to step on objects without attempting auto-pickup + The "nopickup" command prefix (default `m') can be used before a + movement direction to step on objects without attempting auto-pickup and without giving feedback about them. The mention_walls option controls whether you get feedback if you @@ -2615,18 +2615,18 @@ this option to true will describe such things even when they aren't obscured. Doorless doorways and open doors aren't considered worthy of mention; closed doors (if you can move onto their spots) and broken - doors are. Assuming that you're able to do so, moving onto water or - lava or ice will give feedback if not yet on that type of terrain but - not repeat it (unless there has been some intervening message) when - moving from water to another water spot, or lava to lava, or ice to - ice. Moving off of any of those back onto "normal" terrain will give - one message too, unless there is feedback about one or more objects, + doors are. Assuming that you're able to do so, moving onto water or + lava or ice will give feedback if not yet on that type of terrain but + not repeat it (unless there has been some intervening message) when + moving from water to another water spot, or lava to lava, or ice to + ice. Moving off of any of those back onto "normal" terrain will give + one message too, unless there is feedback about one or more objects, in which case the back on land circumstance is implied. - The confirm and safe_pet options control what happens when you + The confirm and safe_pet options control what happens when you try to move onto a peaceful monster's spot or a tame one's spot. - The "nopickup" command prefix (default `m') is also the move- + The "nopickup" command prefix (default `m') is also the move- without-attacking prefix and can be used to try to step onto a visible monster's spot without the move being considered an attack (see the Fighting subsection of Monsters below). The "fight" command prefix @@ -2634,7 +2634,7 @@ attack, when guessing where an unseen monster is or when deliberately - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -2669,13 +2669,13 @@ 6. Monsters Monsters you cannot see are not displayed on the screen. Beware! - You may suddenly come upon one in a dark place. Some magic items can - help you locate them before they locate you (which some monsters can + You may suddenly come upon one in a dark place. Some magic items can + help you locate them before they locate you (which some monsters can do very well). - The commands `/' and `;' may be used to obtain information about - those monsters who are displayed on the screen. The command "#name" - (by default bound to `C'), allows you to assign a name to a monster, + The commands `/' and `;' may be used to obtain information about + those monsters who are displayed on the screen. The command "#name" + (by default bound to `C'), allows you to assign a name to a monster, which may be useful to help distinguish one from another when multiple monsters are present. Assigning a name which is just a space will remove any prior name. @@ -2693,14 +2693,14 @@ unless you attack them. Some of them are very dangerous when angered. Remember: discretion is the better part of valor. - In most circumstances, if you attempt to attack a peaceful mon- - ster by moving into its location, you'll be asked to confirm your - intent. By default an answer of `y' acknowledges that intent, which + In most circumstances, if you attempt to attack a peaceful mon- + ster by moving into its location, you'll be asked to confirm your + intent. By default an answer of `y' acknowledges that intent, which can be error prone if you're using `y' to move. You can set the para- noid_confirmation:attack option to require a response of "yes" - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -2714,11 +2714,11 @@ If you can't see a monster (if it is invisible, or if you are blinded), the symbol `I' will be shown when you learn of its presence. - If you attempt to walk into it, you will try to fight it just like a - monster that you can see; of course, if the monster has moved, you - will attack empty air. If you guess that the monster has moved and - you don't wish to fight, you can use the `m' command to move without - fighting; likewise, if you don't remember a monster but want to try + If you attempt to walk into it, you will try to fight it just like a + monster that you can see; of course, if the monster has moved, you + will attack empty air. If you guess that the monster has moved and + you don't wish to fight, you can use the `m' command to move without + fighting; likewise, if you don't remember a monster but want to try fighting anyway, you can use the `F' command. 6.2. Your pet @@ -2728,15 +2728,15 @@ you. Like you, your pet needs food to survive. Dogs and cats usually feed themselves on fresh carrion and other meats; horses need vegetar- ian food which is harder to come by. If you're worried about your pet - or want to train it, you can feed it, too, by throwing it food. A + or want to train it, you can feed it, too, by throwing it food. A properly trained pet can be very useful under certain circumstances. - Your pet also gains experience from killing monsters, and can - grow over time, gaining hit points and doing more damage. Initially, - your pet may even be better at killing things than you, which makes + Your pet also gains experience from killing monsters, and can + grow over time, gaining hit points and doing more damage. Initially, + your pet may even be better at killing things than you, which makes pets useful for low-level characters. - Your pet will follow you up and down staircases if it is next to + Your pet will follow you up and down staircases if it is next to you when you move. Otherwise your pet will be stranded and may become wild. Similarly, when you trigger certain types of traps which alter your location (for instance, a trap door which drops you to a lower @@ -2746,8 +2746,8 @@ 6.3. Steeds - Some types of creatures in the dungeon can actually be ridden if - you have the right equipment and skill. Convincing a wild beast to + Some types of creatures in the dungeon can actually be ridden if + you have the right equipment and skill. Convincing a wild beast to let you saddle it up is difficult to say the least. Many a dungeoneer has had to resort to magic and wizardry in order to forge the alliance. Once you do have the beast under your control however, you @@ -2766,7 +2766,7 @@ - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -2776,8 +2776,8 @@ - Use the "#loot" command while adjacent to a saddled creature to - try to remove the saddle from that creature. If successful, it will + Use the "#loot" command while adjacent to a saddled creature to + try to remove the saddle from that creature. If successful, it will be transferred to your inventory. 6.4. Bones levels @@ -2786,42 +2786,42 @@ even former incarnations of yourself!) and their personal effects. Ghosts are hard to kill, but easy to avoid, since they're slow and do little damage. You can plunder the deceased adventurer's possessions; - however, they are likely to be cursed. Beware of whatever killed the - former player; it is probably still lurking around, gloating over its + however, they are likely to be cursed. Beware of whatever killed the + former player; it is probably still lurking around, gloating over its last victory. 6.5. Persistence of Monsters - Monsters (a generic reference which also includes humans and + Monsters (a generic reference which also includes humans and pets) are only shown while they can be seen or otherwise sensed. Mov- ing to a location where you can't see or sense a monster any more will - result in it disappearing from your map, similarly if it is the one + result in it disappearing from your map, similarly if it is the one who moved rather than you. - However, if you encounter a monster which you can't see or + However, if you encounter a monster which you can't see or sense--perhaps it is invisible and has just tapped you on the noggin-- a special "remembered, unseen monster" marker will be displayed at the - location where you think it is. That will persist until you have - proven that there is no monster there, even if the unseen monster - moves to another location or you move to a spot where the marker's + location where you think it is. That will persist until you have + proven that there is no monster there, even if the unseen monster + moves to another location or you move to a spot where the marker's location ordinarily wouldn't be seen any more. 7. Objects - When you find something in the dungeon, it is common to want to - pick it up. In NetHack, this is accomplished by using the `,' com- - mand. If autopickup option is on, you will automatically pick up the + When you find something in the dungeon, it is common to want to + pick it up. In NetHack, this is accomplished by using the `,' com- + mand. If autopickup option is on, you will automatically pick up the object by walking over, unless you move with the `m' prefix. - If you're carrying too many items, NetHack will tell you so and - you won't be able to pick up anything more. Otherwise, it will add + If you're carrying too many items, NetHack will tell you so and + you won't be able to pick up anything more. Otherwise, it will add the object(s) to your pack and tell you what you just picked up. - As you add items to your inventory, you also add the weight of - that object to your load. The amount that you can carry depends on - your strength and your constitution. The stronger and sturdier you - are, the less the additional load will affect you. There comes a - point, though, when the weight of all of that stuff you are carrying + As you add items to your inventory, you also add the weight of + that object to your load. The amount that you can carry depends on + your strength and your constitution. The stronger and sturdier you + are, the less the additional load will affect you. There comes a + point, though, when the weight of all of that stuff you are carrying around with you through the dungeon will encumber you. Your reactions will get slower and you'll burn calories faster, requiring food more frequently to cope with it. Eventually, you'll be so overloaded that @@ -2832,7 +2832,7 @@ are encumbered, one of the conditions Burdened, Stressed, Strained, - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -2862,16 +2862,16 @@ extremely obvious, you will be asked what you want to call this type of object so you will recognize it later. You can also use the "#name" command, for the same purpose at any time, to name all objects - of a particular type or just an individual object. When you use - "#name" on an object which has already been named, specifying a space - as the value will remove the prior name instead of assigning a new + of a particular type or just an individual object. When you use + "#name" on an object which has already been named, specifying a space + as the value will remove the prior name instead of assigning a new one. 7.1. Curses and Blessings - Any object that you find may be cursed, even if the object is - otherwise helpful. The most common effect of a curse is being stuck - with (and to) the item. Cursed weapons weld themselves to your hand + Any object that you find may be cursed, even if the object is + otherwise helpful. The most common effect of a curse is being stuck + with (and to) the item. Cursed weapons weld themselves to your hand when wielded, so you cannot unwield them. Any cursed item you wear is not removable by ordinary means. In addition, cursed arms and armor usually, but not always, bear negative enchantments that make them @@ -2884,7 +2884,7 @@ Objects which are neither cursed nor blessed are referred to as uncursed. They could just as easily have been described as unblessed, - but the uncursed designation is what you will see within the game. A + but the uncursed designation is what you will see within the game. A "glass half full versus glass half empty" situation; make of that what you will. @@ -2898,7 +2898,7 @@ - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -2913,12 +2913,12 @@ guished in your inventory by the presence of the word cursed, uncursed, or blessed in the description of the item. In some cases uncursed will be omitted as being redundant when enough other informa- - tion is displayed. The implicit_uncursed option can be used to con- - trol this; toggle it off to have uncursed be displayed even when that + tion is displayed. The implicit_uncursed option can be used to con- + trol this; toggle it off to have uncursed be displayed even when that can be deduced from other attributes. - Sometimes the bless or curse state of objects is referred to as - their "BUC" attribute, for Blessed, Uncursed, or Cursed state, or + Sometimes the bless or curse state of objects is referred to as + their "BUC" attribute, for Blessed, Uncursed, or Cursed state, or "BUCX" for Blessed, Uncursed, Cursed, or unknown. (The term beatitude is occasionally used as well.) @@ -2942,12 +2942,12 @@ can be either positive or negative) that adds to your chance to hit and the damage you do to a monster. The only way to determine a weapon's enchantment is to have it magically identified somehow. Most - weapons are subject to some type of damage like rust. Such "erosion" + weapons are subject to some type of damage like rust. Such "erosion" damage can be repaired. - The chance that an attack will successfully hit a monster, and - the amount of damage such a hit will do, depends upon many factors. - Among them are: type of weapon, quality of weapon (enchantment and/or + The chance that an attack will successfully hit a monster, and + the amount of damage such a hit will do, depends upon many factors. + Among them are: type of weapon, quality of weapon (enchantment and/or erosion), experience level, strength, dexterity, encumbrance, and pro- ficiency (see below). The monster's armor class--a general defense rating, not necessarily due to wearing of armor--is a factor too; @@ -2957,14 +2957,14 @@ Many weapons can be wielded in one hand; some require both hands. When wielding a two-handed weapon, you can not wear a shield, and vice versa. When wielding a one-handed weapon, you can have another weapon - ready to use by setting things up with the `x' command, which - exchanges your primary (the one being wielded) and alternate weapons. - And if you have proficiency in the "two weapon combat" skill, you may - wield both weapons simultaneously as primary and secondary; use the - `X' command to engage or disengage that. Only some types of charac- + ready to use by setting things up with the `x' command, which + exchanges your primary (the one being wielded) and alternate weapons. + And if you have proficiency in the "two weapon combat" skill, you may + wield both weapons simultaneously as primary and secondary; use the + `X' command to engage or disengage that. Only some types of charac- - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -2974,19 +2974,19 @@ - ters (barbarians, for instance) have the necessary skill available. - Even with that skill, using two weapons at once incurs a penalty in - the chance to hit your target compared to using just one weapon at a + ters (barbarians, for instance) have the necessary skill available. + Even with that skill, using two weapons at once incurs a penalty in + the chance to hit your target compared to using just one weapon at a time. - There might be times when you'd rather not wield any weapon at + There might be times when you'd rather not wield any weapon at all. To accomplish that, wield `-', or else use the `A' command which allows you to unwield the current weapon in addition to taking off other worn items. Those of you in the audience who are AD&D players, be aware that each weapon which existed in AD&D does roughly the same damage to mon- - sters in NetHack. Some of the more obscure weapons (such as the + sters in NetHack. Some of the more obscure weapons (such as the aklys, lucern hammer, and bec-de-corbin) are defined in an appendix to Unearthed Arcana, an AD&D supplement. @@ -3003,26 +3003,26 @@ you will be prompted for a direction rather than for a specific tar- get. The distance something can be thrown depends mainly on the type of object and your strength. Arrows can be thrown by hand, but can be - thrown much farther and will be more likely to hit when thrown while + thrown much farther and will be more likely to hit when thrown while you are wielding a bow. - Some weapons will return when thrown. A boomerang--provided it - fails to hit anything--is an obvious example. If an aklys (thonged - club) is thrown while it is wielded, it will return even when it hits - something. A sufficiently strong hero can throw the warhammer Mjoll- - nir; when thrown by a Valkyrie it will return too. However, aklyses - and Mjollnir occasionally fail to return. Returning thrown objects - occasionally fail to be caught, sometimes even hitting the thrower, + Some weapons will return when thrown. A boomerang--provided it + fails to hit anything--is an obvious example. If an aklys (thonged + club) is thrown while it is wielded, it will return even when it hits + something. A sufficiently strong hero can throw the warhammer Mjoll- + nir; when thrown by a Valkyrie it will return too. However, aklyses + and Mjollnir occasionally fail to return. Returning thrown objects + occasionally fail to be caught, sometimes even hitting the thrower, but when caught they become re-wielded. - You can simplify the throwing operation by using the `Q' command - to select your preferred "missile", then using the `f' command to - throw it. You'll be prompted for a direction as above, but you don't - have to specify which item to throw each time you use `f'. There is - also an option, autoquiver, which has NetHack choose another item to - automatically fill your quiver (or quiver sack, or have at the ready) - when the inventory slot used for `Q' runs out. If your quiver is - empty, autoquiver is false, and you are wielding a weapon which + You can simplify the throwing operation by using the `Q' command + to select your preferred "missile", then using the `f' command to + throw it. You'll be prompted for a direction as above, but you don't + have to specify which item to throw each time you use `f'. There is + also an option, autoquiver, which has NetHack choose another item to + automatically fill your quiver (or quiver sack, or have at the ready) + when the inventory slot used for `Q' runs out. If your quiver is + empty, autoquiver is false, and you are wielding a weapon which returns when thrown, you will throw that weapon instead of filling the quiver. The fire command also has extra assistance, if fireassist is on it will try to wield a launcher matching the ammo in the quiver. @@ -3030,7 +3030,7 @@ - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -3045,7 +3045,7 @@ to load several rounds of ammunition at once--or hold several missiles in your hand--and still hit a target is not an easy task. Rangers are among those who are adept at this task, as are those with a high level - of proficiency in the relevant weapon skill (in bow skill if you're + of proficiency in the relevant weapon skill (in bow skill if you're wielding one to shoot arrows, in crossbow skill if you're wielding one to shoot bolts, or in sling skill if you're wielding one to shoot stones). The number of items that the character has a chance to fire @@ -3074,13 +3074,13 @@ daggers or staves but not in swords or bows. The "#enhance" extended command is used to review current weapons - proficiency (also spell proficiency) and to choose which skill(s) to - improve when you've used one or more skills enough to become eligible - to do so. The skill rankings are "none" (sometimes also referred to - as "restricted", because you won't be able to advance), "unskilled", - "basic", "skilled", and "expert". Restricted skills simply will not - appear in the list shown by "#enhance". (Divine intervention might - unrestrict a particular skill, in which case it will start at + proficiency (also spell proficiency) and to choose which skill(s) to + improve when you've used one or more skills enough to become eligible + to do so. The skill rankings are "none" (sometimes also referred to + as "restricted", because you won't be able to advance), "unskilled", + "basic", "skilled", and "expert". Restricted skills simply will not + appear in the list shown by "#enhance". (Divine intervention might + unrestrict a particular skill, in which case it will start at unskilled and be limited to basic.) Some characters can enhance their barehanded combat or martial arts skill beyond expert to "master" or "grand master". @@ -3090,13 +3090,13 @@ amount of damage done when you do hit; at basic level, there is no penalty or bonus; at skilled level, you receive a modest bonus in the chance to hit and amount of damage done; at expert level, the bonus is - higher. A successful hit has a chance to boost your training towards + higher. A successful hit has a chance to boost your training towards the next skill level (unless you've already reached the limit for this skill). Once such training reaches the threshold for that next level, - you'll be told that you feel more confident in your skills. At that + you'll be told that you feel more confident in your skills. At that - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -3106,8 +3106,8 @@ - point you can use "#enhance" to increase one or more skills. Such - skills are not increased automatically because there is a limit to + point you can use "#enhance" to increase one or more skills. Such + skills are not increased automatically because there is a limit to your total overall skills, so you need to actively choose which skills to enhance and which to ignore. @@ -3120,16 +3120,16 @@ weapons are not fully equal; the one in the hand you normally wield with is considered primary and the other one is considered secondary. The most noticeable difference is after you stop--or before you begin, - for that matter--wielding two weapons at once. The primary is your - wielded weapon and the secondary is just an item in your inventory + for that matter--wielding two weapons at once. The primary is your + wielded weapon and the secondary is just an item in your inventory that's been designated as alternate weapon.) - If your primary weapon is wielded but your off hand is empty or - has the wrong weapon, use the sequence `x', `w', `x' to first swap - your primary into your off hand, wield whatever you want as secondary - weapon, then swap them both back into the intended hands. If your - secondary or alternate weapon is correct but your primary one is not, - simply use `w' to wield the primary. Lastly, if neither hand holds + If your primary weapon is wielded but your off hand is empty or + has the wrong weapon, use the sequence `x', `w', `x' to first swap + your primary into your off hand, wield whatever you want as secondary + weapon, then swap them both back into the intended hands. If your + secondary or alternate weapon is correct but your primary one is not, + simply use `w' to wield the primary. Lastly, if neither hand holds the correct weapon, use `w', `x', `w' to first wield the intended sec- ondary, swap it to off hand, and then wield the primary. @@ -3141,9 +3141,9 @@ push the first into secondary position. When in two-weapon combat mode, using the `X' command toggles - back to single-weapon mode. Throwing or dropping either of the weap- - ons or having one of them be stolen or destroyed will also make you - revert to single-weapon combat. + back to single-weapon mode. Throwing or dropping either of the + weapons or having one of them be stolen or destroyed will also make + you revert to single-weapon combat. 7.3. Armor (`[') @@ -3162,7 +3162,7 @@ - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -3184,14 +3184,14 @@ You can also wear other pieces of armor (cloak over suit, shirt under suit, helmet, gloves, boots, shield) to lower your armor class even further. Most of these provide a one or two point improvement to - AC (making the overall value smaller and eventually negative) but can - also be enchanted. Shirts are an exception; they don't provide any - protection unless enchanted. Some cloaks also don't improve AC when - unenchanted but all cloaks offer some protection against rust or cor- - rosion to suits worn under them and against some monster touch + AC (making the overall value smaller and eventually negative) but can + also be enchanted. Shirts are an exception; they don't provide any + protection unless enchanted. Some cloaks also don't improve AC when + unenchanted but all cloaks offer some protection against rust or cor- + rosion to suits worn under them and against some monster touch attacks. - If a piece of armor is enchanted, its armor protection will be + If a piece of armor is enchanted, its armor protection will be better (or worse) than normal, and its "plus" (or minus) will subtract from your armor class. For example, a +1 chain mail would give you better protection than normal chain mail, lowering your armor class @@ -3201,11 +3201,11 @@ tion to being unremovable. Many types of armor are subject to some kind of damage like rust. - Such damage can be repaired. Some types of armor may inhibit spell + Such damage can be repaired. Some types of armor may inhibit spell casting. - The nudist option can be set (prior to game start) to attempt to - play the entire game without wearing any armor (a self-imposed chal- + The nudist option can be set (prior to game start) to attempt to + play the entire game without wearing any armor (a self-imposed chal- lenge which is extremely difficult to accomplish). The commands to use armor are `W' (wear) and `T' (take off). The @@ -3223,12 +3223,12 @@ boxes are heavy, and tins take a while to open. When you kill monsters, they usually leave corpses which are also - "food." Many, but not all, of these are edible; some also give you - special powers when you eat them. A good rule of thumb is "you are + "food." Many, but not all, of these are edible; some also give you + special powers when you eat them. A good rule of thumb is "you are what you eat." - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -3238,7 +3238,7 @@ - Some character roles and some monsters are vegetarian. Vegetar- + Some character roles and some monsters are vegetarian. Vegetar- ian monsters will typically never eat animal corpses, while vegetarian players can, but with some rather unpleasant side-effects. @@ -3264,10 +3264,10 @@ tory by type rather than by label, but the label is known in that sit- uation even though it isn't shown. - Many scrolls produce a different effect from usual if they are + Many scrolls produce a different effect from usual if they are blessed or cursed, or read while the hero is confused. - A mail daemon may run up and deliver mail to you as a scroll of + A mail daemon may run up and deliver mail to you as a scroll of mail (on versions compiled with this feature). To use this feature on versions where NetHack mail delivery is triggered by electronic mail appearing in your system mailbox, you must let NetHack know where to @@ -3283,18 +3283,18 @@ 7.6. Potions (`!') - Potions are distinguished by the color of the liquid inside the + Potions are distinguished by the color of the liquid inside the flask. They disappear after you quaff them. - Clear potions are potions of water. Sometimes these are blessed - or cursed, resulting in holy or unholy water. Holy water is the bane + Clear potions are potions of water. Sometimes these are blessed + or cursed, resulting in holy or unholy water. Holy water is the bane of the undead, so potions of holy water are good things to throw (`t') at them. It is also sometimes very useful to dip ("#dip") an object into a potion. - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -3309,9 +3309,9 @@ 7.7. Wands (`/') Wands usually have multiple magical charges. Some types of wands - require a direction in which to zap them. You can also zap them at - yourself (just give a `.' or `s' for the direction). Be warned, how- - ever, for this is often unwise. Other types of wands don't require a + require a direction in which to zap them. You can also zap them at + yourself (just give a `.' or `s' for the direction). Be warned, how- + ever, for this is often unwise. Other types of wands don't require a direction. The number of charges in a wand is random and decreases by one whenever you use it. @@ -3323,12 +3323,12 @@ of causing it to explode. The chance for such an explosion starts out very small and increases each time the wand is recharged. - In a truly desperate situation, when your back is up against the - wall, you might decide to go for broke and break your wand. This is - not for the faint of heart. Doing so will almost certainly cause a + In a truly desperate situation, when your back is up against the + wall, you might decide to go for broke and break your wand. This is + not for the faint of heart. Doing so will almost certainly cause a catastrophic release of magical energies. - When you have fully identified a particular wand, inventory dis- + When you have fully identified a particular wand, inventory dis- play will include additional information in parentheses: the number of times it has been recharged followed by a colon and then by its cur- rent number of charges. A current charge count of -1 is a special @@ -3360,7 +3360,7 @@ - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -3375,17 +3375,17 @@ Spellbooks are tomes of mighty magic. When studied with the `r' (read) command, they transfer to the reader the knowledge of a spell (and therefore eventually become unreadable)--unless the attempt back- - fires. Reading a cursed spellbook or one with mystic runes beyond + fires. Reading a cursed spellbook or one with mystic runes beyond your ken can be harmful to your health! - A spell (even when learned) can also backfire when you cast it. + A spell (even when learned) can also backfire when you cast it. If you attempt to cast a spell well above your experience level, or if you have little skill with the appropriate spell type, or cast it at a - time when your luck is particularly bad, you can end up wasting both + time when your luck is particularly bad, you can end up wasting both the energy and the time required in casting. - Casting a spell calls forth magical energies and focuses them - with your naked mind. Some of the magical energy released comes from + Casting a spell calls forth magical energies and focuses them + with your naked mind. Some of the magical energy released comes from within you. Casting temporarily drains your magical power, which will slowly be recovered, and causes you to need additional food. Casting of spells also requires practice. With practice, your skill in each @@ -3394,16 +3394,16 @@ Some spells require a direction in which to cast them, similar to wands. To cast one at yourself, just give a `.' or `s' for the direc- - tion. A few spells require you to pick a target location rather than - just specify a particular direction. Other spells don't require any + tion. A few spells require you to pick a target location rather than + just specify a particular direction. Other spells don't require any direction or target. - Just as weapons are divided into groups in which a character can - become proficient (to varying degrees), spells are similarly grouped. - Successfully casting a spell exercises its skill group; using the - "#enhance" command to advance a sufficiently exercised skill will - affect all spells within the group. Advanced skill may increase the - potency of spells, reduce their risk of failure during casting + Just as weapons are divided into groups in which a character can + become proficient (to varying degrees), spells are similarly grouped. + Successfully casting a spell exercises its skill group; using the + "#enhance" command to advance a sufficiently exercised skill will + affect all spells within the group. Advanced skill may increase the + potency of spells, reduce their risk of failure during casting attempts, and improve the accuracy of the estimate for how much longer they will be retained in your memory. Skill slots are shared with weapons skills. (See also the section on "Weapon proficiency".) @@ -3411,8 +3411,8 @@ Casting a spell also requires flexible movement, and wearing var- ious types of armor may interfere with that. - The command to read a spellbook is the same as for scrolls, `r' - (read). The `+' command lists each spell you know along with its + The command to read a spellbook is the same as for scrolls, `r' + (read). The `+' command lists each spell you know along with its level, skill category, chance of failure when casting, and an estimate of how strongly it is remembered. The `Z' (cast) command casts a spell. @@ -3426,7 +3426,7 @@ - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -3452,26 +3452,26 @@ You may encounter bags, boxes, and chests in your travels. A tool of this sort can be opened with the "#loot" extended command when - you are standing on top of it (that is, on the same floor spot), or - with the `a' (apply) command when you are carrying it. However, - chests are often locked, and are in any case unwieldy objects. You - must set one down before unlocking it by using a key or lock-picking - tool with the `a' (apply) command, by kicking it with the `^D' com- - mand, or by using a weapon to force the lock with the "#force" + you are standing on top of it (that is, on the same floor spot), or + with the `a' (apply) command when you are carrying it. However, + chests are often locked, and are in any case unwieldy objects. You + must set one down before unlocking it by using a key or lock-picking + tool with the `a' (apply) command, by kicking it with the `^D' com- + mand, or by using a weapon to force the lock with the "#force" extended command. - Some chests are trapped, causing nasty things to happen when you - unlock or open them. You can check for and try to deactivate traps + Some chests are trapped, causing nasty things to happen when you + unlock or open them. You can check for and try to deactivate traps with the "#untrap" extended command. - When the contents of a container are known, that container will - be described as something like "a sack containing 3 items". In this - example, the 3 refers to number of stacks of compatible items, not to - the total number of individual items. So a sack holding 2 sky blue - potions, 7 arrows, and 350 gold pieces would be described as having 3 - items rather than 10 or 359. And you would need to have 3 unused - inventory slots available in order to take everything out (for the - case where the items you remove don't combine into bigger stacks with + When the contents of a container are known, that container will + be described as something like "a sack containing 3 items". In this + example, the 3 refers to number of stacks of compatible items, not to + the total number of individual items. So a sack holding 2 sky blue + potions, 7 arrows, and 350 gold pieces would be described as having 3 + items rather than 10 or 359. And you would need to have 3 unused + inventory slots available in order to take everything out (for the + case where the items you remove don't combine into bigger stacks with things you're already carrying). If a chest or large box is described as "broken", that means that @@ -3492,7 +3492,7 @@ - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -3505,15 +3505,15 @@ 7.11. Amulets (`"') Amulets are very similar to rings, and often more powerful. Like - rings, amulets have various magical properties, some beneficial, some + rings, amulets have various magical properties, some beneficial, some harmful, which are activated by putting them on. - Only one amulet may be worn at a time, around your neck. Like - wearing rings, wearing an amulet affects your metabolism, causing you + Only one amulet may be worn at a time, around your neck. Like + wearing rings, wearing an amulet affects your metabolism, causing you to grow hungry more rapidly. - The commands to use amulets are the same as for rings, `P' (put - on) and `R' (remove). `A' can be used to remove various worn items + The commands to use amulets are the same as for rings, `P' (put + on) and `R' (remove). `A' can be used to remove various worn items including amulets. Also, `W' (wear) and `T' (take off) which are nor- mally for armor can be used for amulets and other accessories (rings and eyewear), but accessories won't be shown as likely candidates in a @@ -3521,8 +3521,8 @@ 7.12. Gems (`*') - Some gems are valuable, and can be sold for a lot of gold. They - are also a far more efficient way of carrying your riches. Valuable + Some gems are valuable, and can be sold for a lot of gold. They + are also a far more efficient way of carrying your riches. Valuable gems increase your score if you bring them with you when you exit. Other small rocks are also categorized as gems, but they are much @@ -3537,28 +3537,28 @@ Boulders occasionally block your path. You can push one forward (by attempting to walk onto its spot) when nothing blocks its path, or - you can smash it into a pile of small rocks with breaking magic or a + you can smash it into a pile of small rocks with breaking magic or a pick-axe. It is possible to move onto a boulder's location if certain conditions are met; ordinarily one of those conditions is that pushing - it any further be blocked. Using the move-without-picking-up prefix - (default key `m') prior to the direction of movement will attempt to - move to a boulder's location without pushing it in addition to the + it any further be blocked. Using the move-without-picking-up prefix + (default key `m') prior to the direction of movement will attempt to + move to a boulder's location without pushing it in addition to the prefix's usual action of suppressing auto-pickup at the destination. - Very large humanoids (giants and their ilk) have been known to + Very large humanoids (giants and their ilk) have been known to pick up boulders and use them as missile weapons. - Unlike boulders, statues can't be pushed, but don't need to be - because they don't block movement. They can be smashed into rocks + Unlike boulders, statues can't be pushed, but don't need to be + because they don't block movement. They can be smashed into rocks though. - For some configurations of the program, statues are no longer - shown as ``' but by the letter representing the monster they depict + For some configurations of the program, statues are no longer + shown as ``' but by the letter representing the monster they depict instead. - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -3585,12 +3585,12 @@ 7.15. Persistence of Objects Normally, if you have seen an object at a particular map location - and move to another location where you can't directly see that object - any more, it will continue to be displayed on your map. That remains - the case even if it is not actually there any more--perhaps a monster + and move to another location where you can't directly see that object + any more, it will continue to be displayed on your map. That remains + the case even if it is not actually there any more--perhaps a monster has picked it up or it has rotted away--until you can see or feel that location again. One notable exception is that if the object gets cov- - ered by the "remembered, unseen monster" marker. When that marker is + ered by the "remembered, unseen monster" marker. When that marker is later removed after you've verified that no monster is there, you will have forgotten that there was any object there regardless of whether the unseen monster actually took the object. If the object is still @@ -3598,7 +3598,7 @@ cover the object and resume remembering it. The situation is the same for a pile of objects, except that only - the top item of the pile is displayed. The hilite_pile option can be + the top item of the pile is displayed. The hilite_pile option can be enabled in order to show an item differently when it is the top one of a pile. @@ -3617,14 +3617,14 @@ Several of the challenges are related to eating behavior. The most difficult of these is the foodless challenge. Although creatures - can survive long periods of time without food, there is a physiologi- - cal need for water; thus there is no restriction on drinking bever- - ages, even if they provide some minor food benefits. Calling upon + can survive long periods of time without food, there is a physiologi- + cal need for water; thus there is no restriction on drinking bever- + ages, even if they provide some minor food benefits. Calling upon your god for help with starvation does not violate any food challenges either. - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -3639,7 +3639,7 @@ The corpses and tins of blobs (`b'), jellies (`j'), and fungi (`F') are also considered to be vegetable matter. Certain human food is prepared without animal products; namely, lembas wafers, cram rations, - food rations (gunyoki), K-rations, and C-rations. Metal or another + food rations (gunyoki), K-rations, and C-rations. Metal or another normally indigestible material eaten while polymorphed into a creature that can digest it is also considered vegan food. Note however that eating such items still counts against foodless conduct. @@ -3652,7 +3652,7 @@ of royal jelly. Monks are expected to observe a vegetarian diet. Eating any kind of meat violates the vegetarian, vegan, and food- - less conducts. This includes tripe rations, the corpses or tins of + less conducts. This includes tripe rations, the corpses or tins of any monsters not mentioned above, and the various other chunks of meat found in the dungeon. Swallowing and digesting a monster while poly- morphed is treated as if you ate the creature's corpse. Eating @@ -3663,14 +3663,14 @@ Regardless of conduct, there will be some items which are indi- gestible, and others which are hazardous to eat. Using a swallow-and- - digest attack against a monster is equivalent to eating the monster's - corpse. Please note that the term "vegan" is used here only in the - context of diet. You are still free to choose not to use or wear - items derived from animals (e.g. leather, dragon hide, bone, horns, - coral), but the game will not keep track of this for you. Also note - that "milky" potions may be a translucent white, but they do not con- - tain milk, so they are compatible with a vegan diet. Slime molds or - player-defined "fruits", although they could be anything from "cher- + digest attack against a monster is equivalent to eating the monster's + corpse. Please note that the term "vegan" is used here only in the + context of diet. You are still free to choose not to use or wear + items derived from animals (e.g. leather, dragon hide, bone, horns, + coral), but the game will not keep track of this for you. Also note + that "milky" potions may be a translucent white, but they do not con- + tain milk, so they are compatible with a vegan diet. Slime molds or + player-defined "fruits", although they could be anything from "cher- ries" to "pork chops", are also assumed to be vegan. An atheist is one who rejects religion. This means that you can- @@ -3683,14 +3683,14 @@ other religious figure; a true atheist would hear the words but attach no special meaning to them. - Most players fight with a wielded weapon (or tool intended to be - wielded as a weapon). Another challenge is to win the game without - using such a wielded weapon. You are still permitted to throw, fire, - and kick weapons; use a wand, spell, or other type of item; or fight + Most players fight with a wielded weapon (or tool intended to be + wielded as a weapon). Another challenge is to win the game without + using such a wielded weapon. You are still permitted to throw, fire, + and kick weapons; use a wand, spell, or other type of item; or fight with your hands and feet. - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -3700,12 +3700,12 @@ - In NetHack, a pacifist refuses to cause the death of any other - monster (i.e. if you would get experience for the death). This is a - particularly difficult challenge, although it is still possible to + In NetHack, a pacifist refuses to cause the death of any other + monster (i.e. if you would get experience for the death). This is a + particularly difficult challenge, although it is still possible to gain experience by other means. - An illiterate character does not read or write. This includes + An illiterate character does not read or write. This includes reading a scroll, spellbook, fortune cookie message, or t-shirt; writ- ing a scroll; or making an engraving of anything other than a single "X" (the traditional signature of an illiterate person). Reading an @@ -3723,40 +3723,40 @@ when in that branch of the dungeon. Some rules can't be bypassed, such as being unable to push a boulder diagonally. Other rules can, such as not smashing boulders with magic or tools, but doing so causes - you to receive a luck penalty. No message about that is given at the + you to receive a luck penalty. No message about that is given at the time, but it is tracked as a conduct. The #conduct command and end of game disclosure will report whether you have abided by the special rules of Sokoban, and if not, how many times you violated them, pro- viding you with a way to discover which actions incur bad luck so that - you can be better informed about whether or not to avoid repeating + you can be better informed about whether or not to avoid repeating those actions in the future. (Note: the Sokoban conduct will only be displayed if you have entered the Sokoban branch of the dungeon during - the current game. Once that has happened, it becomes part of dis- - closed conduct even if you haven't done anything interesting there. - Ending the game with "never broke the Sokoban rules" conduct is most - meaningful if you also manage to perform the "obtained the Sokoban + the current game. Once that has happened, it becomes part of dis- + closed conduct even if you haven't done anything interesting there. + Ending the game with "never broke the Sokoban rules" conduct is most + meaningful if you also manage to perform the "obtained the Sokoban prize" achievement (see Achievements below).) - There are several other challenges tracked by the game. It is - possible to eliminate one or more species of monsters by genocide; + There are several other challenges tracked by the game. It is + possible to eliminate one or more species of monsters by genocide; playing without this feature is considered a challenge. When the game offers you an opportunity to genocide monsters, you may respond with the monster type "none" if you want to decline. You can change the form of an item into another item of the same type ("polypiling") or the form of your own body into another creature ("polyself") by wand, spell, or potion of polymorph; avoiding these effects are each consid- - ered challenges. Polymorphing monsters, including pets, does not - break either of these challenges. Finally, you may sometimes receive - wishes; a game without an attempt to wish for any items is a chal- + ered challenges. Polymorphing monsters, including pets, does not + break either of these challenges. Finally, you may sometimes receive + wishes; a game without an attempt to wish for any items is a chal- lenge, as is a game without wishing for an artifact (even if the arti- fact immediately disappears). When the game offers you an opportunity - to make a wish for an item, you may choose "nothing" if you want to + to make a wish for an item, you may choose "nothing" if you want to decline. - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -3768,12 +3768,12 @@ 8.1. Achievements - End of game disclosure will also display various achievements - representing progress toward ultimate ascension, if any have been - attained. They aren't directly related to conduct but are grouped - with it because they fall into the same category of "bragging rights" - and to limit the number of questions during disclosure. Listed here - roughly in order of difficulty and not necessarily in the order in + End of game disclosure will also display various achievements + representing progress toward ultimate ascension, if any have been + attained. They aren't directly related to conduct but are grouped + with it because they fall into the same category of "bragging rights" + and to limit the number of questions during disclosure. Listed here + roughly in order of difficulty and not necessarily in the order in which you might accomplish them. Rank - Attained rank title Rank. @@ -3785,12 +3785,12 @@ Novel - Read a passage from a Discworld Novel. Sokoban - Entered Sokoban. Big Room - Entered the Big Room. - Soko-Prize - Explored to the top of Sokoban and found a + Soko-Prize - Explored to the top of Sokoban and found a special item there. - Mines' End - Explored to the bottom of the Gnomish Mines + Mines' End - Explored to the bottom of the Gnomish Mines and found a special item there. Medusa - Defeated Medusa. - Tune - Discovered the tune that can be used to open + Tune - Discovered the tune that can be used to open and close the drawbridge on the Castle level. Bell - Acquired the Bell of Opening. Gehennom - Entered Gehennom. @@ -3810,19 +3810,19 @@ Notes: - Achievements are recorded and subsequently reported in the order - in which they happen during your current game rather than the order + Achievements are recorded and subsequently reported in the order + in which they happen during your current game rather than the order listed here. - There are nine titles for each role, bestowed at experi- - ence levels 1, 3, 6, 10, 14, 18, 22, 26, and 30. The one for experi- - ence level 1 is not recorded as an achievement. Losing enough levels + There are nine titles for each role, bestowed at experi- + ence levels 1, 3, 6, 10, 14, 18, 22, 26, and 30. The one for experi- + ence level 1 is not recorded as an achievement. Losing enough levels to revert to lower rank(s) does not discard the corresponding achieve- ment(s). - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -3847,11 +3847,11 @@ even if you are not directly responsible, and only if she dies. The 5-note tune can be learned via trial and error with a musical - instrument played closely enough--but not too close!--to the Castle + instrument played closely enough--but not too close!--to the Castle level's drawbridge or can be given to you via prayer boon. - Blind, Deaf, Nudist, and Pauper are also conducts, and they can - only be enabled by setting the correspondingly named option in + Blind, Deaf, Nudist, and Pauper are also conducts, and they can + only be enabled by setting the correspondingly named option in NETHACKOPTIONS or run-time configuration file prior to game start. In the case of Blind and Deaf, the option also enforces the conduct. They aren't really significant accomplishments unless/until you make @@ -3866,8 +3866,8 @@ 9.1. Setting the options Options may be set in a number of ways. Within the game, the `O' - command allows you to view all options and change most of them. You - can also set options automatically by placing them in a configuration + command allows you to view all options and change most of them. You + can also set options automatically by placing them in a configuration file, or in the NETHACKOPTIONS environment variable. Some versions of NetHack also have front-end programs that allow you to set options before starting the game or a global configuration for system adminis- @@ -3875,10 +3875,10 @@ 9.2. Using a configuration file - The default name of the configuration file varies on different + The default name of the configuration file varies on different operating systems. - On UNIX, Linux, and macOS it is ".nethackrc" in the user's home + On UNIX, Linux, and macOS it is ".nethackrc" in the user's home directory. The file may not exist, but it is a normal ASCII text file and can be created with any text editor. @@ -3888,7 +3888,7 @@ ning NetHack for the first time, you should find a default template - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -3908,9 +3908,9 @@ as a comment and ignored. Empty lines are ignored. Any line beginning with `[' and ending in `]' is a section marker - (the closing `]' can be followed by whitespace and then an arbitrary - comment beginning with `#'). The text between the square brackets is - the section name. Section markers are only valid after a CHOOSE + (the closing `]' can be followed by whitespace and then an arbitrary + comment beginning with `#'). The text between the square brackets is + the section name. Section markers are only valid after a CHOOSE directive and their names are case insensitive. Lines after a section marker belong to that section up until another section starts or a marker without a name is encountered or the file ends. Lines within @@ -3925,11 +3925,11 @@ Here is a list of allowed directives: OPTIONS - There are two types of options, boolean and compound options. Bool- - ean options toggle a setting on or off, while compound options take - more diverse values. Prefix a boolean option with "no" or `!' to - turn it off. For compound options, the option name and value are - separated by a colon. Some options are persistent, and apply only + There are two types of options, boolean and compound options. + Boolean options toggle a setting on or off, while compound options + take more diverse values. Prefix a boolean option with "no" or `!' + to turn it off. For compound options, the option name and value are + separated by a colon. Some options are persistent, and apply only to new games. You can specify multiple OPTIONS directives, and mul- tiple options separated by commas in a single OPTIONS directive. (Comma separated options are processed from right to left.) @@ -3941,20 +3941,20 @@ HACKDIR Default location of files NetHack needs. On Windows HACKDIR defaults - to the location of the NetHack.exe or NetHackw.exe file so setting + to the location of the NetHack.exe or NetHackw.exe file so setting HACKDIR to override that is not usually necessary or recommended. LEVELDIR - The location that in-progress level files are stored. Defaults to + The location that in-progress level files are stored. Defaults to HACKDIR, must be writable. SAVEDIR - The location where saved games are kept. Defaults to HACKDIR, must + The location where saved games are kept. Defaults to HACKDIR, must be writable. - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -3973,11 +3973,11 @@ HACKDIR, must be writable. TROUBLEDIR - The location that a record of game aborts and self-diagnosed game + The location that a record of game aborts and self-diagnosed game problems is kept. Defaults to HACKDIR, must be writable. AUTOCOMPLETE - Enable or disable an extended command autocompletion. Autocomple- + Enable or disable an extended command autocompletion. Autocomple- tion has no effect for the X11 windowport. You can specify multiple autocompletions. To enable autocompletion, list the extended com- mand. Prefix the command with "!" to disable the autocompletion for @@ -3988,11 +3988,11 @@ AUTOCOMPLETE=zap,!annotate AUTOPICKUP_EXCEPTION - Set exceptions to the pickup_types option. See the "Configuring + Set exceptions to the pickup_types option. See the "Configuring Autopickup Exceptions" section. BINDINGS - Change the key bindings of some special keys, menu accelerators, + Change the key bindings of some special keys, menu accelerators, extended commands, or mouse buttons. You can specify multiple bind- ings. Format is key followed by the command, separated by a colon. See the "Changing Key Bindings" section for more information. @@ -4020,7 +4020,7 @@ begins; whatever follows will be common to all sections. Otherwise - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -4062,8 +4062,8 @@ WIZKIT Debug mode only: extra items to add to initial inventory. Value is - the name of a text file containing a list of item names, one per - line, up to a maximum of 128 lines. Each line is processed by the + the name of a text file containing a list of item names, one per + line, up to a maximum of 128 lines. Each line is processed by the function that handles wishing. Example: @@ -4086,7 +4086,7 @@ - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -4117,8 +4117,8 @@ 9.3. Using the NETHACKOPTIONS environment variable - The NETHACKOPTIONS variable is a comma-separated list of initial - values for the various options. Some can only be turned on or off. + The NETHACKOPTIONS variable is a comma-separated list of initial + values for the various options. Some can only be turned on or off. You turn one of these on by adding the name of the option to the list, and turn it off by typing a `!' or "no" before the name. Others take a character string as a value. You can set string options by typing @@ -4152,7 +4152,7 @@ sign) to let NetHack know that the rest is intended as a file name. - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -4178,7 +4178,7 @@ Add location or direction information to messages (default is off). acoustics - Enable messages about what your character hears (default on). Note + Enable messages about what your character hears (default on). Note that this has nothing to do with your computer's audio capabilities. Persistent. @@ -4189,12 +4189,12 @@ role for a description of how to use negation to exclude choices. If align is not specified, there is no default value; player will be - prompted unless role and/or race forces a choice for alignment. + prompted unless role and/or race forces a choice for alignment. Cannot be set with the `O' command. Persistent. autodescribe - Automatically describe the terrain under cursor when asked to get a - location on the map (default true). The whatis_coord option con- + Automatically describe the terrain under cursor when asked to get a + location on the map (default true). The whatis_coord option con- trols whether the description includes map coordinates. autodig @@ -4206,19 +4206,19 @@ sistent. autopickup - Automatically pick up things onto which you move (default off). + Automatically pick up things onto which you move (default off). Persistent. - See pickup_types and also autopickup_exception for ways to refine + See pickup_types and also autopickup_exception for ways to refine the behavior. Note: prior to version 3.7.0, the default for autopickup was on. autoquiver - This option controls what happens when you attempt the `f' (fire) + This option controls what happens when you attempt the `f' (fire) - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -4228,8 +4228,8 @@ - command when nothing is quivered or readied (default false). When - true, the computer will fill your quiver or quiver sack or make + command when nothing is quivered or readied (default false). When + true, the computer will fill your quiver or quiver sack or make ready some suitable weapon. Note that it will not take into account the blessed/cursed status, enchantment, damage, or quality of the weapon; you are free to manually fill your quiver or quiver sack or @@ -4247,12 +4247,12 @@ it will ask whether you want to try to disarm the trap; if you decline, your character will forget that the door or box is trapped; - Apply-Key - if carrying a key or other unlocking tool, prompt about + Apply-Key - if carrying a key or other unlocking tool, prompt about using it; - Kick - kick the door (if you omit untrap or decline to attempt - untrap and you omit apply-key or you lack a key or you + Kick - kick the door (if you omit untrap or decline to attempt + untrap and you omit apply-key or you lack a key or you decline to use the key; has no effect on containers); - Force - try to force a container's lid with your currently + Force - try to force a container's lid with your currently wielded weapon (if you omit untrap or decline to attempt untrap and you omit apply-key or you lack a key or you decline to use the key; has no effect on doors); @@ -4262,10 +4262,10 @@ Omitting the value is treated as if autounlock:apply-key. Preceding autounlock with `!' or "no" is treated as autounlock:none. - Applying a key might set off a trap if the door or container is - trapped. Successfully kicking a door will break it and wake up - nearby monsters. Successfully forcing a container open will break - its lock and might also destroy some of its contents or damage your + Applying a key might set off a trap if the door or container is + trapped. Successfully kicking a door will break it and wake up + nearby monsters. Successfully forcing a container open will break + its lock and might also destroy some of its contents or damage your weapon or both. The default is Apply-Key. Persistent. @@ -4277,14 +4277,14 @@ Allow saving and loading bones files (default true). Persistent. boulder - Set the character used to display boulders (default is the "large + Set the character used to display boulders (default is the "large rock" class symbol, ``'). catname - Name your starting cat (for example "catname:Morris"). Cannot be + Name your starting cat (for example "catname:Morris"). Cannot be - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -4297,7 +4297,7 @@ set with the `O' command. character - Synonym for "role" to pick the type of your character (for example + Synonym for "role" to pick the type of your character (for example "character:Monk"). See role for more details. checkpoint @@ -4350,7 +4350,7 @@ - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -4374,12 +4374,12 @@ of-game disclosure follows a set sequence. (for example "disclose:yi na +v -g o") The example sets inventory to - prompt and default to yes, attributes to prompt and default to no, - vanquished to disclose without prompting, genocided to not disclose - and not prompt, conduct to implicitly prompt and default to no, and + prompt and default to yes, attributes to prompt and default to no, + vanquished to disclose without prompting, genocided to not disclose + and not prompt, conduct to implicitly prompt and default to no, and overview to disclose without prompting. - Note that the vanquished monsters list includes all monsters killed + Note that the vanquished monsters list includes all monsters killed by traps and each other as well as by you. And the dungeon overview shows all levels you had visited but does not reveal things about them that you hadn't discovered. @@ -4394,9 +4394,9 @@ interface except that it does not require that you hit Enter. It is implemented for the tty interface (default off). - For the X11 interface, which always uses a menu for choosing an - extended command, it controls whether the menu shows all available - commands (on) or just the subset of commands which have tradition- + For the X11 interface, which always uses a menu for choosing an + extended command, it controls whether the menu shows all available + commands (on) or just the subset of commands which have tradition- ally been considered extended ones (off). female @@ -4411,12 +4411,12 @@ fixinv An object's inventory letter sticks to it when it's dropped (default - on). If this is off, dropping an object shifts all the remaining + on). If this is off, dropping an object shifts all the remaining inventory letters. Persistent. - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -4427,11 +4427,11 @@ force_invmenu - Commands asking for an inventory item show a menu instead of a text + Commands asking for an inventory item show a menu instead of a text query with possible menu letters. Default is off. fruit - Name a fruit after something you enjoy eating (for example + Name a fruit after something you enjoy eating (for example "fruit:mango") (default "slime mold"). Basically a nostalgic whimsy that NetHack uses from time to time. You should set this to some- thing you find more appetizing than slime mold. Apples, oranges, @@ -4445,8 +4445,8 @@ gender option is also present it will take precedence. See role for a description of how to use negation to exclude choices. - If gender is not specified, there is no default value; player will - be prompted unless role and/or race forces a choice for gender. + If gender is not specified, there is no default value; player will + be prompted unless role and/or race forces a choice for gender. Cannot be set with the `O' command. Persistent. goldX @@ -4466,23 +4466,23 @@ herecmd_menu When using a windowport that supports mouse and clicking on yourself - or next to you, show a menu of possible actions for the location. + or next to you, show a menu of possible actions for the location. Same as "#herecmdmenu" and "#therecmdmenu" commands. hilite_pet - Visually distinguish pets from similar animals (default off). The - behavior of this option depends on the type of windowing you use. + Visually distinguish pets from similar animals (default off). The + behavior of this option depends on the type of windowing you use. In text windowing, text highlighting or inverse video is often used; with tiles, generally displays a heart symbol near pets. With the tty or curses interface, the petattr option controls how to - highlight pets and setting it will turn the hilite_pet option on or + highlight pets and setting it will turn the hilite_pet option on or off as warranted. - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -4494,28 +4494,28 @@ hilite_pile Visually distinguish piles of objects from individual objects - (default off). The behavior of this option depends on the type of - windowing you use. In text windowing, text highlighting or inverse - video is often used; with tiles, generally displays a small plus- + (default off). The behavior of this option depends on the type of + windowing you use. In text windowing, text highlighting or inverse + video is often used; with tiles, generally displays a small plus- symbol beside the object on the top of the pile. hitpointbar - Show a hit point bar graph behind your name and title in the status + Show a hit point bar graph behind your name and title in the status display (default off). - The "curses" interface supports it even if the status highlighting - feature has been disabled when building the program. The "tty" and - "mswin" (aka "Windows GUI") interfaces support it only if status - highlighting is left enabled when building. You don't need to set - up any highlighting rules in order to display the bar. If there is - one for hitpoints in effect and it specifies color, that color will + The "curses" interface supports it even if the status highlighting + feature has been disabled when building the program. The "tty" and + "mswin" (aka "Windows GUI") interfaces support it only if status + highlighting is left enabled when building. You don't need to set + up any highlighting rules in order to display the bar. If there is + one for hitpoints in effect and it specifies color, that color will be used for the bar. However if it specifies video attributes, they will be ignored in favor of inverse. For tty and curses, blink will also be used if the current hitpoint value is at or below the criti- cal HP threshold. The "Qt" interface also supports hitpointbar, by drawing a solid bar - above the name and title with a hard-coded color scheme. (As of + above the name and title with a hard-coded color scheme. (As of this writing, having the bar enabled unintentionally inhibits resiz- ing the status panel. To resize that, use the #optionsfull command to toggle the hitpointbar option off, perform the resize while it's @@ -4544,11 +4544,11 @@ your character as lit (default off). Persistent. lootabc - When using a menu to interact with a container, use the old `a', - `b', and `c' keyboard shortcuts rather than the mnemonics `o', `i', + When using a menu to interact with a container, use the old `a', + `b', and `c' keyboard shortcuts rather than the mnemonics `o', `i', - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -4564,24 +4564,24 @@ Enable mail delivery during the game (default on). Persistent. male - An obsolete synonym for "gender:male". Cannot be set with the `O' + An obsolete synonym for "gender:male". Cannot be set with the `O' command. mention_decor - Give feedback when walking onto various dungeon features such as - stairs, fountains, or altars which are ordinarily only described - when covered by one or more objects (default off). Cannot be set + Give feedback when walking onto various dungeon features such as + stairs, fountains, or altars which are ordinarily only described + when covered by one or more objects (default off). Cannot be set with the `O' command. Persistent. mention_map Give feedback when interesting map locations change (default off). mention_walls - Give feedback when walking against a wall (default off). Persis- + Give feedback when walking against a wall (default off). Persis- tent. menucolors - Enable coloring menu lines (default off). See "Configuring Menu + Enable coloring menu lines (default off). See "Configuring Menu Colors" on how to configure the colors. menustyle @@ -4595,12 +4595,12 @@ it consists of a prompt for object class characters, followed by an object-by-object prompt for all items matching the selected object class(es). Combination starts with a prompt for object class(es) of - interest, but then displays a menu of matching objects rather than + interest, but then displays a menu of matching objects rather than prompting one-by-one. Full displays a menu of object classes rather than a character prompt, and then a menu of matching objects for selection. (Choosing its `A' (Autoselect-All) choice skips the sec- - ond menu. To avoid choosing that by accident, set paranoid_con- - firm:AutoAll to require confirmation.) Partial skips the object + ond menu. To avoid choosing that by accident, set paranoid_con- + firm:AutoAll to require confirmation.) Partial skips the object class filtering and immediately displays a menu of all objects. menu_deselect_all @@ -4614,7 +4614,7 @@ - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -4625,9 +4625,9 @@ menu_headings - Controls how the headings in a menu are highlighted. Takes a text - attribute, or text color and attribute separated by ampersand. For - allowed attributes and colors, see "Configuring Menu Colors". Not + Controls how the headings in a menu are highlighted. Takes a text + attribute, or text color and attribute separated by ampersand. For + allowed attributes and colors, see "Configuring Menu Colors". Not all ports can actually display all types. menu_invert_all @@ -4643,11 +4643,11 @@ Key to go to the next menu page. Default `>'. menu_objsyms - Inventory and other object menus are normally separated by object + Inventory and other object menus are normally separated by object class (weapons, armor, and so forth), with a menu header line at the beginning of each group. You can have menus add the display symbol for the class of objects for each header line. You can also add the - display symbol for the individual item in each menu entry. For a + display symbol for the individual item in each menu entry. For a tiles map, that would be a small rendition of an object's tile. For a text map, it is the same character as is used for the object's class, which would be most useful when there are no headers separat- @@ -4658,13 +4658,13 @@ 1 - Headers - show symbols on header lines but not entries; 2 - Entries - show symbols on menu entry lines but not headers; 3 - Both - show symbols on headers and entries; - 4 - Conditional - only show symbols for entries if there are no + 4 - Conditional - only show symbols for entries if there are no headers; - 5 - One-or-other - show symbols on headers, or on entries if no + 5 - One-or-other - show symbols on headers, or on entries if no headers. - Supported by tty and curses. When setting the value, it can be - specified by digit or keyword. The default value is Conditional + Supported by tty and curses. When setting the value, it can be + specified by digit or keyword. The default value is Conditional (4). menu_overlay @@ -4680,7 +4680,7 @@ - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -4698,11 +4698,11 @@ menu_shift_left Key to scroll a menu--one which has been scrolled right--back to the - left. Implemented for perm_invent only by curses and X11. Default + left. Implemented for perm_invent only by curses and X11. Default `{'. menu_shift_right - Key to scroll a menu which has text beyond the right edge to the + Key to scroll a menu which has text beyond the right edge to the right. Implemented for perm_invent only by curses and X11. Default `}'. @@ -4746,7 +4746,7 @@ defaults to "full"), or it can be negated (which defaults to "sin- - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -4767,7 +4767,7 @@ On some systems, the default is the player's user name; on others, there is no default and the player will be prompted. The former can - made to behave like the latter by specifying a generic name such as + made to behave like the latter by specifying a generic name such as ``player''. Cannot be set with the `O' command. news @@ -4812,7 +4812,7 @@ A space separated list of specific situations where alternate - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -4829,17 +4829,17 @@ than `y', also require "no" to reject instead of accepting any non-yes response as no; changes pray and AutoAll to require "yes" or `no' too; - quit - require "yes" rather than `y' to confirm quitting the + quit - require "yes" rather than `y' to confirm quitting the game or switching into non-scoring explore mode; - die - require "yes" rather than `y' to confirm dying (not + die - require "yes" rather than `y' to confirm dying (not useful in normal play; applies to explore mode); - bones - require "yes" rather than `y' to confirm saving bones + bones - require "yes" rather than `y' to confirm saving bones data when dying in debug mode; - attack - require "yes" rather than `y' to confirm attacking a + attack - require "yes" rather than `y' to confirm attacking a peaceful monster; - wand-break - require "yes" rather than `y' to confirm breaking a + wand-break - require "yes" rather than `y' to confirm breaking a wand with the apply command; - eating - require "yes" rather than `y' to confirm whether to + eating - require "yes" rather than `y' to confirm whether to continue eating; Were-change - require "yes" rather than `y' to confirm changing form due to lycanthropy when hero has polymorph control; @@ -4847,27 +4847,27 @@ immediately praying; on by default; (to require "yes" rather than just `y', set Confirm too); trap - require `y' to confirm an attempt to move into or onto - a known trap, unless doing so is considered to be + a known trap, unless doing so is considered to be harmless; when enabled, this confirmation is also used for moving into visible gas cloud regions; (to require - "yes" rather than just `y', set Confirm too); confir- - mation can be skipped by using the `m' movement pre- + "yes" rather than just `y', set Confirm too); confir- + mation can be skipped by using the `m' movement pre- fix; swim - prevent walking into water or lava; on by default; (to deliberately step onto/into such terrain when this is set, use the `m' movement prefix when adjacent); AutoAll - require confirmation when the `A' (Autoselect-All) choice is selected in object class filtering menus for - menustyle:Full; (to require "yes" rather than just + menustyle:Full; (to require "yes" rather than just `y', set Confirm too); - Remove - require selection from inventory for `R' and `T' com- + Remove - require selection from inventory for `R' and `T' com- mands even when wearing just one applicable item; all - turn on all of the above. By default, the pray, swim, and trap choices are enabled, the others disabled. To disable them without setting any of the other choices, - use paranoid_confirmation:none. To keep them enabled while setting - any of the others, you can include them in the new list, such as + use paranoid_confirmation:none. To keep them enabled while setting + any of the others, you can include them in the new list, such as paranoid_confirmation:attack pray swim Remove or you can precede the first entry in the list with a plus sign, paranoid_confirma- tion:+attack Remove. To remove an entry that has been previously @@ -4878,7 +4878,7 @@ list entries to be added by their name and entries to be removed by - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -4892,11 +4892,11 @@ can be intermixed. pauper - Start the character with no possessions (default false). Persis- + Start the character with no possessions (default false). Persis- tent. perm_invent - If true, always display your current inventory in a window (default + If true, always display your current inventory in a window (default false). This only makes sense for windowing system interfaces that implement @@ -4921,8 +4921,8 @@ petattr Specifies one or more text highlighting attributes to use when show- - ing pets on the map. Effectively a superset of the hilite_pet bool- - ean option. Curses or tty interface only; value is one of none, + ing pets on the map. Effectively a superset of the hilite_pet + boolean option. Curses or tty interface only; value is one of none, bold, dim, underline, italic, blink, and inverse. Some of those choices might not work, depending upon terminal hardware or terminal emulation software. @@ -4930,21 +4930,21 @@ pettype Specify the type of your initial pet, if you are playing a character class that uses multiple types of pets; or choose to have no initial - pet at all. Possible values are "cat", "dog", "horse", and "none". + pet at all. Possible values are "cat", "dog", "horse", and "none". If the choice is not allowed for the role you are currently playing, it will be silently ignored. For example, "horse" will only be hon- ored when playing a knight. Cannot be set with the `O' command. pickup_burden - When you pick up an item that would exceed this encumbrance level - (Unencumbered, Burdened, streSsed, straiNed, overTaxed, or over- - Loaded), you will be asked if you want to continue. (Default `S'). + When you pick up an item that would exceed this encumbrance level + (Unencumbered, Burdened, streSsed, straiNed, overTaxed, or over- + Loaded), you will be asked if you want to continue. (Default `S'). Persistent. - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -4955,8 +4955,8 @@ pickup_stolen - If this option is on and autopickup is also on, try to pick up - things that a monster stole from you, even if they aren't in + If this option is on and autopickup is also on, try to pick up + things that a monster stole from you, even if they aren't in pickup_types or match an autopickup exception. Default is on. Per- sistent. @@ -4984,14 +4984,14 @@ pile_limit When walking across a pile of objects on the floor, threshold at which the message "there are few/several/many objects here" is given - instead of showing a popup list of those objects. A value of 0 + instead of showing a popup list of those objects. A value of 0 means "no limit" (always list the objects); a value of 1 effectively means "never show the objects" since the pile size will always be at least that big; default value is 5. Persistent. playmode - Values are "normal", "explore", or "debug". Allows selection of - explore mode (also known as discovery mode) or debug mode (also + Values are "normal", "explore", or "debug". Allows selection of + explore mode (also known as discovery mode) or debug mode (also known as wizard mode) instead of normal play. Debug mode might only be allowed for someone logged in under a particular user name (on multi-user systems) or specifying a particular character name (on @@ -5010,7 +5010,7 @@ - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -5022,14 +5022,14 @@ quick_farsight When set, usually prevents the "you sense your surroundings" message - where play pauses to allow you to browse the map whenever clairvoy- - ance randomly activates. Some situations, such as being underwater - or engulfed, ignore this option. It does not affect the clairvoy- - ance spell where pausing to examine revealed objects or monsters is + where play pauses to allow you to browse the map whenever clairvoy- + ance randomly activates. Some situations, such as being underwater + or engulfed, ignore this option. It does not affect the clairvoy- + ance spell where pausing to examine revealed objects or monsters is less intrusive. Default is off. Persistent. race - Selects your race (for example, race:human). Choices are human, + Selects your race (for example, race:human). Choices are human, dwarf, elf, gnome, and orc but most roles restrict which of the non- human races are allowed. See role for a description of how to use negation to exclude choices. @@ -5038,8 +5038,19 @@ prompted unless role forces a choice for race. Cannot be set with the `O' command. Persistent. + reroll + Allows rerolling your character's starting inventory and attributes + (default false). Persistent. + + Note that rerolling your character is not a recommended way to play + if aiming merely to win (a lucky start has a much smaller influence + on whether or not you win the game than your actions later in the + game). This option exists partly as an acknowledgement that some + players will insist on doing so anyway, and partly because rerolling + may be necessary for certain types of challenge games. + rest_on_space - Make the space bar a synonym for the `.' (#wait) command (default + Make the space bar a synonym for the `.' (#wait) command (default off). Persistent. role @@ -5057,26 +5068,15 @@ OPTIONS=role:!arc !bar !kni OPTIONS=!role:arc bar kni - There can be multiple instances of the role option if they're all + There can be multiple instances of the role option if they're all negations. - If role is not specified, there is no default value; player will be + If role is not specified, there is no default value; player will be prompted. Cannot be set with the `O' command. Persistent. - roguesymset - This option may be used to select one of the named symbol sets found - within "symbols" to alter the symbols displayed on the screen on the - rogue level. - - rlecomp - When writing out a save file, perform run length compression of the - map. Not all ports support run length compression. It has no effect - on reading an existing save file. - - - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -5086,6 +5086,16 @@ + roguesymset + This option may be used to select one of the named symbol sets found + within "symbols" to alter the symbols displayed on the screen on the + rogue level. + + rlecomp + When writing out a save file, perform run length compression of the + map. Not all ports support run length compression. It has no effect + on reading an existing save file. + runmode Controls the amount of screen updating for the map window when engaged in multi-turn movement (running via shift+direction or con- @@ -5108,41 +5118,31 @@ sistent. safe_wait - Prevents you from waiting or searching when next to a hostile mon- + Prevents you from waiting or searching when next to a hostile mon- ster (default on). Persistent. sanity_check - Evaluate monsters, objects, and map prior to each turn (default + Evaluate monsters, objects, and map prior to each turn (default off). Debug mode only. scores - Control what parts of the score list you are shown at the end (for - example "scores:5 top scores/4 around my score/own scores"). Only - the first letter of each category (`t', `a', or `o') is necessary. + Control what parts of the score list you are shown at the end (for + example "scores:5 top scores/4 around my score/own scores"). Only + the first letter of each category (`t', `a', or `o') is necessary. Persistent. showdamage - Whenever your character takes damage, show a message of the damage + Whenever your character takes damage, show a message of the damage taken, and the amount of hit points left. showexp - Show your accumulated experience points on bottom line (default + Show your accumulated experience points on bottom line (default off). Persistent. - showrace - Display yourself as the glyph for your race, rather than the glyph - for your role (default off). Note that this setting affects only - the appearance of the display, not the way the game treats you. - Persistent. - - showscore - Show your approximate accumulated score on bottom line (default - off). By default, this feature is suppressed when building the pro- - gram. Persistent. - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -5152,6 +5152,17 @@ + showrace + Display yourself as the glyph for your race, rather than the glyph + for your role (default off). Note that this setting affects only + the appearance of the display, not the way the game treats you. + Persistent. + + showscore + Show your approximate accumulated score on bottom line (default + off). By default, this feature is suppressed when building the pro- + gram. Persistent. + showvers Include the game's version number on the status lines (default off). Potentially useful if you switch between different versions or vari- @@ -5170,32 +5181,45 @@ The possible values are: - o - list object types by class, in discovery order within each + o - list object types by class, in discovery order within each class; default; - s - list object types by sortloot classification: by class, by sub- - class within class for classes which have substantial groupings - (like helmets, boots, gloves, and so forth for armor), with - object types partly-discovered via assigned name coming before + s - list object types by sortloot classification: by class, by sub- + class within class for classes which have substantial groupings + (like helmets, boots, gloves, and so forth for armor), with + object types partly-discovered via assigned name coming before fully identified types; c - list by class, alphabetically within each class; a - list alphabetically across all classes. - Can be interactively set via the `O' command or via using the `m' + Can be interactively set via the `O' command or via using the `m' prefix before the `\' or ``' command. sortloot - Controls the sorting behavior of the pickup lists for inventory and + Controls the sorting behavior of the pickup lists for inventory and #loot commands and some others. Persistent. The possible values are: full - always sort the lists; - loot - only sort the lists that don't use inventory letters, like + loot - only sort the lists that don't use inventory letters, like with the #loot and pickup commands; none - show lists the traditional way without sorting; default. + + + + NetHack 3.7.0 December 05, 2025 + + + + + + NetHack Guidebook 80 + + + sortpack - Sort the pack contents by type when displaying inventory (default + Sort the pack contents by type when displaying inventory (default on). Persistent. sortvanquished @@ -5206,21 +5230,9 @@ t - traditional--order by monster level; ties are broken by internal monster index; default; - - - NetHack 3.7.0 October 24, 2025 - - - - - - NetHack Guidebook 80 - - - - d - order by monster difficulty rating; ties broken by internal + d - order by monster difficulty rating; ties broken by internal index; - a - order alphabetically, first any unique monsters then all the + a - order alphabetically, first any unique monsters then all the others; c - order by monster class, by low to high level within each class; n - order by count, high to low; ties are broken by internal monster @@ -5236,7 +5248,7 @@ on). sparkle - Display a sparkly effect when a monster (including yourself) is hit + Display a sparkly effect when a monster (including yourself) is hit by an attack to which it is resistant (default on). Persistent. spot_monsters @@ -5251,14 +5263,27 @@ ing Status Hilites" for further information. status_updates - Allow updates to the status lines at the bottom of the screen + Allow updates to the status lines at the bottom of the screen (default true). suppress_alert - This option may be set to a NetHack version level to suppress alert - notification messages about feature changes for that and prior ver- + This option may be set to a NetHack version level to suppress alert + notification messages about feature changes for that and prior ver- sions (for example "suppress_alert:3.3.1"). + + + + NetHack 3.7.0 December 05, 2025 + + + + + + NetHack Guidebook 81 + + + symset This option may be used to select one of the named symbol sets found within "symbols" to alter the symbols displayed on the screen. Use @@ -5272,18 +5297,6 @@ When pausing momentarily for display effect, such as with explosions and moving objects, use a timer rather than sending extra characters to the screen. (Applies to "tty" and "curses" interfaces only; - - - NetHack 3.7.0 October 24, 2025 - - - - - - NetHack Guidebook 81 - - - "X11" interface always uses a timer-based delay. The default is on if configured into the program.) Persistent. @@ -5314,8 +5327,8 @@ Provide more commentary during the game (default on). Persistent. whatis_coord - When using the `/' or `;' commands to look around on the map with - autodescribe on, display coordinates after the description. Also + When using the `/' or `;' commands to look around on the map with + autodescribe on, display coordinates after the description. Also works in other situations where you are asked to pick a location. The possible settings are: @@ -5326,21 +5339,8 @@ s - screen [row,column] (row is offset to match tty usage); n - none (no coordinates shown) [default]. - The whatis_coord option is also used with the "/m", "/M", "/o", and - "/O" sub-commands of `/', where the "none" setting is overridden - with "map". - whatis_filter - When getting a location on the map, and using the keys to cycle - through next and previous targets, allows filtering the possible - targets. - - n - no filtering [default] - v - in view only - a - in same area only - - - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -5350,31 +5350,44 @@ - The area-filter tries to be slightly predictive--if you're - standing on a doorway, it will consider the area on the side of - the door you were last moving towards. + The whatis_coord option is also used with the "/m", "/M", "/o", + and "/O" sub-commands of `/', where the "none" setting is over- + ridden with "map". + + whatis_filter + When getting a location on the map, and using the keys to cycle + through next and previous targets, allows filtering the possible + targets. + + n - no filtering [default] + v - in view only + a - in same area only + + The area-filter tries to be slightly predictive--if you're standing + on a doorway, it will consider the area on the side of the door you + were last moving towards. Filtering can also be changed when getting a location with the "get- pos.filter" key. whatis_menu When getting a location on the map, and using a key to cycle through - next and previous targets, use a menu instead to pick a target. + next and previous targets, use a menu instead to pick a target. (default off) whatis_moveskip - When getting a location on the map, and using shifted movement keys - or meta-digit keys to fast-move, instead of moving 8 units at a + When getting a location on the map, and using shifted movement keys + or meta-digit keys to fast-move, instead of moving 8 units at a time, move by skipping the same glyphs. (default off) windowtype - When the program has been built to support multiple interfaces, - select which one to use, such as "tty" or "X11" (default depends on - build-time settings; use "#version" to check). Cannot be set with + When the program has been built to support multiple interfaces, + select which one to use, such as "tty" or "X11" (default depends on + build-time settings; use "#version" to check). Cannot be set with the `O' command. - When used, it should be the first option set since its value might - enable or disable the availability of various other options. For + When used, it should be the first option set since its value might + enable or disable the availability of various other options. For multiple lines in a configuration file, that would be the first non- comment line. For a comma-separated list in NETHACKOPTIONS or an OPTIONS line in a configuration file, that would be the rightmost @@ -5389,24 +5402,11 @@ contents. Not all ports support zero-comp compression. It has no effect on reading an existing save file. - 9.5. Window Port Customization options - - Here are explanations of the various options that are used to - customize and change the characteristics of the windowtype that you - have chosen. Character strings that are too long may be truncated. - Not all window ports will adjust for all settings listed here. You - can safely add any of these options to your configuration file, and if - the window port is capable of adjusting to suit your preferences, it - will attempt to do so. If it can't it will silently ignore it. You - can find out if an option is supported by the window port that you are - currently using by checking to see if it shows up in the Options list. - Some options are dynamic and can be specified during the game with the - `O' command. - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -5416,6 +5416,20 @@ + 9.5. Window Port Customization options + + Here are explanations of the various options that are used to + customize and change the characteristics of the windowtype that you + have chosen. Character strings that are too long may be truncated. + Not all window ports will adjust for all settings listed here. You + can safely add any of these options to your configuration file, and if + the window port is capable of adjusting to suit your preferences, it + will attempt to do so. If it can't it will silently ignore it. You + can find out if an option is supported by the window port that you are + currently using by checking to see if it shows up in the Options list. + Some options are dynamic and can be specified during the game with the + `O' command. + align_message Where to align or place the message window (top, bottom, left, or right) @@ -5457,6 +5471,17 @@ If NetHack can, it should use a font by the chosen name for the sta- tus window. + + NetHack 3.7.0 December 05, 2025 + + + + + + NetHack Guidebook 84 + + + font_text If NetHack can, it should use a font by the chosen name for text windows. @@ -5470,18 +5495,6 @@ font_size_message If NetHack can, it should use this size font for the message window. - - - NetHack 3.7.0 October 24, 2025 - - - - - - NetHack Guidebook 84 - - - font_size_status If NetHack can, it should use this size font for the status window. @@ -5493,8 +5506,8 @@ than in a window. guicolor - Use color text and/or highlighting attributes when displaying some - non-map data (such as menu selector letters). Curses interface + Use color text and/or highlighting attributes when displaying some + non-map data (such as menu selector letters). Curses interface only; default is on. large_font @@ -5504,17 +5517,17 @@ If NetHack can, it should display the map in the manner specified. player_selection - If NetHack can, it should pop up dialog boxes, or use prompts for + If NetHack can, it should pop up dialog boxes, or use prompts for character selection. popup_dialog If NetHack can, it should pop up dialog boxes for input. preload_tiles - If NetHack can, it should preload tiles into memory. For example, + If NetHack can, it should preload tiles into memory. For example, in the protected mode MS-DOS version, control whether tiles get pre- loaded into RAM at the start of the game. Doing so enhances perfor- - mance of the tile graphics, but uses more memory. (default on). + mance of the tile graphics, but uses more memory. (default on). Cannot be set with the `O' command. scroll_amount @@ -5523,22 +5536,9 @@ scroll_margin If NetHack can, it should scroll the display when the hero or cursor - is this number of cells away from the edge of the window. - - selectsaved - If NetHack can, it should display a menu of existing saved games for - the player to choose from at game startup, if it can. Not all ports - support this option. - - softkeyboard - Display an onscreen keyboard. Handhelds are most likely to support - this option. - - splash_screen - If NetHack can, it should display an opening splash screen when it - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -5548,6 +5548,19 @@ + is this number of cells away from the edge of the window. + + selectsaved + If NetHack can, it should display a menu of existing saved games for + the player to choose from at game startup, if it can. Not all ports + support this option. + + softkeyboard + Display an onscreen keyboard. Handhelds are most likely to support + this option. + + splash_screen + If NetHack can, it should display an opening splash screen when it starts up (default yes). statuslines @@ -5562,11 +5575,11 @@ The curses interface does likewise if the align_status option is set to top or bottom but ignores statuslines when set to left or right. - The Qt interface already displays more than 3 lines for status so + The Qt interface already displays more than 3 lines for status so uses the statuslines value differently. A value of 3 renders status in the Qt interface's original format, with the status window spread - out vertically. A value of 2 makes status be slightly condensed, - moving some fields to different lines to eliminate one whole line, + out vertically. A value of 2 makes status be slightly condensed, + moving some fields to different lines to eliminate one whole line, reducing the height needed. (If NetHack has been built using a ver- sion of Qt older than qt-5.9, statuslines can only be set in the run-time configuration file or via NETHACKOPTIONS, not during play @@ -5588,6 +5601,19 @@ options to select an alternate tile file. See NetHack.ad, the sam- ple X "application defaults" file. + + + + NetHack 3.7.0 December 05, 2025 + + + + + + NetHack Guidebook 86 + + + tile_height Specify the preferred height of each tile in a tile capable port. @@ -5603,17 +5629,6 @@ use_darkgray Use bold black instead of blue for black glyphs (TTY only). - - NetHack 3.7.0 October 24, 2025 - - - - - - NetHack Guidebook 86 - - - use_inverse If NetHack can, it should display inverse when the game specifies it. @@ -5624,7 +5639,7 @@ windowborders Whether to draw boxes around the map, status area, message area, and - persistent inventory window if enabled. Curses interface only. + persistent inventory window if enabled. Curses interface only. Acceptable values are 0 - off, never show borders @@ -5633,17 +5648,17 @@ 3 - on, except forced off for perm_invent 4 - auto, except forced off for perm_invent - (The 26x82 size threshold for `2' refers to number of rows and col- - umns of the display. A width of at least 110 columns (80+2+26+2) is - needed to show borders if align_status is set to left or right.) + (The 26x82 size threshold for `2' refers to number of rows and + columns of the display. A width of at least 110 columns (80+2+26+2) + is needed to show borders if align_status is set to left or right.) The persistent inventory window, when enabled, can grow until it is too big to fit on most displays, resulting in truncation of its con- tents. If borders are forced on (1) or the display is big enough to show them (2), setting the value to 3 or 4 instead will keep borders for the map, message, and status windows but have room for two addi- - tional lines of inventory plus widen each inventory line by two col- - umns. + tional lines of inventory plus widen each inventory line by two + columns. windowcolors If NetHack can, it should display all windows of a particular style @@ -5652,9 +5667,22 @@ OPTION=windowcolors:style foreground/background + + + + NetHack 3.7.0 December 05, 2025 + + + + + + NetHack Guidebook 87 + + + where style is one of "menu", "message", "status", or "text", and foreground and background are colors, either numeric (hash sign fol- - lowed by three pairs of hexadecimal digits, #rrggbb), one of the + lowed by three pairs of hexadecimal digits, #rrggbb), one of the named colors (black, red, green, brown, blue, magenta, cyan, orange, bright-green, yellow, bright-blue, bright-magenta, bright-cyan, white, gray, purple, silver, maroon, fuchsia, lime, olive, navy, @@ -5668,18 +5696,6 @@ If NetHack can, it should wrap long lines of text if they don't fit in the visible area of the window. - - - NetHack 3.7.0 October 24, 2025 - - - - - - NetHack Guidebook 87 - - - 9.6. Crash Report Options Please note that NetHack does not send any information off your @@ -5715,13 +5731,24 @@ prompts. Note that typing one or more digits as a count prefix prior to a command--preceded by n if the number_pad option is set-- is also subject to this conversion, so attempting to abort the count - by typing ESC will leave NetHack waiting for another character to - complete the two character sequence. Type a second ESC to finish + by typing ESC will leave NetHack waiting for another character to + complete the two character sequence. Type a second ESC to finish cancelling such a count. At other prompts a single ESC suffices. + + NetHack 3.7.0 December 05, 2025 + + + + + + NetHack Guidebook 88 + + + BIOS - Use BIOS calls to update the screen display quickly and to read the - keyboard (allowing the use of arrow keys to move) on machines with + Use BIOS calls to update the screen display quickly and to read the + keyboard (allowing the use of arrow keys to move) on machines with an IBM PC compatible BIOS ROM (default off, OS/2, PC, and ST NetHack only). @@ -5734,21 +5761,9 @@ subkeyvalue (Win32 tty NetHack only). May be used to alter the value of key- strokes that the operating system returns to NetHack to help compen- - - - NetHack 3.7.0 October 24, 2025 - - - - - - NetHack Guidebook 88 - - - - sate for international keyboard issues. OPTIONS=subkeyvalue:171/92 - will return 92 to NetHack, if 171 was originally going to be - returned. You can use multiple subkeyvalue assignments in the con- + sate for international keyboard issues. OPTIONS=subkeyvalue:171/92 + will return 92 to NetHack, if 171 was originally going to be + returned. You can use multiple subkeyvalue assignments in the con- figuration file if needed. Cannot be set with the `O' command. video @@ -5756,9 +5771,9 @@ "default", "vga", or "vesa". Setting "vesa" will cause the game to display tiles, using the full capability of the VGA hardware. Set- ting "vga" will cause the game to display tiles, fixed at 640x480 in - 16 colors, a mode that is compatible with all VGA hardware. Third - party tilesets will probably not work. Setting "autodetect" - attempts "vesa", then "vga", and finally sets "default" if neither + 16 colors, a mode that is compatible with all VGA hardware. Third + party tilesets will probably not work. Setting "autodetect" + attempts "vesa", then "vga", and finally sets "default" if neither of those modes works. Cannot be set with the `O' command. video_height @@ -5768,41 +5783,26 @@ Set the VGA mode resolution width (MS-DOS only, with video:vesa) videocolors - Set the color palette for PC systems using NO_TERMS (default - 4-2-6-1-5-3-15-12-10-14-9-13-11, (PC NetHack only). The order of - colors is red, green, brown, blue, magenta, cyan, bright.white, - bright.red, bright.green, yellow, bright.blue, bright.magenta, and + Set the color palette for PC systems using NO_TERMS (default + 4-2-6-1-5-3-15-12-10-14-9-13-11, (PC NetHack only). The order of + colors is red, green, brown, blue, magenta, cyan, bright.white, + bright.red, bright.green, yellow, bright.blue, bright.magenta, and bright.cyan. Cannot be set with the `O' command. videoshades - Set the intensity level of the three gray scales available (default - dark normal light, PC NetHack only). If the game display is diffi- - cult to read, try adjusting these scales; if this does not correct + Set the intensity level of the three gray scales available (default + dark normal light, PC NetHack only). If the game display is diffi- + cult to read, try adjusting these scales; if this does not correct the problem, try !color. Cannot be set with the `O' command. 9.8. Regular Expressions - Regular expressions are normally POSIX extended regular expres- - sions. It is possible to compile NetHack without regular expression - support on a platform where there is no regular expression library. - While this is not true of any modern platform, if your NetHack was - built this way, patterns are instead glob patterns; regardless, this - document refers to both as `regular expressions.' This applies to - Autopickup exceptions, Message types, Menu colors, and User sounds. - - 9.9. Configuring Autopickup Exceptions - - You can further refine the behavior of the autopickup option - beyond what is available through the pickup_types option. - - By placing autopickup_exception lines in your configuration file, - you can define patterns to be checked when the game is about to - autopickup something. + Regular expressions are normally POSIX extended regular expres- + sions. It is possible to compile NetHack without regular expression + support on a platform where there is no regular expression library. - - - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -5812,6 +5812,20 @@ + While this is not true of any modern platform, if your NetHack was + built this way, patterns are instead glob patterns; regardless, this + document refers to both as `regular expressions.' This applies to + Autopickup exceptions, Message types, Menu colors, and User sounds. + + 9.9. Configuring Autopickup Exceptions + + You can further refine the behavior of the autopickup option + beyond what is available through the pickup_types option. + + By placing autopickup_exception lines in your configuration file, + you can define patterns to be checked when the game is about to + autopickup something. + autopickup_exception Sets an exception to the pickup_types option. The autopickup_excep- tion option should be followed by a regular expression to be used as @@ -5829,7 +5843,7 @@ to override an earlier rule. Exceptions can be set with the `O' command, but because they are not - included in your configuration file, they won't be in effect if you + included in your configuration file, they won't be in effect if you save and then restore your game. autopickup_exception rules and not saved with the game. @@ -5841,7 +5855,7 @@ The first example above will result in autopickup of any type of arrow. The second example results in the exclusion of any corpse from - autopickup. The last example results in the exclusion of items known + autopickup. The last example results in the exclusion of items known to be cursed from autopickup. 9.10. Changing Key Bindings @@ -5853,6 +5867,17 @@ character ("x"), a control key ("^X", "C-x"), a meta key ("M-x"), a mouse button, or a three-digit decimal ASCII code. + + NetHack 3.7.0 December 05, 2025 + + + + + + NetHack Guidebook 90 + + + For example: BIND=^X:getpos.autodescribe @@ -5866,19 +5891,7 @@ Menu accelerator keys The menu control or accelerator keys can also be rebound via OPTIONS - - - NetHack 3.7.0 October 24, 2025 - - - - - - NetHack Guidebook 90 - - - - lines in the configuration file. You cannot bind object symbols or + lines in the configuration file. You cannot bind object symbols or selection letters into menu accelerators. Some interfaces only sup- port some of the menu accelerators. @@ -5893,17 +5906,17 @@ available. Special command can only be bound to a single key. count - Prefix key to start a count, to repeat a command this many times. + Prefix key to start a count, to repeat a command this many times. With number_pad only. Default is `n'. getdir.help - When asked for a direction, the key to show the help. Default is + When asked for a direction, the key to show the help. Default is `?'. getdir.mouse - When asked for a direction, the key to initiate a simulated mouse - click. You will be asked to pick a location. Use movement key- - strokes to move the cursor around the map, then type the get- + When asked for a direction, the key to initiate a simulated mouse + click. You will be asked to pick a location. Use movement key- + strokes to move the cursor around the map, then type the get- pos.pick.once key (default `,') or the getpos.pick key (default `.') to finish as if performing a left or right click. Only useful when using the #therecmdmenu command. Default is `_'. @@ -5920,21 +5933,8 @@ When asked for a location, the key to toggle autodescribe. Default is `#'. - getpos.all.next - When asked for a location, the key to go to next closest interesting - thing. Default is `a'. - getpos.all.prev - When asked for a location, the key to go to previous closest inter- - esting thing. Default is `A'. - - getpos.door.next - When asked for a location, the key to go to next closest door or - doorway. Default is `d'. - - - - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -5944,6 +5944,18 @@ + getpos.all.next + When asked for a location, the key to go to next closest interesting + thing. Default is `a'. + + getpos.all.prev + When asked for a location, the key to go to previous closest inter- + esting thing. Default is `A'. + + getpos.door.next + When asked for a location, the key to go to next closest door or + doorway. Default is `d'. + getpos.door.prev When asked for a location, the key to go to previous closest door or doorway. Default is `D'. @@ -5968,39 +5980,27 @@ Default is `O'. getpos.menu - When asked for a location, and using one of the next or previous - keys to cycle through targets, toggle showing a menu instead. + When asked for a location, and using one of the next or previous + keys to cycle through targets, toggle showing a menu instead. Default is `!'. getpos.moveskip - When asked for a location, and using the shifted movement keys or - meta-digit keys to fast-move around, move by skipping the same + When asked for a location, and using the shifted movement keys or + meta-digit keys to fast-move around, move by skipping the same glyphs instead of by 8 units. Default is `*'. getpos.filter - When asked for a location, change the filtering mode when using one - of the next or previous keys to cycle through targets. Toggles - between no filtering, in view only, and in the same area only. + When asked for a location, change the filtering mode when using one + of the next or previous keys to cycle through targets. Toggles + between no filtering, in view only, and in the same area only. Default is `"'. getpos.pick - When asked for a location, the key to choose the location, and pos- - sibly ask for more info. When simulating a mouse click after being - asked for a direction (see getdir.mouse above), the key to use to - respond as right click. Default is `.'. - - getpos.pick.once - When asked for a location, the key to choose the location, and skip - asking for more info. When simulating a mouse click after being - asked for a direction, the key to respond as left click. Default is - `,'. - - getpos.pick.quick - When asked for a location, the key to choose the location, skip ask- - ing for more info, and exit the location asking loop. Default is + When asked for a location, the key to choose the location, and pos- + sibly ask for more info. When simulating a mouse click after being - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -6010,26 +6010,38 @@ + asked for a direction (see getdir.mouse above), the key to use to + respond as right click. Default is `.'. + + getpos.pick.once + When asked for a location, the key to choose the location, and skip + asking for more info. When simulating a mouse click after being + asked for a direction, the key to respond as left click. Default is + `,'. + + getpos.pick.quick + When asked for a location, the key to choose the location, skip ask- + ing for more info, and exit the location asking loop. Default is `;'. getpos.pick.verbose - When asked for a location, the key to choose the location, and show + When asked for a location, the key to choose the location, and show more info without asking. Default is `:'. getpos.self - When asked for a location, the key to go to your location. Default + When asked for a location, the key to go to your location. Default is `@'. getpos.unexplored.next - When asked for a location, the key to go to next closest unexplored + When asked for a location, the key to go to next closest unexplored location. Default is `x'. getpos.unexplored.prev - When asked for a location, the key to go to previous closest unex- + When asked for a location, the key to go to previous closest unex- plored location. Default is `X'. getpos.valid - When asked for a location, the key to go to show valid target loca- + When asked for a location, the key to go to show valid target loca- tions. Default is `$'. getpos.valid.next @@ -6051,6 +6063,19 @@ type - how the message should be shown; pattern - the pattern to match. + + + + NetHack 3.7.0 December 05, 2025 + + + + + + NetHack Guidebook 93 + + + The pattern should be a regular expression. Allowed types are: @@ -6064,18 +6089,6 @@ Here's an example of message types using NetHack's internal pattern matching facility: - - - NetHack 3.7.0 October 24, 2025 - - - - - - NetHack Guidebook 93 - - - MSGTYPE=stop "You feel hungry." MSGTYPE=hide "You displaced *." @@ -6108,31 +6121,18 @@ Allowed colors are black, red, green, brown, blue, magenta, cyan, gray, orange, light-green, yellow, light-blue, light-magenta, light- - cyan, and white. And no-color, the default foreground color, which + cyan, and white. And no-color, the default foreground color, which isn't necessarily the same as any of the other colors. - Allowed attributes are none, bold, dim, italic, underline, blink, + Allowed attributes are none, bold, dim, italic, underline, blink, and inverse. "Normal" is a synonym for "none". Note that the plat- form used may interpret the attributes any way it wants. Here's an example of menu colors using NetHack's internal pattern matching facility: - MENUCOLOR="* blessed *"=green - MENUCOLOR="* cursed *"=red - MENUCOLOR="* cursed *(being worn)"=red&underline - specifies that any menu line with " blessed " contained in it will - be shown in green color, lines with " cursed " will be shown in red, - and lines with " cursed " followed by "(being worn)" on the same - line will be shown in red color and underlined. You can have multi- - ple MENUCOLOR entries in your configuration file, and the last MENU- - COLOR line that matches a menu line will be used for the line. - - - - - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -6142,14 +6142,25 @@ - Note that if you intend to have one or more color specifications + MENUCOLOR="* blessed *"=green + MENUCOLOR="* cursed *"=red + MENUCOLOR="* cursed *(being worn)"=red&underline + + specifies that any menu line with " blessed " contained in it will + be shown in green color, lines with " cursed " will be shown in red, + and lines with " cursed " followed by "(being worn)" on the same + line will be shown in red color and underlined. You can have multi- + ple MENUCOLOR entries in your configuration file, and the last MENU- + COLOR line that matches a menu line will be used for the line. + + Note that if you intend to have one or more color specifications match " uncursed ", you will probably want to turn the - implicit_uncursed option off so that all items known to be uncursed + implicit_uncursed option off so that all items known to be uncursed are actually displayed with the "uncursed" description. 9.13. Configuring User Sounds - Some platforms allow you to define sound files to be played when + Some platforms allow you to define sound files to be played when a message that matches a user-defined pattern is delivered to the mes- sage window. At this time the Qt port and the win32tty and win32gui ports support the use of user sounds. @@ -6164,9 +6175,9 @@ An entry that maps a sound file to a user-specified message pattern. Each SOUND entry is broken down into the following parts: - MESG - message window mapping (the only one supported in + MESG - message window mapping (the only one supported in 3.7.0); - msgtype - optional; message type to use, see "Configuring Mes- + msgtype - optional; message type to use, see "Configuring Mes- sage Types" pattern - the pattern to match; sound file - the sound file to play; @@ -6183,22 +6194,11 @@ - 9.14. Configuring Status Hilites - - Your copy of NetHack may have been compiled with support for - "Status Hilites". If so, you can customize your game display by set- - ting thresholds to change the color or appearance of fields in the - status display. - - The format for defining status colors is: - - OPTION=hilite_status:field-name/behavior/color&attributes - - For example, the following line in your configuration file will - cause the hitpoints field to display in the color red if your hit- - NetHack 3.7.0 October 24, 2025 + + + NetHack 3.7.0 December 05, 2025 @@ -6208,63 +6208,63 @@ + 9.14. Configuring Status Hilites + + Your copy of NetHack may have been compiled with support for + "Status Hilites". If so, you can customize your game display by set- + ting thresholds to change the color or appearance of fields in the + status display. + + The format for defining status colors is: + + OPTION=hilite_status:field-name/behavior/color&attributes + + For example, the following line in your configuration file will + cause the hitpoints field to display in the color red if your hit- points drop to or below a threshold of 30%: OPTION=hilite_status:hitpoints/<=30%/red/normal - (That example is actually specifying red&normal for <=30% and no- + (That example is actually specifying red&normal for <=30% and no- color&normal for >30%.) - For another example, the following line in your configuration + For another example, the following line in your configuration file will cause wisdom to be displayed red if it drops and green if it rises: OPTION=hilite_status:wisdom/down/red/up/green Allowed colors are black, red, green, brown, blue, magenta, cyan, - gray, orange, light-green, yellow, light-blue, light-magenta, light- - cyan, and white. And "no-color", the default foreground color on the + gray, orange, light-green, yellow, light-blue, light-magenta, light- + cyan, and white. And "no-color", the default foreground color on the display, which is not necessarily the same as black or white or any of the other colors. Allowed attributes are none, bold, dim, underline, italic, blink, - and inverse. "Normal" is a synonym for "none"; they should not be + and inverse. "Normal" is a synonym for "none"; they should not be used in combination with any of the other attributes. - To specify both a color and an attribute, use `&' to combine - them. To specify multiple attributes, use `+' to combine those. For + To specify both a color and an attribute, use `&' to combine + them. To specify multiple attributes, use `+' to combine those. For example: "magenta&inverse+dim". - Note that the display may substitute or ignore particular - attributes depending upon its capabilities, and in general may inter- - pret the attributes any way it wants. For example, on some display - systems a request for bold might yield blink or vice versa. On oth- - ers, issuing an attribute request while another is already set up will - replace the earlier attribute rather than combine with it. Since - NetHack issues attribute requests sequentially (at least with the tty - interface) rather than all at once, the only way a situation like that - can be controlled is to specify just one attribute. + Note that the display may substitute or ignore particular attrib- + utes depending upon its capabilities, and in general may interpret the + attributes any way it wants. For example, on some display systems a + request for bold might yield blink or vice versa. On others, issuing + an attribute request while another is already set up will replace the + earlier attribute rather than combine with it. Since NetHack issues + attribute requests sequentially (at least with the tty interface) + rather than all at once, the only way a situation like that can be + controlled is to specify just one attribute. You can adjust the appearance of the following status fields: title dungeon-level experience-level strength gold experience - dexterity hitpoints HD - constitution hitpoints-max time - intelligence power hunger - wisdom power-max carrying-capacity - charisma armor-class condition - alignment score - - The pseudo-field "characteristics" can be used to set all six of - Str, Dex, Con, Int, Wis, and Cha at once. "HD" is "hit dice", an - approximation of experience level displayed when polymorphed. - "experience", "time", and "score" are conditionally displayed - depending upon your other option settings. - - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -6274,14 +6274,27 @@ - Instead of a behavior, "condition" takes the following condition - flags: stone, slime, strngl, foodpois, termill, blind, deaf, stun, + dexterity hitpoints HD + constitution hitpoints-max time + intelligence power hunger + wisdom power-max carrying-capacity + charisma armor-class condition + alignment score + + The pseudo-field "characteristics" can be used to set all six of + Str, Dex, Con, Int, Wis, and Cha at once. "HD" is "hit dice", an + approximation of experience level displayed when polymorphed. + "experience", "time", and "score" are conditionally displayed + depending upon your other option settings. + + Instead of a behavior, "condition" takes the following condition + flags: stone, slime, strngl, foodpois, termill, blind, deaf, stun, conf, hallu, lev, fly, and ride. You can use "major_troubles" as an alias for stone through termill, "minor_troubles" for blind through hallu, "movement" for lev, fly, and ride, and "all" for every condi- tion. - Allowed behaviors are "always", "up", "down", "changed", a percent- + Allowed behaviors are "always", "up", "down", "changed", a percent- age or absolute number threshold, or text to match against. For the hitpoints field, the additional behavior "criticalhp" is available. It overrides other behavior rules if hit points are at or below the @@ -6304,33 +6317,20 @@ matches the percentage. It is specified as a number between 0 and 100, followed by `%' (percent sign). If the percentage is prefixed with `<=' or `>=', it also matches when value is below - or above the percentage. Use prefix `<' or `>' to match when + or above the percentage. Use prefix `<' or `>' to match when strictly below or above. (The numeric limit is relaxed - slightly for those: >-1% and <101% are allowed.) Only four - fields support percentage rules. Percentages for "hitpoints" - and "power" are straightforward; they're based on the corre- - sponding maximum field. Percentage highlight rules are also - allowed for "experience level" and "experience points" (valid + slightly for those: >-1% and <101% are allowed.) Only four + fields support percentage rules. Percentages for "hitpoints" + and "power" are straightforward; they're based on the corre- + sponding maximum field. Percentage highlight rules are also + allowed for "experience level" and "experience points" (valid when the showexp option is enabled). For those, the percentage is based on the progress from the start of the current experi- ence level to the start of the next level. So if level 2 starts at 20 points and level 3 starts at 40 points, having 30 - points is 50% and 35 points is 75%. 100% is unattainable for - experience because you'll gain a level and the calculations - will be reset for that new level, but a rule for =100% is - allowed and matches the special case of being exactly 1 experi- - ence point short of the next level. - - * absolute value sets the attribute when the field value matches - that number. The number must be 0 or higher, except for - "armor-class' which allows negative values, and may optionally - be preceded by `='. If the number is preceded by `<=' or `>=' - instead, it also matches when value is below or above. If the - prefix is `<' or `>', only match when strictly above or below. - - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -6340,7 +6340,20 @@ - * criticalhp only applies to the hitpoints field and only when + points is 50% and 35 points is 75%. 100% is unattainable for + experience because you'll gain a level and the calculations + will be reset for that new level, but a rule for =100% is + allowed and matches the special case of being exactly 1 experi- + ence point short of the next level. + + * absolute value sets the attribute when the field value matches + that number. The number must be 0 or higher, except for + "armor-class' which allows negative values, and may optionally + be preceded by `='. If the number is preceded by `<=' or `>=' + instead, it also matches when value is below or above. If the + prefix is `<' or `>', only match when strictly above or below. + + * criticalhp only applies to the hitpoints field and only when current hit points are below a threshold (which varies by maxi- mum hit points and experience level). When the threshold is met, a criticalhp rule takes precedence over all other hit- @@ -6377,26 +6390,13 @@ NetHack can load entire symbol sets from the symbol file. - The options that are used to select a particular symbol set from + The options that are used to select a particular symbol set from the symbol file are: - symset - Set the name of the symbol set that you want to load. - - roguesymset - Set the name of the symbol set that you want to load for display on - the rogue level. - - You can also override one or more symbols using the SYMBOLS and - ROGUESYMBOLS configuration file options. Symbols are specified as - name:value pairs. Note that NetHack escape-processes the value string - in conventional C fashion. This means that \ is a prefix to take the - following character literally. Thus \ needs to be represented as \\. - The special prefix form \m switches on the meta bit in the symbol - value, and the ^ prefix causes the following character to be treated - NetHack 3.7.0 October 24, 2025 + + NetHack 3.7.0 December 05, 2025 @@ -6406,6 +6406,20 @@ + symset + Set the name of the symbol set that you want to load. + + roguesymset + Set the name of the symbol set that you want to load for display on + the rogue level. + + You can also override one or more symbols using the SYMBOLS and + ROGUESYMBOLS configuration file options. Symbols are specified as + name:value pairs. Note that NetHack escape-processes the value string + in conventional C fashion. This means that \ is a prefix to take the + following character literally. Thus \ needs to be represented as \\. + The special prefix form \m switches on the meta bit in the symbol + value, and the ^ prefix causes the following character to be treated as a control character. NetHack Symbols @@ -6445,6 +6459,19 @@ # S_darkroom (dark room) ^ S_dart_trap (dart trap) & S_demon (major demon) + + + + NetHack 3.7.0 December 05, 2025 + + + + + + NetHack Guidebook 99 + + + * S_digbeam (dig beam) > S_dnladder (ladder down) > S_dnstair (staircase down) @@ -6459,19 +6486,6 @@ \ S_expl_tr (explosion top right) | S_expl_ml (explosion middle left) S_expl_mc (explosion middle center) - - - - NetHack 3.7.0 October 24, 2025 - - - - - - NetHack Guidebook 99 - - - | S_expl_mr (explosion middle right) \ S_expl_bl (explosion bottom left) - S_expl_bc (explosion bottom center) @@ -6511,6 +6525,19 @@ } S_lava (molten lava) } S_lavawall (wall of lava) l S_leprechaun (leprechaun) + + + + NetHack 3.7.0 December 05, 2025 + + + + + + NetHack Guidebook 100 + + + ^ S_level_teleporter (level teleporter) L S_lich (lich) y S_light (light) @@ -6525,19 +6552,6 @@ N S_naga (naga) . S_ndoor (doorway without door) n S_nymph (nymph) - - - - NetHack 3.7.0 October 24, 2025 - - - - - - NetHack Guidebook 100 - - - O S_ogre (ogre) o S_orc (orc) p S_piercer (piercer) @@ -6577,6 +6591,19 @@ | S_sw_ml (swallow middle left) | S_sw_mr (swallow middle right) - S_sw_tc (swallow top center) + + + + NetHack 3.7.0 December 05, 2025 + + + + + + NetHack Guidebook 101 + + + / S_sw_tl (swallow top left) \ S_sw_tr (swallow top right) - S_tdwall (wall) @@ -6591,19 +6618,6 @@ # S_tree (tree) T S_troll (troll) | S_trwall (wall) - - - - NetHack 3.7.0 October 24, 2025 - - - - - - NetHack Guidebook 101 - - - - S_tuwall (wall) U S_umber (umber hulk) S_unexplored (unexplored terrain) @@ -6639,28 +6653,14 @@ * Several symbols in this table appear to be blank. They are the space character, except for S_pet_override and S_hero_override which - don't have any default value and can only be used if enabled in the + don't have any default value and can only be used if enabled in the "sysconf" file. - * S_rock is misleadingly named; rocks and stones use S_gem. Statues - and boulders are the rock being referred to, but since version - 3.6.0, statues are displayed as the monster they depict. So S_rock - is only used for boulders and not used at all if overridden by the - more specific S_boulder. - - 9.16. Customizing Map Glyph Representations Using Unicode - - If your platform or terminal supports the display of UTF-8 char- - acter sequences, you can customize your game display by assigning Uni- - code codepoint values and red-green-blue colors to glyph representa- - tions. The customizations can be specified for use with a symset that - has a UTF8 handler within the symbols file such as the enhanced1 set, - or individually within your nethack.rc file. + * S_rock is misleadingly named; rocks and stones use S_gem. Statues + and boulders are the rock being referred to, but since version - - - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -6670,6 +6670,19 @@ + 3.6.0, statues are displayed as the monster they depict. So S_rock + is only used for boulders and not used at all if overridden by the + more specific S_boulder. + + 9.16. Customizing Map Glyph Representations Using Unicode + + If your platform or terminal supports the display of UTF-8 char- + acter sequences, you can customize your game display by assigning Uni- + code codepoint values and red-green-blue colors to glyph representa- + tions. The customizations can be specified for use with a symset that + has a UTF8 handler within the symbols file such as the enhanced1 set, + or individually within your nethack.rc file. + The format for defining a glyph representation is: OPTIONS=glyph:glyphid/U+nnnn/R-G-B @@ -6677,13 +6690,13 @@ The window port that is active needs to provide support for dis- playing UTF-8 character sequences and explicit red-green-blue colors in order for the glyph representation to be visible. For example, the - following line in your configuration file will cause the glyph repre- - sentation for glyphid G_pool to use Unicode codepoint U+224B and the + following line in your configuration file will cause the glyph repre- + sentation for glyphid G_pool to use Unicode codepoint U+224B and the color represented by R-G-B value 0-0-160: OPTIONS=glyph:G_pool/U+224B/0-0-160 - The list of acceptable glyphid's can be produced by nethack --dumpg- + The list of acceptable glyphid's can be produced by nethack --dumpg- lyphids. Individual NetHack glyphs can be specified using the G_ pre- fix, or you can use an S_ symbol for a glyphid and store the custom representation for all NetHack glyphs that would map to that particu- @@ -6700,33 +6713,20 @@ pletely accessible to the blind who use speech and/or Braille access technologies. Players will require a good working knowledge of their screen-reader's review features, and will have to know how to navigate - horizontally and vertically character by character. They will also + horizontally and vertically character by character. They will also find the search capabilities of their screen-readers to be quite valu- able. Be certain to examine this Guidebook before playing so you have an idea what the screen layout is like. You'll also need to be able to - locate the PC cursor. It is always where your character is located. - Merely searching for an @-sign will not always find your character - since there are other humanoids represented by the same sign. Your - screen-reader should also have a function which gives you the row and - column of your review cursor and the PC cursor. These co-ordinates + locate the PC cursor. It is always where your character is located. + Merely searching for an @-sign will not always find your character + since there are other humanoids represented by the same sign. Your + screen-reader should also have a function which gives you the row and + column of your review cursor and the PC cursor. These co-ordinates are often useful in giving players a better sense of the overall loca- tion of items on the screen. - NetHack can also be compiled with support for sending the game - messages to an external program, such as a text-to-speech synthesizer. - If the "#version" extended command shows "external program as a mes- - sage handler", your NetHack has been compiled with the capability. - When compiling NetHack from source on Linux and other POSIX systems, - define MSGHANDLER to enable it. To use the capability, set the envi- - ronment variable NETHACK_MSGHANDLER to an executable, which will be - executed with the game message as the program's only parameter. - The most crucial settings to make the game more accessible are: - - - - - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -6736,6 +6736,17 @@ + NetHack can also be compiled with support for sending the game + messages to an external program, such as a text-to-speech synthesizer. + If the "#version" extended command shows "external program as a mes- + sage handler", your NetHack has been compiled with the capability. + When compiling NetHack from source on Linux and other POSIX systems, + define MSGHANDLER to enable it. To use the capability, set the envi- + ronment variable NETHACK_MSGHANDLER to an executable, which will be + executed with the game message as the program's only parameter. + + The most crucial settings to make the game more accessible are: + symset:plain Load a symbol set appropriate for use by blind players. @@ -6746,8 +6757,8 @@ Show menus on a cleared screen and aligned to the left edge. number_pad - A lot of speech access programs use the number-pad to review the - screen. If this is the case, disable the number_pad option and use + A lot of speech access programs use the number-pad to review the + screen. If this is the case, disable the number_pad option and use the traditional Rogue-like commands. paranoid_confirmation:swim @@ -6757,11 +6768,11 @@ Adds direction or location information to messages. spot_monsters - Shows a message when hero notices a monster; combine with accessi- + Shows a message when hero notices a monster; combine with accessi- blemsg. mon_movement - Shows a message when hero notices a monster movement; combine with + Shows a message when hero notices a monster movement; combine with spot_monsters and accessiblemsg. autodescribe @@ -6771,15 +6782,28 @@ Give feedback messages when interesting map locations change. mention_walls - Give feedback messages when walking towards a wall or when travel + Give feedback messages when walking towards a wall or when travel command was interrupted. whatis_coord:compass - When targeting with cursor, describe the cursor position with coor- + When targeting with cursor, describe the cursor position with coor- dinates relative to your character. + + + + NetHack 3.7.0 December 05, 2025 + + + + + + NetHack Guidebook 104 + + + whatis_filter:area - When targeting with cursor, filter possible locations so only those + When targeting with cursor, filter possible locations so only those in the same area (eg. same room, or same corridor) are considered. whatis_moveskip @@ -6791,17 +6815,6 @@ your screen-reader reads those lines. The same information can be seen via the "#attributes" command. - - NetHack 3.7.0 October 24, 2025 - - - - - - NetHack Guidebook 104 - - - showdamage Give a message of damage taken and how many hit points are left. @@ -6842,6 +6855,19 @@ SEDUCE = 0 or 1 to disable or enable, respectively, the SEDUCE option. When disabled, incubi and succubi behave like nymphs. + + + + NetHack 3.7.0 December 05, 2025 + + + + + + NetHack Guidebook 105 + + + CHECK_PLNAME = Setting this to 1 will make the EXPLORERS, WIZARDS, and SHELLERS check for the player name instead of the user's login name. @@ -6856,40 +6882,28 @@ ENTRYMAX = Maximum number of entries in the score file. - - - NetHack 3.7.0 October 24, 2025 - - - - - - NetHack Guidebook 105 - - - - POINTSMIN = Minimum number of points to get an entry in the score + POINTSMIN = Minimum number of points to get an entry in the score file. - PERS_IS_UID = 0 or 1 to use user names or numeric userids, respec- + PERS_IS_UID = 0 or 1 to use user names or numeric userids, respec- tively, to identify unique people for the score file. - HIDEUSAGE = 0 or 1 to control whether the help menu entry for com- + HIDEUSAGE = 0 or 1 to control whether the help menu entry for com- mand line usage is shown or suppressed. - MAX_STATUENAME_RANK = Maximum number of score file entries to use + MAX_STATUENAME_RANK = Maximum number of score file entries to use for random statue names (default is 10). ACCESSIBILITY = 0 or 1 to disable or enable, respectively, the abil- ity for players to set S_pet_override and S_hero_override symbols in their configuration file. - PORTABLE_DEVICE_PATHS = 0 or 1 Windows OS only, the game will look - for all of its external files, and write to all of its output files + PORTABLE_DEVICE_PATHS = 0 or 1 Windows OS only, the game will look + for all of its external files, and write to all of its output files in one place rather than at the standard locations. - DUMPLOGFILE = A filename where the end-of-game dumplog is saved. - Not defining this will prevent dumplog from being created. Only + DUMPLOGFILE = A filename where the end-of-game dumplog is saved. + Not defining this will prevent dumplog from being created. Only available if your game is compiled with DUMPLOG. Allows the follow- ing placeholders: @@ -6908,23 +6922,9 @@ panying the program contains a comment which lists the meaning of the various bits used. Intended for server systems supporting simultaneous play by multiple players (to be clear, each one running - a separate single player game), for displaying their game progress - to observers. Only relevant if the program was built with LIVELOG - enabled. When available, it should be left commented out on single - player installations because over time the file could grow to be - extremely large unless it is actively maintained. - - CRASHREPORTURL = If set to - https://www.nethack.org/links/cr-37BETA.html and support is compiled - in, brings up a browser window pre-populated with the information - needed to report a problem if the game panics or ends up in an - internally inconsistent state, or if the #bugreport command is - invoked. - - - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -6934,6 +6934,19 @@ + a separate single player game), for displaying their game progress + to observers. Only relevant if the program was built with LIVELOG + enabled. When available, it should be left commented out on single + player installations because over time the file could grow to be + extremely large unless it is actively maintained. + + CRASHREPORTURL = If set to + https://www.nethack.org/links/cr-37BETA.html and support is compiled + in, brings up a browser window pre-populated with the information + needed to report a problem if the game panics or ends up in an + internally inconsistent state, or if the #bugreport command is + invoked. + 10. Scoring NetHack maintains a list of the top scores or scorers on your @@ -6966,13 +6979,27 @@ paltry cost of not getting on the high score list. There are two ways of enabling explore mode. One is to start the - game with the -X command-line switch or with the playmode:explore - option. The other is to issue the "#exploremode" extended command - while already playing the game. Starting a new game in explore mode - provides your character with a wand of wishing in initial inventory; - switching during play does not. The other benefits of explore mode + game with the -X command-line switch or with the playmode:explore + option. The other is to issue the "#exploremode" extended command + while already playing the game. Starting a new game in explore mode + provides your character with a wand of wishing in initial inventory; + switching during play does not. The other benefits of explore mode are left for the trepid reader to discover. + + + + + NetHack 3.7.0 December 05, 2025 + + + + + + NetHack Guidebook 107 + + + 11.1. Debug mode Debug mode, also known as wizard mode, is undocumented aside from @@ -6985,27 +7012,15 @@ option. For some systems, the player must be logged in under a particular - user name to be allowed to use debug mode; for others, the hero must - be given a particular character name (but may be any role; there's no - connection between "wizard mode" and the Wizard role). Attempting to - - - NetHack 3.7.0 October 24, 2025 - - - - - - NetHack Guidebook 107 - - - - start a game in debug mode when not allowed or not available will + user name to be allowed to use debug mode; for others, the hero must + be given a particular character name (but may be any role; there's no + connection between "wizard mode" and the Wizard role). Attempting to + start a game in debug mode when not allowed or not available will result in falling back to explore mode instead. 12. Credits - The original hack game was modeled on the Berkeley UNIX rogue + The original hack game was modeled on the Berkeley UNIX rogue game. Large portions of this document were shamelessly cribbed from A Guide to the Dungeons of Doom, by Michael C. Toy and Kenneth C. R. C. Arnold. Small portions were adapted from Further Exploration of the @@ -7039,24 +7054,9 @@ Mike Stephenson merged these various versions back together, incorporating many of the added features, and produced NetHack version 1.4 in 1987. He then coordinated a cast of thousands in enhancing and - debugging NetHack 1.4 and released NetHack versions 2.2 and 2.3. Like - Hack, they were released by posting their source code to Usenet where - they remained available in various archives accessible via ftp and - uucp after expiring from the newsgroup. - - Later, Mike coordinated a major re-write of the game, heading a - team which included Ken Arromdee, Jean-Christophe Collet, Steve Creps, - Eric Hendrickson, Izchak Miller, Eric S. Raymond, John Rupley, Mike - Threepoint, and Janet Walz, to produce NetHack 3.0c. - - NetHack 3.0 was ported to the Atari by Eric R. Smith, to OS/2 by - Timo Hakulinen, and to VMS by David Gentzel. The three of them and - Kevin Darcy later joined the main NetHack Development Team to produce - subsequent revisions of 3.0. - - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -7066,6 +7066,21 @@ + debugging NetHack 1.4 and released NetHack versions 2.2 and 2.3. Like + Hack, they were released by posting their source code to Usenet where + they remained available in various archives accessible via ftp and + uucp after expiring from the newsgroup. + + Later, Mike coordinated a major re-write of the game, heading a + team which included Ken Arromdee, Jean-Christophe Collet, Steve Creps, + Eric Hendrickson, Izchak Miller, Eric S. Raymond, John Rupley, Mike + Threepoint, and Janet Walz, to produce NetHack 3.0c. + + NetHack 3.0 was ported to the Atari by Eric R. Smith, to OS/2 by + Timo Hakulinen, and to VMS by David Gentzel. The three of them and + Kevin Darcy later joined the main NetHack Development Team to produce + subsequent revisions of 3.0. + Olaf Seibert ported NetHack 2.3 and 3.0 to the Amiga. Norm Meluch, Stephen Spackman and Pierre Martineau designed overlay code for PC NetHack 3.0. Johnny Lee ported NetHack 3.0 to the Macintosh. @@ -7074,7 +7089,7 @@ Version 3.0 went through ten relatively rapidly released "patch- level" revisions. Versions at the time were known as 3.0 for the base - release and variously as "3.0a" through "3.0j", "3.0 patchlevel 1" + release and variously as "3.0a" through "3.0j", "3.0 patchlevel 1" through "3.0 patchlevel 10", or "3.0pl1" through "3.0pl10" rather than 3.0.0 and 3.0.1 through 3.0.10; the three component numbering scheme began to be used with 3.1.0. @@ -7090,11 +7105,11 @@ NetHack 3.1. Version 3.1.0 was released in January of 1993. Ken Lorber, Gregg Wonderly and Greg Olson, with help from Richard - Addison, Mike Passaretti, and Olaf Seibert, developed NetHack 3.1 for + Addison, Mike Passaretti, and Olaf Seibert, developed NetHack 3.1 for the Amiga. - Norm Meluch and Kevin Smolkowski, with help from Carl Schelin, - Stephen Spackman, Steve VanDevender, and Paul Winner, ported NetHack + Norm Meluch and Kevin Smolkowski, with help from Carl Schelin, + Stephen Spackman, Steve VanDevender, and Paul Winner, ported NetHack 3.1 to the PC. Jon W{tte and Hao-yang Wang, with help from Ross Brown, Mike Eng- @@ -7105,24 +7120,9 @@ Timo Hakulinen ported NetHack 3.1 to OS/2. Eric Smith ported NetHack 3.1 to the Atari. Pat Rankin, with help from Joshua - Delahunty, was responsible for the VMS version of NetHack 3.1. - Michael Allison ported NetHack 3.1 to Windows NT. - - Dean Luick, with help from David Cohrs, developed NetHack 3.1 for - X11. It drew the map as text rather than graphically but included - nh10.bdf, an optionally used custom X11 font which has tiny images in - place of letters and punctuation, a precursor of tiles. Those images - don't extend to individual monster and object types, just replacements - for monster and object classes (so one custom image for all "a" - insects and another for all "[" armor and so forth, not separate - images for beetles and ants or for cloaks and boots). - - Warwick Allison wrote a graphically displayed version of NetHack - for the Atari where the tiny pictures were described as "icons" and - were distinct for specific types of monsters and objects rather than - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -7132,6 +7132,21 @@ + Delahunty, was responsible for the VMS version of NetHack 3.1. + Michael Allison ported NetHack 3.1 to Windows NT. + + Dean Luick, with help from David Cohrs, developed NetHack 3.1 for + X11. It drew the map as text rather than graphically but included + nh10.bdf, an optionally used custom X11 font which has tiny images in + place of letters and punctuation, a precursor of tiles. Those images + don't extend to individual monster and object types, just replacements + for monster and object classes (so one custom image for all "a" + insects and another for all "[" armor and so forth, not separate + images for beetles and ants or for cloaks and boots). + + Warwick Allison wrote a graphically displayed version of NetHack + for the Atari where the tiny pictures were described as "icons" and + were distinct for specific types of monsters and objects rather than just their classes. He contributed them to the NetHack Development Team which rechristened them "tiles", original usage which has subse- quently been picked up by various other games. NetHack's tiles sup- @@ -7146,19 +7161,19 @@ Version 3.2 marked the tenth anniversary of the formation of the development team. In a testament to their dedication to the game, all - thirteen members of the original NetHack Development Team remained on - the team at the start of work on that release. During the interval + thirteen members of the original NetHack Development Team remained on + the team at the start of work on that release. During the interval between the release of 3.1.3 and 3.2.0, one of the founding members of the NetHack Development Team, Dr. Izchak Miller, was diagnosed with cancer and passed away. That release of the game was dedicated to him by the development and porting teams. - Version 3.2 proved to be more stable than previous versions. - Many bugs were fixed, abuses eliminated, and game features tuned for + Version 3.2 proved to be more stable than previous versions. + Many bugs were fixed, abuses eliminated, and game features tuned for better game play. - During the lifespan of NetHack 3.1 and 3.2, several enthusiasts - of the game added their own modifications to the game and made these + During the lifespan of NetHack 3.1 and 3.2, several enthusiasts + of the game added their own modifications to the game and made these "variants" publicly available: Tom Proudfoot and Yuval Oren created NetHack++, which was quickly @@ -7171,6 +7186,18 @@ Warren Cheung combined SLASH with the Wizard Patch to produce Slash'EM, and with the help of Kevin Hugo, added more features. Kevin + + + NetHack 3.7.0 December 05, 2025 + + + + + + NetHack Guidebook 110 + + + later joined the NetHack Development Team and incorporated the best of these ideas into NetHack 3.3. @@ -7181,23 +7208,11 @@ systems that usually had such. (To anyone considering resurrecting an old version: all versions - before 3.2.3 had a Y2K bug. The high scores file and the log file - contained dates which were formatted using a two-digit year, and - 1999's year 99 was followed by 2000's year 100. That got written out - successfully but it unintentionally introduced an extra column in the + before 3.2.3 had a Y2K bug. The high scores file and the log file + contained dates which were formatted using a two-digit year, and + 1999's year 99 was followed by 2000's year 100. That got written out + successfully but it unintentionally introduced an extra column in the file layout which prevented score entries from being read back in cor- - - - NetHack 3.7.0 October 24, 2025 - - - - - - NetHack Guidebook 110 - - - rectly, interfering with insertion of new high scores and with retrieval of old character names to use for random ghost and statue names in the current game.) @@ -7205,7 +7220,7 @@ The 3.3 NetHack Development Team, consisting of Michael Allison, Ken Arromdee, David Cohrs, Jessie Collet, Steve Creps, Kevin Darcy, Timo Hakulinen, Kevin Hugo, Steve Linhart, Ken Lorber, Dean Luick, Pat - Rankin, Eric Smith, Mike Stephenson, Janet Walz, and Paul Winner, + Rankin, Eric Smith, Mike Stephenson, Janet Walz, and Paul Winner, released 3.3.0 in December 1999 and 3.3.1 in August of 2000. Version 3.3 offered many firsts. It was the first version to sep- @@ -7238,6 +7253,17 @@ Dean Luick, Mark Modrall, and Kevin Hugo maintained and enhanced the Macintosh port of 3.4. + + NetHack 3.7.0 December 05, 2025 + + + + + + NetHack Guidebook 111 + + + Michael Allison, David Cohrs, Alex Kompel, Dion Nicolaas, and Yitzhak Sapir maintained and enhanced 3.4 for the Microsoft Windows platform. Alex Kompel contributed a new graphical interface for the @@ -7252,24 +7278,12 @@ Janne Salmijarvi and Teemu Suikki maintained and enhanced the Amiga port of 3.4 after Janne Salmijarvi resurrected it for 3.3.1. - - - NetHack 3.7.0 October 24, 2025 - - - - - - NetHack Guidebook 111 - - - Christian "Marvin" Bressler maintained 3.4 for the Atari after he resurrected it for 3.3.1. - The release of NetHack 3.4.3 in December 2003 marked the begin- - ning of a long release hiatus. 3.4.3 proved to be a remarkably stable - version that provided continued enjoyment by the community for more + The release of NetHack 3.4.3 in December 2003 marked the begin- + ning of a long release hiatus. 3.4.3 proved to be a remarkably stable + version that provided continued enjoyment by the community for more than a decade. The NetHack Development Team slowly and quietly contin- ued to work on the game behind the scenes during the tenure of 3.4.3. It was during that same period that several new variants emerged @@ -7280,8 +7294,8 @@ enjoyed by the community to this day. In September 2014, an interim snapshot of the code under develop- - ment was released publicly by other parties. Since that code was a - work-in-progress and had not gone through the process of debugging it + ment was released publicly by other parties. Since that code was a + work-in-progress and had not gone through the process of debugging it as a suitable release, it was decided that the version numbers present on that code snapshot would be retired and never used in an official NetHack release. An announcement was posted on the NetHack Develop- @@ -7304,23 +7318,9 @@ game, author Terry Pratchett, passed away. NetHack 3.6.0 introduced a tribute to him. - 3.6.0 was released in December 2015, and merged work done by the - development team since the release of 3.4.3 with some of the beloved - community patches. Many bugs were fixed and some code was restruc- - tured. - - The NetHack Development Team, as well as Steve VanDevender and - Kevin Smolkowski, ensured that NetHack 3.6 continued to operate on - various UNIX flavors and maintained the X11 interface. - - Ken Lorber, Haoyang Wang, Pat Rankin, and Dean Luick maintained - the port of NetHack 3.6 for MacOS. - - Michael Allison, David Cohrs, Bart House, Pasi Kallinen, Alex - Kompel, Dion Nicolaas, Derek S. Ray and Yitzhak Sapir maintained the - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -7330,22 +7330,36 @@ + 3.6.0 was released in December 2015, and merged work done by the + development team since the release of 3.4.3 with some of the beloved + community patches. Many bugs were fixed and some code was restruc- + tured. + + The NetHack Development Team, as well as Steve VanDevender and + Kevin Smolkowski, ensured that NetHack 3.6 continued to operate on + various UNIX flavors and maintained the X11 interface. + + Ken Lorber, Haoyang Wang, Pat Rankin, and Dean Luick maintained + the port of NetHack 3.6 for MacOS. + + Michael Allison, David Cohrs, Bart House, Pasi Kallinen, Alex + Kompel, Dion Nicolaas, Derek S. Ray and Yitzhak Sapir maintained the port of NetHack 3.6 for Microsoft Windows. - Pat Rankin attempted to keep the VMS port running for NetHack - 3.6, hindered by limited access. Kevin Smolkowski has updated and - tested it for the most recent version of OpenVMS (V8.4 as of this + Pat Rankin attempted to keep the VMS port running for NetHack + 3.6, hindered by limited access. Kevin Smolkowski has updated and + tested it for the most recent version of OpenVMS (V8.4 as of this writing) on Alpha and Integrity (aka Itanium aka IA64) but not VAX. - Ray Chason resurrected the MS-DOS port for 3.6 and contributed + Ray Chason resurrected the MS-DOS port for 3.6 and contributed the necessary updates to the community at large. - In late April 2018, several hundred bug fixes for 3.6.0 and some - new features were assembled and released as NetHack 3.6.1. The - NetHack Development Team at the time of release of 3.6.1 consisted of - Warwick Allison, Michael Allison, Ken Arromdee, David Cohrs, Jessie - Collet, Pasi Kallinen, Ken Lorber, Dean Luick, Patric Mueller, Pat - Rankin, Derek S. Ray, Alex Smith, Mike Stephenson, Janet Walz, and + In late April 2018, several hundred bug fixes for 3.6.0 and some + new features were assembled and released as NetHack 3.6.1. The + NetHack Development Team at the time of release of 3.6.1 consisted of + Warwick Allison, Michael Allison, Ken Arromdee, David Cohrs, Jessie + Collet, Pasi Kallinen, Ken Lorber, Dean Luick, Patric Mueller, Pat + Rankin, Derek S. Ray, Alex Smith, Mike Stephenson, Janet Walz, and Paul Winner. In early May 2019, another 320 bug fixes along with some enhance- @@ -7367,10 +7381,22 @@ NetHack 3.6.6 was released on March 8, 2020 containing a security fix and some bug fixes. - NetHack 3.6.7 was released on February 16, 2023 containing a + NetHack 3.6.7 was released on February 16, 2023 containing a security fix and some bug fixes. - The official NetHack web site is maintained by Ken Lorber at + + + NetHack 3.7.0 December 05, 2025 + + + + + + NetHack Guidebook 113 + + + + The official NetHack web site is maintained by Ken Lorber at https://www.nethack.org/. @@ -7386,73 +7412,47 @@ - NetHack 3.7.0 October 24, 2025 - NetHack Guidebook 113 + + + + + + + + + + + + + + + + + + + 12.1. Special Thanks - On behalf of the NetHack community, thank you very much once - again to M. Drew Streib and Pasi Kallinen for providing a public - NetHack server at nethack.alt.org. Thanks to Keith Simpson and Andy - Thomson for hardfought.org. Thanks to all those unnamed dungeoneers - who invest their time and effort into annual NetHack tournaments such - as Junethack, The November NetHack Tournament, and in days past, + On behalf of the NetHack community, thank you very much once + again to M. Drew Streib and Pasi Kallinen for providing a public + NetHack server at nethack.alt.org. Thanks to Keith Simpson and Andy + Thomson for hardfought.org. Thanks to all those unnamed dungeoneers + who invest their time and effort into annual NetHack tournaments such + as Junethack, The November NetHack Tournament, and in days past, devnull.net (gone for now, but not forgotten). - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 12.2. Dungeoneers - - From time to time, some depraved individual out there in netland - sends a particularly intriguing modification to help out with the - - - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -7462,7 +7462,11 @@ - game. The NetHack Development Team sometimes makes note of the names + 12.2. Dungeoneers + + From time to time, some depraved individual out there in netland + sends a particularly intriguing modification to help out with the + game. The NetHack Development Team sometimes makes note of the names of the worst of these miscreants in this, the list of Dungeoneers: @@ -7511,14 +7515,10 @@ Andy Church Jeff Bailey Pasi Kallinen Andy Swanson Jochen Erwied Pat Rankin Andy Thomson John Kallen Patric Mueller - Ari Huttunen John Rupley Paul Winner - Bart House John S. Bien Pierre Martineau - Benson I. Margulies Johnny Lee Ralf Brown - Bill Dyer Jon W{tte Ray Chason - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -7528,6 +7528,10 @@ + Ari Huttunen John Rupley Paul Winner + Bart House John S. Bien Pierre Martineau + Benson I. Margulies Johnny Lee Ralf Brown + Bill Dyer Jon W{tte Ray Chason Boudewijn Waijers Jonathan Handler Richard Addison Bruce Cox Joshua Delahunty Richard Beigel Bruce Holloway Karl Garrison Richard P. Hughey @@ -7580,11 +7584,7 @@ - - - - - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 @@ -7594,7 +7594,7 @@ - Brand and product names are trademarks or registered trademarks + Brand and product names are trademarks or registered trademarks of their respective holders. @@ -7650,7 +7650,7 @@ - NetHack 3.7.0 October 24, 2025 + NetHack 3.7.0 December 05, 2025 From b1329137eb010b4cbe33bf2699d43abcd68e9033 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 6 Dec 2025 15:55:06 -0500 Subject: [PATCH 142/442] update commit for submodules/pdcursesmod --- submodules/pdcursesmod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/pdcursesmod b/submodules/pdcursesmod index dd794f6c9..5a2554607 160000 --- a/submodules/pdcursesmod +++ b/submodules/pdcursesmod @@ -1 +1 @@ -Subproject commit dd794f6c98b6d3d6acbe0910c754559177fb56a5 +Subproject commit 5a255460769fd70067ee46989e85570729ceeae8 From 68aaa5a3ae1ddb23289f53d11c29d5e636607c6d Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 9 Dec 2025 13:47:11 -0500 Subject: [PATCH 143/442] call update_inventory() after #name inventory obj Resolves #1470 --- src/do_name.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/do_name.c b/src/do_name.c index 946fbdd4d..1ce7dde16 100644 --- a/src/do_name.c +++ b/src/do_name.c @@ -671,6 +671,8 @@ docall(struct obj *obj) *uname_p = dupstr(buf); discover_object(obj->otyp, FALSE, TRUE, TRUE); /* possibly add to disco[] */ } + if (obj->where == OBJ_INVENT) + update_inventory(); } staticfn void From 6ba053669e4a40864fcf62147fe9cb06b0dd5e83 Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 9 Dec 2025 14:17:42 -0500 Subject: [PATCH 144/442] follow-up for #name permanent inventory update Using extended #name for an object on the floor (for example) wasn't updating the permanent inventory to reflect the updated object type name if there was also one in inventory. --- include/extern.h | 1 + src/do_name.c | 2 +- src/invent.c | 12 ++++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/include/extern.h b/include/extern.h index 8a5056d91..4ac5b3b1e 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1360,6 +1360,7 @@ extern void perm_invent_toggled(boolean negated); extern void prepare_perminvent(winid window); extern struct obj *carrying_stoning_corpse(void); extern void repopulate_perminvent(void); +extern boolean hero_has_one_of_these(short); /* ### ioctl.c ### */ diff --git a/src/do_name.c b/src/do_name.c index 1ce7dde16..746f99959 100644 --- a/src/do_name.c +++ b/src/do_name.c @@ -671,7 +671,7 @@ docall(struct obj *obj) *uname_p = dupstr(buf); discover_object(obj->otyp, FALSE, TRUE, TRUE); /* possibly add to disco[] */ } - if (obj->where == OBJ_INVENT) + if (obj->where == OBJ_INVENT || hero_has_one_of_these(obj->otyp)) update_inventory(); } diff --git a/src/invent.c b/src/invent.c index f3c7f3803..2983b2997 100644 --- a/src/invent.c +++ b/src/invent.c @@ -2779,6 +2779,18 @@ learn_unseen_invent(void) update_inventory(); } +boolean +hero_has_one_of_these(short otyp) +{ + struct obj *otmp; + + for (otmp = gi.invent; otmp; otmp = otmp->nobj) { + if (otmp->otyp == otyp) + return TRUE; + } + return FALSE; +} + /* persistent inventory window is maintained by interface code; 'update_inventory' used to be a macro for (*windowprocs.win_update_inventory) but the restore hackery to suppress From 7dc4512bb35cddda5c972f6358be0ad543e26d1a Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 9 Dec 2025 15:25:10 -0500 Subject: [PATCH 145/442] follow-up: just use existing carrying() --- include/extern.h | 1 - src/do_name.c | 2 +- src/invent.c | 12 ------------ 3 files changed, 1 insertion(+), 14 deletions(-) diff --git a/include/extern.h b/include/extern.h index 4ac5b3b1e..8a5056d91 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1360,7 +1360,6 @@ extern void perm_invent_toggled(boolean negated); extern void prepare_perminvent(winid window); extern struct obj *carrying_stoning_corpse(void); extern void repopulate_perminvent(void); -extern boolean hero_has_one_of_these(short); /* ### ioctl.c ### */ diff --git a/src/do_name.c b/src/do_name.c index 746f99959..c27396343 100644 --- a/src/do_name.c +++ b/src/do_name.c @@ -671,7 +671,7 @@ docall(struct obj *obj) *uname_p = dupstr(buf); discover_object(obj->otyp, FALSE, TRUE, TRUE); /* possibly add to disco[] */ } - if (obj->where == OBJ_INVENT || hero_has_one_of_these(obj->otyp)) + if (obj->where == OBJ_INVENT || carrying(obj->otyp)) update_inventory(); } diff --git a/src/invent.c b/src/invent.c index 2983b2997..f3c7f3803 100644 --- a/src/invent.c +++ b/src/invent.c @@ -2779,18 +2779,6 @@ learn_unseen_invent(void) update_inventory(); } -boolean -hero_has_one_of_these(short otyp) -{ - struct obj *otmp; - - for (otmp = gi.invent; otmp; otmp = otmp->nobj) { - if (otmp->otyp == otyp) - return TRUE; - } - return FALSE; -} - /* persistent inventory window is maintained by interface code; 'update_inventory' used to be a macro for (*windowprocs.win_update_inventory) but the restore hackery to suppress From bc13a42ce319f4fdc11abe97921e82e97041955d Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 9 Dec 2025 17:24:33 -0800 Subject: [PATCH 146/442] fix issue #1469 - glitches with type-naming Issue reported by ars3niy: assigning names to types of objects, or clearing such, did not update persistent inventory window. Also, the sequence assign-a-name, name-as-' '-to-unname, assign-a-name again, unname again would result in impossible: "named object not in disco". This fixes the impossibility. The fix for #1470 has already taken care of the presistent inventory issue. Fixes #1469 --- doc/fixes3-7-0.txt | 3 +++ src/o_init.c | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index f7998fdc4..ae91aa08c 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -2168,6 +2168,9 @@ livelog/#chronicle for bribing a demon lord reported random monster and livelog/#chronicle for saving-grace: if saving-grace prevents hero's death, report it [at present, it uses the livelog classification for breaking a conduct] +naming a type of item, unnaming it (with name of ), naming it again + (new name or re-use of old one), then unnaming it again would issue + impossible: "named object not in disco" Fixes to 3.7.0-x Platform and/or Interface Problems Exposed Via git Repository diff --git a/src/o_init.c b/src/o_init.c index 06b88e57e..d53ee1ecd 100644 --- a/src/o_init.c +++ b/src/o_init.c @@ -488,7 +488,7 @@ discover_object( void undiscover_object(int oindx) { - if (!objects[oindx].oc_name_known) { + if (!objects[oindx].oc_name_known && !objects[oindx].oc_encountered) { int dindx, acls = objects[oindx].oc_class; boolean found = FALSE; @@ -510,7 +510,6 @@ undiscover_object(int oindx) if (objects[oindx].oc_class == GEM_CLASS) gem_learned(oindx); /* ok, it's actually been unlearned */ - update_inventory(); } } From 12bd63a3a67ca2db04811507c39f463c73fcad78 Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 9 Dec 2025 17:26:11 -0800 Subject: [PATCH 147/442] tweak mail daemon vs deaf hero Turn an instance of "Never mind" into a sentence. Unlike various others, this one isn't a direct result of something the player has initiated. --- src/mail.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mail.c b/src/mail.c index 25572f029..96735abe5 100644 --- a/src/mail.c +++ b/src/mail.c @@ -372,7 +372,7 @@ md_rush(struct monst *md, SetVoice(md, 0, 80, 0); verbalize("This place's too crowded. I'm outta here."); } else { - pline1(Never_mind); + pline("%s.", Never_mind); } remove_monster(fx, fy); From 99c888f3930becd12c97219b13430a2d26b09873 Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 10 Dec 2025 07:21:47 -0500 Subject: [PATCH 148/442] update tested versions of Visual Studio 2025-12-10 --- sys/windows/Makefile.nmake | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index 7977841ce..597f0ae2c 100644 --- a/sys/windows/Makefile.nmake +++ b/sys/windows/Makefile.nmake @@ -9,7 +9,7 @@ # # Visual Studio Compilers Tested: # - Microsoft Visual Studio Community 2022 v 17.14.21 -# - Microsoft Visual Studio Community 2026 v 18.0.2 +# - Microsoft Visual Studio Community 2026 v 18.1.0 # #============================================================================== # This is used for building two distinct executables of NetHack: @@ -1185,8 +1185,7 @@ rc=Rc.exe # # Recently tested versions: TESTEDVS2022 = 14.44.35221.0 -# Other versions: -TESTEDVS2026 = 14.50.35719.0 +TESTEDVS2026 = 14.50.35720.0 VS20261ST = 1450000000 VS2026CUR = $(TESTEDVS2026:.=) From 842c595dcf692a36175af04f5e90c90d7de6089e Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 11 Dec 2025 21:37:57 -0800 Subject: [PATCH 149/442] Guidebook.mn fix Fix the font manipulation bug that Keni pointed out. The \fP that is next-to-last could be eliminated but I've left it, at least for now. --- doc/Guidebook.mn | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index ac9fc70b5..aa3c5dc2e 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -5564,7 +5564,9 @@ change the color or appearance of fields in the status display. .pg The format for defining status colors is: .SD n -\f(CROPTION=hilite_status:\fIfield-name\fP/\fIbehavior\fP/\fIcolor\fP&\fIattributes\fP\fP +.\" was "\f(CR...\fI...\fP\fP" but font changes don't nest so final \fP didn't +.\" restore the pre-\f(CR font; assume that was Roman and explicitly use \fR +\f(CROPTION=hilite_status:\fIfield-name\fP/\fIbehavior\fP/\fIcolor\fP&\fIattributes\fP\fR .ED .pg For example, the following line in your configuration file will cause From 020abc9cbcea10d48a55b1afbf5bc02cdade3aaa Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 11 Dec 2025 23:11:35 -0800 Subject: [PATCH 150/442] partial fix for #K4317 - monster grudge behavior A band-aid for monster-vs-monster aggression. Prevent monsters in the Wizard's tower from attacking each other unless the hero is inside the tower too, and those outside the tower from attacking each unless the hero is outside. --- src/mon.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/mon.c b/src/mon.c index 1536f460b..bc5868e31 100644 --- a/src/mon.c +++ b/src/mon.c @@ -2359,23 +2359,34 @@ mfndpos( staticfn long mm_2way_aggression(struct monst *magr, struct monst *mdef) { + if (On_W_tower_level(&u.uz)) { + /* treat inside the Wizard's tower as if it were a separate level + from outside so when hero is inside Wizard's tower, both monsters + need to be too; when outside, the monsters need to be too */ + if (In_W_tower(u.ux, u.uy, &u.uz) + ? (!In_W_tower(magr->mx, magr->my, &u.uz) + || !In_W_tower(mdef->mx, mdef->my, &u.uz)) + : (In_W_tower(magr->mx, magr->my, &u.uz) + || In_W_tower(mdef->mx, mdef->my, &u.uz))) + return 0L; + } /* liches/zombies vs things that can be zombified Note: avoid this on the Castle level, partly for balance reasons (the monster-versus-monster fights clear out significant portions of the Castle and make it easier than it should be), partly for flavor reasons (monsters who attacked other monsters to zombify them would - have been counterattacked to death long before the hero arried). + have been counterattacked to death long before the hero arrived). Also don't include unique monsters in this, otherwise it leads to them waking up early (e.g. because a zombie decided to attack the Wizard of Yendor). */ - if (zombie_maker(magr) && zombie_form(mdef->data) != NON_PM) - if (!Is_stronghold(&u.uz) && - !unique_corpstat(magr->data) && !unique_corpstat(mdef->data)) + if (zombie_maker(magr) && zombie_form(mdef->data) != NON_PM) { + if (!Is_stronghold(&u.uz) + && !unique_corpstat(magr->data) && !unique_corpstat(mdef->data)) return (ALLOW_M | ALLOW_TM); - - return 0; + } + return 0L; } /* Monster against monster special attacks; for the specified monster @@ -2392,7 +2403,7 @@ mm_aggression( /* don't allow pets to fight each other */ if (magr->mtame && mdef->mtame) - return 0; + return 0L; /* supposedly purple worms are attracted to shrieking because they like to eat shriekers, so attack the latter when feasible */ From e68c5826d3ae34fe607c767ad47762609eb66af6 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Fri, 12 Dec 2025 17:44:59 +0200 Subject: [PATCH 151/442] Remove extra space --- src/allmain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/allmain.c b/src/allmain.c index 710063045..7a431ea27 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -756,7 +756,7 @@ init_sound_disp_gamewindows(void) if (iflags.perm_invent_pending) check_perm_invent_again(); #endif - } +} void newgame(void) From b92cccbbcd4f57421e2714006cb68fadb325157b Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Fri, 12 Dec 2025 17:57:58 +0200 Subject: [PATCH 152/442] A line added, a space removed --- src/glyphs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/glyphs.c b/src/glyphs.c index d92bef9fc..bf8edf314 100644 --- a/src/glyphs.c +++ b/src/glyphs.c @@ -790,6 +790,7 @@ purge_custom_entries(enum graphics_sets which_set) gdc->count = 0; } } + void dump_all_glyphids(FILE *fp) { @@ -815,7 +816,7 @@ wizcustom_glyphids(winid win) wizcustom_callback(win, glyphnum, id); } } - } +} staticfn int parse_id( From 758528f0941017e11b84c5958c199e30d77da548 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 14 Dec 2025 12:00:26 +0200 Subject: [PATCH 153/442] Grimtooth and Sunsword invoke can be paid with magic power If the invoke timeout hasn't run out, the effect will take 25 pw, the same as a lvl 5 spell. If hero doesn't have that much, then the invoke fails. --- doc/fixes3-7-0.txt | 1 + src/artifact.c | 55 ++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index ae91aa08c..586e42a63 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1510,6 +1510,7 @@ if a tree and a boulder or statue were at the same location, applying an axe would break the boulder or statue rather than chop the tree avoid premapping outside Sokoban map to prevent showing stone glyphs Grimtooth is permanently poisoned, protects from poison, and can fling poison +invoke cost for Grimtooth and Sunsword can be paid with magic power cursed magic whistle can teleport you to your pet Fire and Frost Brand can be invoked for expert level fireball or cone of cold wielding Trollsbane grants hungerless regeneration diff --git a/src/artifact.c b/src/artifact.c index 11db684f0..e0e3df173 100644 --- a/src/artifact.c +++ b/src/artifact.c @@ -40,6 +40,8 @@ staticfn int invoke_banish(struct obj *) NONNULLARG1; staticfn int invoke_fling_poison(struct obj *) NONNULLARG1; staticfn int invoke_storm_spell(struct obj *) NONNULLARG1; staticfn int invoke_blinding_ray(struct obj *) NONNULLARG1; +staticfn int arti_invoke_cost_pw(struct obj *) NONNULLARG1; +staticfn boolean arti_invoke_cost(struct obj *) NONNULLARG1; staticfn int arti_invoke(struct obj *); staticfn boolean Mb_hit(struct monst * magr, struct monst *mdef, struct obj *, int *, int, boolean, char *); @@ -2083,6 +2085,48 @@ invoke_blinding_ray(struct obj *obj) return ECMD_TIME; } +/* return the amount of Pw invoking an object costs. + return a negative value, if obj invoking cannot be paid with Pw */ +staticfn int +arti_invoke_cost_pw(struct obj *obj) +{ + const struct artifact *oart = get_artifact(obj); + + if (oart->inv_prop == FLING_POISON + || oart->inv_prop == BLINDING_RAY) { + /* pretend it's a level 5 spell */ + return SPELL_LEV_PW(5); + } + + return -1; +} + +/* return TRUE if artifact object's invoke cost can be paid (and pay it) */ +staticfn boolean +arti_invoke_cost(struct obj *obj) +{ + if (obj->age > svm.moves) { + int pw_cost = arti_invoke_cost_pw(obj); + + if (pw_cost < 0 || u.uen < pw_cost) { + /* the artifact is tired :-) */ + You_feel("that %s %s ignoring you.", the(xname(obj)), + otense(obj, "are")); + /* and just got more so; patience is essential... */ + obj->age += (long) d(3, 10); + return FALSE; + } else { + /* you pay invoke cost with your own magic */ + You_feel("drained..."); + u.uen -= pw_cost; + disp.botl = TRUE; + } + } else { + obj->age = svm.moves + rnz(100); + } + return TRUE; +} + staticfn int arti_invoke(struct obj *obj) { @@ -2102,17 +2146,10 @@ arti_invoke(struct obj *obj) return ECMD_TIME; } + /* It's a special power, not "just" a property */ if (oart->inv_prop > LAST_PROP) { - /* It's a special power, not "just" a property */ - if (obj->age > svm.moves) { - /* the artifact is tired :-) */ - You_feel("that %s %s ignoring you.", the(xname(obj)), - otense(obj, "are")); - /* and just got more so; patience is essential... */ - obj->age += (long) d(3, 10); + if (!arti_invoke_cost(obj)) return ECMD_TIME; - } - obj->age = svm.moves + rnz(100); switch (oart->inv_prop) { case TAMING: res = invoke_taming(obj); break; From 91cc3e3f129ca8448de54ba0ed109d91ead9fe4d Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 14 Dec 2025 20:23:22 +0200 Subject: [PATCH 154/442] Replace a weight magic number --- src/hack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hack.c b/src/hack.c index fb356ab78..c27e86357 100644 --- a/src/hack.c +++ b/src/hack.c @@ -947,7 +947,7 @@ cant_squeeze_thru(struct monst *mon) /* lugging too much junk? */ amt = (mon == &gy.youmonst) ? inv_weight() + weight_cap() : curr_mon_load(mon); - if (amt > 600) + if (amt > WT_TOOMUCH_DIAGONAL) return 2; /* Sokoban restriction applies to hero only */ From 9828a05bbbe165de3a796a80d2e75ce78bcb55b4 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Sun, 21 Dec 2025 10:44:50 +0000 Subject: [PATCH 155/442] Don't treat low monster-form HP while polymorphed as a major trouble Running low on HP in monster form isn't the same sort of critical problem that running low on HP while not polymorphed is, because the character changes back when the monster form HP runs out rather than dying. (Indeed, running out of HP in monster form is often intentional.) The exception is when wearing unchanging (which implies both that the monster form is intentional and that running out of monster HP would be fatal), so low monster-form HP is treated as a major trouble in that case. Inspired by . --- doc/fixes3-7-0.txt | 2 ++ src/pray.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 586e42a63..4263a6007 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1552,6 +1552,8 @@ you cannot sacrifice objects/corpses while stunned or confused that they work; /y and /n for them now only work when lootabc is off writing on an unidentified scroll of blank paper identifies blank paper dumplogs include spells and skills +praying will not restore monster-form HP while polymorphed, unless you + have unchanging Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/pray.c b/src/pray.c index 075711400..281ce64e7 100644 --- a/src/pray.c +++ b/src/pray.c @@ -217,7 +217,7 @@ in_trouble(void) return TROUBLE_STARVING; if (region_danger()) return TROUBLE_REGION; - if (critically_low_hp(FALSE)) + if ((!Upolyd || Unchanging) && critically_low_hp(FALSE)) return TROUBLE_HIT; if (ismnum(u.ulycn)) return TROUBLE_LYCANTHROPE; From 45766db520ec87d8baea2a26a42a758fbfc16819 Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 23 Dec 2025 23:13:08 -0800 Subject: [PATCH 156/442] pull request #1471 - winter wolf cub Pull request by umbire: the list of monsters which had lycanthrope forms includes winter wolf but was missing winter wolf cub. Affects cannibalism check when hero is a werewolf and eats a winter wolf cub corpse. One-line fix entered manually rather than using the git commit. Fixes #1471 --- doc/fixes3-7-0.txt | 1 + src/were.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 4263a6007..6a70bf195 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1554,6 +1554,7 @@ writing on an unidentified scroll of blank paper identifies blank paper dumplogs include spells and skills praying will not restore monster-form HP while polymorphed, unless you have unchanging +winter wolf cub was missing for monster to lycanthrope conversion Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/were.c b/src/were.c index b38bf0ed0..aa65341cb 100644 --- a/src/were.c +++ b/src/were.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 were.c $NHDT-Date: 1717570494 2024/06/05 06:54:54 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.36 $ */ +/* NetHack 3.7 were.c $NHDT-Date: 1766588485 2025/12/24 07:01:25 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.41 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -84,6 +84,7 @@ were_beastie(int pm) case PM_WOLF: case PM_WARG: case PM_WINTER_WOLF: + case PM_WINTER_WOLF_CUB: return PM_WEREWOLF; default: break; From aae7778d663ea5da9ad56ce3f0f0579c53f63fe4 Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 23 Dec 2025 23:28:58 -0800 Subject: [PATCH 157/442] github issue #1472 - elven monster bow bonus Issue reported by Tomsod: monster elves were intended to get a small bonus to to-hit and damage when shooting arrows with bows, but the check for that tested the arrows for skill P_BOW which never matches. It should be -P_BOW. [Pretty minor: +1 to-hit for any bow, another +1 to-hit if elven bow; +1 damage for elven arrow; against hero and against other monsters.] Fixes #1472 --- doc/fixes3-7-0.txt | 2 ++ src/mthrowu.c | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 6a70bf195..8790b59c3 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1555,6 +1555,8 @@ dumplogs include spells and skills praying will not restore monster-form HP while polymorphed, unless you have unchanging winter wolf cub was missing for monster to lycanthrope conversion +monster elves shooting arrows weren't getting intended small to-hit and damage + bonuses Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/mthrowu.c b/src/mthrowu.c index e19ca4ce9..613150656 100644 --- a/src/mthrowu.c +++ b/src/mthrowu.c @@ -698,8 +698,9 @@ m_throw( hitv = 3 - distmin(u.ux, u.uy, mon->mx, mon->my); if (hitv < -4) hitv = -4; + /* [elves get a shooting bonus, orcs don't...] */ if (is_elf(mon->data) - && objects[singleobj->otyp].oc_skill == P_BOW) { + && objects[singleobj->otyp].oc_skill == -P_BOW) { hitv++; if (MON_WEP(mon) && MON_WEP(mon)->otyp == ELVEN_BOW) hitv++; From 5bda026acb39e7246a6cde042adf2b605378b759 Mon Sep 17 00:00:00 2001 From: PatR Date: Wed, 24 Dec 2025 22:11:16 -0800 Subject: [PATCH 158/442] fix issue #1455 - fully thrown quiver stack won't autoquiver when picked back up Issue reported by ars3niy: empty quiver used to be refilled when picking a thrown item or stack up. Bug introduced by a previous fix (commit 593a93d2545731629b74fd5ca64dad54cf88ba53) dealing with the post-3.6 obj->how_lost field. As with the last time I dealt with this, there was a lot of trial and error involved. This fixes the quiver issue without bringing the earlier problem back. This time the problem was that how_lost got cleared before it was used to check whether an item being picked up had been thrown. Dropping part of a stack and throwing another part of the same stack may behave oddly if a monster picks both up. I am not going to try to figure that out. Fixes #1466 --- doc/fixes3-7-0.txt | 2 ++ src/invent.c | 22 ++++++++++++++++------ src/pickup.c | 14 ++++---------- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 8790b59c3..04a085b97 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -2177,6 +2177,8 @@ livelog/#chronicle for saving-grace: if saving-grace prevents hero's death, naming a type of item, unnaming it (with name of ), naming it again (new name or re-use of old one), then unnaming it again would issue impossible: "named object not in disco" +throwing entire quiver and then picking the stack back up was not putting it + back into quiver Fixes to 3.7.0-x Platform and/or Interface Problems Exposed Via git Repository diff --git a/src/invent.c b/src/invent.c index f3c7f3803..0c5f4e0cd 100644 --- a/src/invent.c +++ b/src/invent.c @@ -1063,8 +1063,9 @@ addinv_core2(struct obj *obj) * Adjust hero attributes as necessary. */ staticfn struct obj * -addinv_core0(struct obj *obj, struct obj *other_obj, - boolean update_perm_invent) +addinv_core0( + struct obj *obj, struct obj *other_obj, + boolean update_perm_invent) { struct obj *otmp, *prev; int saved_otyp = (int) obj->otyp; /* for panic */ @@ -1072,6 +1073,9 @@ addinv_core0(struct obj *obj, struct obj *other_obj, if (obj->where != OBJ_FREE) panic("addinv: obj not free"); + if (obj->how_lost == LOST_EXPLODING) + return (struct obj *) NULL; + /* normally addtobill() clears no_charge when items in a shop are picked up, but won't do so if the shop has become untended */ obj->no_charge = 0; /* should not be set in hero's invent */ @@ -1146,6 +1150,7 @@ addinv_core0(struct obj *obj, struct obj *other_obj, setuqwep(obj); added: obj->pickup_prev = 1; + obj->how_lost = 0; addinv_core2(obj); /* handle extrinsics conferred by carrying obj */ carry_obj_effects(obj); /* carrying affects the obj */ if (update_perm_invent) @@ -1163,7 +1168,8 @@ addinv(struct obj *obj) /* add obj to the hero's inventory by inserting in front of a specific item; used for throw-and-return in case '!fixinv' is in effect */ struct obj * -addinv_before(struct obj *obj, struct obj *other_obj) +addinv_before( + struct obj *obj, struct obj *other_obj) { /* if 'other_obj' is present this will implicitly be 'nomerge' */ return addinv_core0(obj, other_obj, TRUE); @@ -5094,7 +5100,12 @@ mergable( if (obj->cursed != otmp->cursed || obj->blessed != otmp->blessed) return FALSE; - if ((obj->how_lost & ~LOSTOVERRIDEMASK) != 0) + + if (obj->how_lost == LOST_EXPLODING + || otmp->how_lost == LOST_EXPLODING) + return FALSE; + if ((obj->how_lost & ~LOSTOVERRIDEMASK) + != (otmp->how_lost & ~LOSTOVERRIDEMASK)) return FALSE; #if 0 /* don't require 'bypass' to match; that results in items dropped * via 'D' not stacking with compatible items already on the floor; @@ -5111,8 +5122,7 @@ mergable( if (obj->unpaid != otmp->unpaid || obj->spe != otmp->spe || obj->no_charge != otmp->no_charge || obj->obroken != otmp->obroken - || obj->otrapped != otmp->otrapped || obj->lamplit != otmp->lamplit - || obj->how_lost != otmp->how_lost) + || obj->otrapped != otmp->otrapped || obj->lamplit != otmp->lamplit) return FALSE; if (obj->oclass == FOOD_CLASS diff --git a/src/pickup.c b/src/pickup.c index 26655a6c6..bd1fe41db 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -1805,7 +1805,6 @@ pickup_object( long count, /* if non-zero, pick up a subset of this amount */ boolean telekinesis) /* not picking it up directly by hand */ { - unsigned save_how_lost; int res; if (obj->quan < count) { @@ -1862,16 +1861,12 @@ pickup_object( } } - save_how_lost = obj->how_lost; /* obj has either already passed autopick_testobj or we are explicitly - picking it off the floor, so override obj->how_lost; otherwise we - couldn't pick up a thrown, stolen, or dropped item that was split - off from a carried stack even while still carrying the rest of the - stack unless we have at least one free slot available */ - obj->how_lost &= ~LOSTOVERRIDEMASK; /* affects merge_choice() */ + picking it off the floor, so addinv() will override obj->how_lost; + otherwise we couldn't pick up a thrown, stolen, or dropped item that + was split off from a carried stack even while still carrying the + rest of the stack unless we have at least one free slot available */ res = lift_object(obj, (struct obj *) 0, &count, telekinesis); - obj->how_lost = save_how_lost; /* even when res > 0, - * in case we call splitobj() below */ if (res <= 0) return res; @@ -1881,7 +1876,6 @@ pickup_object( if (obj->quan != count && obj->otyp != LOADSTONE) obj = splitobj(obj, count); - obj->how_lost &= ~LOSTOVERRIDEMASK; obj = pick_obj(obj); if (uwep && uwep == obj) From 4d33d00ad2d059c440e27a645b9f46a2047df42e Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 26 Dec 2025 12:37:28 -0500 Subject: [PATCH 159/442] update tested versions of Visual Studio 2025-12-26 --- sys/windows/Makefile.nmake | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index 597f0ae2c..a6bae9d28 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.21 -# - Microsoft Visual Studio Community 2026 v 18.1.0 +# - Microsoft Visual Studio Community 2022 v 17.14.23 +# - Microsoft Visual Studio Community 2026 v 18.1.1 # #============================================================================== # This is used for building two distinct executables of NetHack: @@ -1184,8 +1184,8 @@ rc=Rc.exe # is too old or untested. # # Recently tested versions: -TESTEDVS2022 = 14.44.35221.0 -TESTEDVS2026 = 14.50.35720.0 +TESTEDVS2022 = 14.44.35222.0 +TESTEDVS2026 = 14.50.35721.0 VS20261ST = 1450000000 VS2026CUR = $(TESTEDVS2026:.=) From 5030de7343d2ab5f13985731b9f97a0c5d2c3465 Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 26 Dec 2025 14:53:59 -0800 Subject: [PATCH 160/442] fix #1466 fix - stacking in inventory The earlier fix from a couple of days ago was mislabeled as #1455 but was actually #1466. It fixed picking up a thrown stack into hero's empty quiver but broke keeping thrown items, dropped items, and stolen items separate on the floor. This repairs that. --- src/invent.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/invent.c b/src/invent.c index 0c5f4e0cd..a7cc92bfc 100644 --- a/src/invent.c +++ b/src/invent.c @@ -1150,7 +1150,6 @@ addinv_core0( setuqwep(obj); added: obj->pickup_prev = 1; - obj->how_lost = 0; addinv_core2(obj); /* handle extrinsics conferred by carrying obj */ carry_obj_effects(obj); /* carrying affects the obj */ if (update_perm_invent) @@ -5104,8 +5103,7 @@ mergable( if (obj->how_lost == LOST_EXPLODING || otmp->how_lost == LOST_EXPLODING) return FALSE; - if ((obj->how_lost & ~LOSTOVERRIDEMASK) - != (otmp->how_lost & ~LOSTOVERRIDEMASK)) + if (otmp->how_lost != LOST_NONE && (obj->how_lost != otmp->how_lost)) return FALSE; #if 0 /* don't require 'bypass' to match; that results in items dropped * via 'D' not stacking with compatible items already on the floor; From 6707d3bfbab1c28ac4e897c3fc1833eff9763d28 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sat, 27 Dec 2025 18:14:18 +0200 Subject: [PATCH 161/442] Safe armor enchantment limits The safe armor enchantment limit is lowered by one, if the armor is innately magical. This takes off 3-7 points of AC from a typical ascension kit, but should not really have any effect for early game. Also clean up the relevant code a bit. --- doc/fixes3-7-0.txt | 1 + src/read.c | 40 +++++++++++++++++++++++++++++++++------- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 04a085b97..45484c528 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1557,6 +1557,7 @@ praying will not restore monster-form HP while polymorphed, unless you winter wolf cub was missing for monster to lycanthrope conversion monster elves shooting arrows weren't getting intended small to-hit and damage bonuses +safe armor enchantment limit is lowered by one for magical armor Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/read.c b/src/read.c index c98a8a725..e35d3abe7 100644 --- a/src/read.c +++ b/src/read.c @@ -21,6 +21,8 @@ staticfn void forget(int); staticfn int maybe_tame(struct monst *, struct obj *); staticfn boolean can_center_cloud(coordxy, coordxy); staticfn void display_stinking_cloud_positions(boolean); +staticfn boolean is_special_armor_enchant(struct obj *); +staticfn int armor_enchant_limit(struct obj *); staticfn void seffect_enchant_armor(struct obj **); staticfn void seffect_destroy_armor(struct obj **); staticfn void seffect_confuse_monster(struct obj **); @@ -1110,12 +1112,37 @@ display_stinking_cloud_positions(boolean on_off) } } +/* some armor vibrates warningly when enchanted beyond a limit, + or can be enchanted higher than usual */ +staticfn boolean +is_special_armor_enchant(struct obj *otmp) +{ + return is_elven_armor(otmp) + || (Role_if(PM_WIZARD) && otmp->otyp == CORNUTHAUM); +} + +/* return the safe enchantment limit for an armor */ +staticfn int +armor_enchant_limit(struct obj *otmp) +{ + int limit = 3; /* default safe armor enchantment limit */ + + /* some armor can take a higher enchantment */ + if (is_special_armor_enchant(otmp)) + limit += 2; + /* object's internal magic interferes */ + if (objects[otmp->otyp].oc_magic) + limit--; + + return limit; +} + staticfn void seffect_enchant_armor(struct obj **sobjp) { struct obj *sobj = *sobjp; schar s; - boolean special_armor; + int safe_spe_limit; boolean same_color; struct obj *otmp = some_armor(&gy.youmonst); boolean sblessed = sobj->blessed; @@ -1160,9 +1187,6 @@ seffect_enchant_armor(struct obj **sobjp) otmp->oerodeproof = new_erodeproof ? 1 : 0; return; } - /* elven armor vibrates warningly when enchanted beyond a limit */ - special_armor = is_elven_armor(otmp) - || (Role_if(PM_WIZARD) && otmp->otyp == CORNUTHAUM); if (scursed) same_color = (otmp->otyp == BLACK_DRAGON_SCALE_MAIL || otmp->otyp == BLACK_DRAGON_SCALES); @@ -1173,9 +1197,11 @@ seffect_enchant_armor(struct obj **sobjp) if (Blind) same_color = FALSE; + safe_spe_limit = armor_enchant_limit(otmp); + /* KMH -- catch underflow */ s = scursed ? -otmp->spe : otmp->spe; - if (s > (special_armor ? 5 : 3) && rn2(s)) { + if (s > safe_spe_limit && rn2(s)) { otmp->in_use = TRUE; pline("%s violently %s%s%s for a while, then %s.", Yname2(otmp), otense(otmp, Blind ? "vibrate" : "glow"), @@ -1254,8 +1280,8 @@ seffect_enchant_armor(struct obj **sobjp) alter_cost(otmp, 0L); } - if ((otmp->spe > (special_armor ? 5 : 3)) - && (special_armor || !rn2(7))) + if ((otmp->spe > safe_spe_limit) + && (is_special_armor_enchant(otmp) || !rn2(7))) pline("%s %s.", Yobjnam2(otmp, "suddenly vibrate"), Blind ? "again" : "unexpectedly"); } From 0dac2b5fa02ec6b27c8e4574a2fed169c45634c1 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Tue, 30 Dec 2025 19:24:57 +0200 Subject: [PATCH 162/442] TTY: add support for the palette config option For now, terminal colors are not reset back when exiting NetHack. Depends on CHANGE_COLOR compile-time option. --- doc/fixes3-7-0.txt | 2 +- win/tty/termcap.c | 38 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 45484c528..df639a884 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -2337,7 +2337,7 @@ curses: if user's terminal was set to 'application keypad mode' (DEC VTxxx "ESC SPC G"), nethack wasn't recognizing number pad keys curses: change petattr attributes, dropping support for curses-only ones curses: swap the grey and no-color color initialization -curses+Qt: allow changing default colors with the 'palette' config option +curses+tty+Qt: allow changing default colors with the 'palette' config option (only if compiled with CHANGE_COLOR) curses: if messages have been issued during start-up (for instance, warnings about issues in run-time config file), prompt user to press diff --git a/win/tty/termcap.c b/win/tty/termcap.c index 3af6333f9..ad8e25764 100644 --- a/win/tty/termcap.c +++ b/win/tty/termcap.c @@ -39,6 +39,7 @@ struct tc_lcl_data tc_lcl_data = { 0, 0, 0, 0, 0, 0, 0, FALSE }; static char *nh_VI = (char *) 0; /* cursor_invisible */ static char *nh_VE = (char *) 0; /* cursor_normal */ /*static char *nh_VS = (char *) 0;*/ /* cursor_visible (highlighted cursor) */ +static char *nh_Ic = (char *) 0; /* initialize_color */ static char *HO, *CL, *CE, *UP, *XD, *BC, *SO, *SE, *TI, *TE; static char *VS, *VE; @@ -290,6 +291,8 @@ term_startup(int *wid, int *hgt) if (!nh_VI || !nh_VE /*|| !nh_VS*/ ) nh_VI = nh_VE = /*nh_VS =*/ (char *) 0; + nh_Ic = Tgetstr(nhStr("Ic")); + /* Get rid of padding numbers for nh_HI and nh_HE. Hope they * aren't really needed!!! nh_HI and nh_HE are outputted to the * pager as a string - so how can you send it NULs??? @@ -1508,9 +1511,40 @@ term_curs_set(int visibility) #ifdef CHANGE_COLOR void -tty_change_color(int color UNUSED, long rgb UNUSED, int reverse UNUSED) +tty_change_color(int color, long rgb, int reverse UNUSED) { - return; + char buf[BUFSZ]; + int i; + char *c, *fmt; + + /* FIXME: colors are not reset back when exiting NetHack */ + if (nh_Ic && *nh_Ic) { + long clr = color, r, g, b; + + /* color * 3 seems to work correctly? */ + /* this probably depends on the termcap definition */ + r = ((rgb >> 16) & 0xFF) * 3; + g = ((rgb >> 8) & 0xFF) * 3; + b = (rgb & 0xFF) * 3; + + + fmt = tparm(nh_Ic, clr, r, g, b); + + c = fmt; + + i = 0; + while (*c) { + if (*c == '\033') { + buf[i++] = '\\'; + buf[i++] = 'E'; + } else { + buf[i++] = *c; + } + c++; + } + buf[i++] = '\0'; + xputs(fmt); + } } #endif /* CHANGE_COLOR */ From 5b22feb795f95396e8f605bee5dccdcd2e8b09ae Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Wed, 31 Dec 2025 12:35:12 +0200 Subject: [PATCH 163/442] Reduce the effect of luck on to-hit Luck has a massive effect on the to-hit chance; maximum luck alone (which almost everyone has past the midpoint of the game) gives 10 points to to-hit, so accounts for 50% chance alone, excluding all other effects. Multiple variants do something similar to this, so it is well tested. This version comes from xNetHack by copperwater , and allows the +1 or -1 luck adjustments of early game, such as full moon, to have an effect. --- doc/fixes3-7-0.txt | 1 + src/uhitm.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index df639a884..08dfb2f21 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1558,6 +1558,7 @@ winter wolf cub was missing for monster to lycanthrope conversion monster elves shooting arrows weren't getting intended small to-hit and damage bonuses safe armor enchantment limit is lowered by one for magical armor +luck has a reduced effect on to-hit chance Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/uhitm.c b/src/uhitm.c index 316858896..954a0f1ea 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -372,7 +372,8 @@ find_roll_to_hit( *role_roll_penalty = 0; /* default is `none' */ - tmp = 1 + Luck + abon() + find_mac(mtmp) + u.uhitinc + tmp = 1 + abon() + find_mac(mtmp) + u.uhitinc + + (sgn(Luck) * ((abs(Luck) + 2) / 3)) + maybe_polyd(gy.youmonst.data->mlevel, u.ulevel); /* some actions should occur only once during multiple attacks */ From 01928276d5e452e678d93e7dabe780c32e5abf1c Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sat, 3 Jan 2026 11:04:13 +0200 Subject: [PATCH 164/442] Monsters use wand of teleportation more --- doc/fixes3-7-0.txt | 1 + src/muse.c | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 08dfb2f21..59666461d 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1559,6 +1559,7 @@ monster elves shooting arrows weren't getting intended small to-hit and damage bonuses safe armor enchantment limit is lowered by one for magical armor luck has a reduced effect on to-hit chance +monsters use wand of teleportation to move hero away from item pile Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/muse.c b/src/muse.c index f4d270e48..0f02778be 100644 --- a/src/muse.c +++ b/src/muse.c @@ -29,6 +29,7 @@ staticfn boolean linedup_chk_corpse(coordxy, coordxy); staticfn void m_use_undead_turning(struct monst *, struct obj *); staticfn boolean hero_behind_chokepoint(struct monst *); staticfn boolean mon_has_friends(struct monst *); +staticfn boolean mon_likes_objpile_at(struct monst *mtmp, coordxy x, coordxy y) NONNULLARG1; staticfn int mbhitm(struct monst *, struct obj *); staticfn boolean fhito_loc(struct obj *obj, coordxy x, coordxy y, int (*fhito)(OBJ_P, OBJ_P)); @@ -1388,6 +1389,30 @@ mon_has_friends(struct monst *mtmp) return FALSE; } +/* does monster like object pile at x,y? */ +staticfn boolean +mon_likes_objpile_at(struct monst *mtmp, coordxy x, coordxy y) +{ + int i; + struct obj *otmp; + + if (!isok(x,y) || !OBJ_AT(x,y)) + return FALSE; + + /* monster likes any of the top 3 items in the pile? */ + for (i = 0, otmp = svl.level.objects[x][y]; otmp && i < 3; i++) { + if (mon_would_take_item(mtmp, otmp)) + return TRUE; + otmp = otmp->nexthere; + } + + /* pile is larger than 3 stacks? */ + if (i >= 3) + return TRUE; + + return FALSE; +} + /* Select an offensive item/action for a monster. Returns TRUE iff one is * found. */ @@ -1488,6 +1513,7 @@ find_offensive(struct monst *mtmp) /* do try to move hero to a more vulnerable spot */ && (onscary(u.ux, u.uy, mtmp) || (hero_behind_chokepoint(mtmp) && mon_has_friends(mtmp)) + || mon_likes_objpile_at(mtmp, u.ux, u.uy) || stairway_at(u.ux, u.uy))) { gm.m.offensive = obj; gm.m.has_offense = MUSE_WAN_TELEPORTATION; From 71d7b1e5870798e1fc2d094227b1219524802457 Mon Sep 17 00:00:00 2001 From: danielclow <106956386+danielclow@users.noreply.github.com> Date: Sat, 3 Jan 2026 20:57:31 +0800 Subject: [PATCH 165/442] Update LaTeX guidebook to use 2e documentclass This is essentially the minimum required to start modernizing the LaTeX code for the guidebook. Because this breaks the way that underscores and straight single quotation marks were implemented, this also replaces those with textunderscore and textsinglequote. --- doc/Guidebook.tex | 823 +++++++++++++++++++++++----------------------- 1 file changed, 410 insertions(+), 413 deletions(-) diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index bdc21ecc6..df0db5dfd 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -1,11 +1,8 @@ -\documentstyle[titlepage,longtable]{article} % NetHack 3.7 Guidebook.tex $NHDT-Date: 1745139202 2025/04/20 00:53:22 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.579 $ */ -%+% we're still limping along in LaTeX 2.09 compatibility mode -%-%\documentclass{article} -%-%\usepackage{hyperref} % before longtable -%-%% if hyperref isn't available, we can get by with this instead -%-%%\RequirePackage[errorshow]{tracefnt} \DeclareSymbolFont{typewriter}{OT1}{cmtt}{m}{n} -%-%\usepackage{longtable} +\documentclass[titlepage, a4paper]{article} +\usepackage{hyperref} +\usepackage{longtable} + \textheight 220mm \textwidth 160mm \oddsidemargin 0mm @@ -566,7 +563,7 @@ Note: statues are displayed as if they were the monsters they depict so won't appear as a {\it grave accent\/} (aka {\it back-tick}). \item[\tb{0}] An iron ball. -\item[\tb{\verb+_+}] +\item[\tb{\textunderscore}] An altar, or an iron chain. \item[\tb{\{}] A fountain or a sink. @@ -577,8 +574,8 @@ or a pool of lava or a wall of lava. An opulent throne. \item[\tb{a-z}] {\normalfont and}] \item[\tb{A-HJ-Z}] {\normalfont and}] -%should probably change \item[\tb{@\&\verb+'+:;}] to \item[\tb{\verb+@&':;+}] -\item[\tb{@\&\verb+'+:;}] +%should probably change \item[\tb{@\&\textquotesingle:;}] to \item[\tb{\verb+@&':;+}] +\item[\tb{@\&\textquotesingle:;}] Letters and certain other symbols represent the various inhabitants of the Mazes of Menace. Watch out, they can be nasty and vicious. @@ -632,7 +629,7 @@ command. %.pg You can put a number before some commands to repeat them that many times; for example, ``{\tt 10s}'' will search ten times. If you have the -{\it number\verb+_+pad\/} +{\it number\textunderscore pad\/} option set, you must type `{\tt n}' to prefix a count, so the example above would be typed ``{\tt n10s}'' instead. Commands for which counts make no sense ignore them. In addition, movement commands can be prefixed for @@ -674,7 +671,7 @@ option is on, a short description of what you see at each location is shown as you move the cursor. Typing `{\tt \#}' while picking a location will toggle that option on or off. The -{\it whatis\verb+_+coord\/} +{\it whatis\textunderscore coord\/} option controls whether the short description includes map coordinates. %.lp "" @@ -685,7 +682,7 @@ always gives any additional information available about that name. You may also request a description of nearby monsters, all monsters currently displayed, nearby objects, or all objects. The -{\it whatis\verb+_+coord\/} +{\it whatis\textunderscore coord\/} option controls which format of map coordinate is included with their descriptions. %.lp @@ -712,7 +709,7 @@ one-step movement commands cause you to fight monsters; the others \verb+ h- . -l + & \verb+ 4- . -6 +\\ \verb+ / | \ + & \verb+ / | \ +\\ \verb+ b j n + & \verb+ 1 2 3 +\\ - & (if {\it number\verb+_+pad\/} set) + & (if {\it number\textunderscore pad\/} set) \end{tabular} \end{center} %.ed @@ -782,7 +779,7 @@ by using {\tt m} and {\tt G} in combination. {\tt m} can also be used in combination with {\tt g}, {\tt +}, or {\tt +}. %.lp -\item[\tb{\tt \verb+_+}] +\item[\tb{\tt \textunderscore }] Travel to a map location via a shortest-path algorithm.\\ %.lp "" The shortest path @@ -799,7 +796,7 @@ location other than the current position. \item[\tb{.}] Wait or rest, do nothing for one turn. Precede with the `{\tt m}' prefix -to wait for a turn even next to a hostile monster, if {\it safe\verb+_+wait\/} +to wait for a turn even next to a hostile monster, if {\it safe\textunderscore wait\/} is on. %.lp \item[\tb{a}] @@ -893,7 +890,7 @@ If you attempt to eat while already satiated, you might choke to death. If you risk it, you will be asked whether to ``continue eating?'' {\it if you survive the first bite\/}. You can set the -{\it paranoid\verb+_+confirmation:eating\/} +{\it paranoid\textunderscore confirmation:eating\/} option to require a response of ``{\tt yes}'' instead of just `{\tt y}'. %.lp % Make sure Elbereth is not hyphenated below, the exact spelling matters. @@ -993,7 +990,7 @@ Repeat previous message.\\ %.lp "" Subsequent {\tt \^{}P}'s repeat earlier messages. For some interfaces, the behavior can be varied via the -{\it msg\verb+_+window\/} option. +{\it msg\textunderscore window\/} option. %.lp \item[\tb{q}] Quaff (drink) something (potion, water, etc).\\ @@ -1019,7 +1016,7 @@ Remove a worn accessory (ring, amulet, or blindfold).\\ If you're wearing more than one, you'll be prompted for which one to remove. When you're only wearing one, then by default it will be removed without asking, but you can set the -{\it paranoid\verb+_+confirmation:Remove\/} +{\it paranoid\textunderscore confirmation:Remove\/} option to require a prompt.\\ %.lp "" This command may also be used to take off armor. The prompt for which @@ -1035,7 +1032,7 @@ Redraw the screen. Search for secret doors and traps around you. It usually takes several tries to find something. Precede with the `{\tt m}' prefix to wait for a turn -even next to a hostile monster, if {\it safe\verb+_+wait\/} +even next to a hostile monster, if {\it safe\textunderscore wait\/} is on.\\ %.lp "" Can also be used to figure out whether there is still a monster at @@ -1077,7 +1074,7 @@ and/or a shirt, or a suit covering a shirt, as if the underlying items weren't there.) When you're only wearing one, then by default it will be taken off without asking, but you can set the -{\it paranoid\verb+_+confirmation:Remove\/} +{\it paranoid\textunderscore confirmation:Remove\/} option to require a prompt.\\ %.lp "" This command may also be used to remove accessories. The prompt @@ -1233,15 +1230,15 @@ May be preceded by `{\tt m}' to select preferred display order. %.lp \item[\tb{|}] If persistent inventory display is supported and enabled (with the -{\it perm\verb+_+invent\/} +{\it perm\textunderscore invent\/} option), interact with it instead of with the map. \\ %.lp "" Allows scrolling with the -menu\verb+_+first\verb+_+page, menu\verb+_+previous\verb+_+page, -menu\verb+_+next\verb+_+page, and menu\verb+_+last\verb+_+page +menu\textunderscore first\textunderscore page, menu\textunderscore previous\textunderscore page, +menu\textunderscore next\textunderscore page, and menu\textunderscore last\textunderscore page keys (`{\tt \^{}}', `{\tt <}', `{\tt >}', `{\tt \verb+|+}' by default). -Some interfaces also support menu\verb+_+shift\verb+_+left and menu\verb+_+shift\verb+_+right +Some interfaces also support menu\textunderscore shift\textunderscore left and menu\textunderscore shift\textunderscore right keys (`{\tt \verb+{+}' and `{\tt \verb+}+}' by default). Use the {\it Return\/} (aka {\it Enter\/}) or {\it Escape\/} key to resume play. @@ -1309,7 +1306,7 @@ Allows you to specify one line of text to associate with the current dungeon level. All levels with annotations are displayed by the ``{\tt \#overview}'' command. Autocompletes. Default key is `{\tt M-A}', -and also `{\tt \^{}N}' if {\it number\verb+_+pad\/} is on. +and also `{\tt \^{}N}' if {\it number\textunderscore pad\/} is on. %.lp \item[\tb{\#apply}] Apply (use) a tool such as a pick-axe, a key, or a lamp. @@ -1393,14 +1390,14 @@ Default key is `{\tt M-X}'.\\ Requires confirmation; default response is `{\tt n}' (no). To really switch to explore mode, respond with `{\tt y}'. You can set the -{\it paranoid\verb+_+confirmation:quit\/} +{\it paranoid\textunderscore confirmation:quit\/} option to require a response of ``{\tt yes}'' instead. %.lp \item[\tb{\#fight}] Prefix key to force fight a direction, even if you see nothing to fight there. Default key is `{\tt F}', or `{\tt -}' with -{\it number\verb+_+pad\/} +{\it number\textunderscore pad\/} %.lp \item[\tb{\#fire}] Fire ammunition from quiver, possibly autowielding a launcher, @@ -1436,7 +1433,7 @@ Show what type of thing a map symbol corresponds to. Default key is `{\tt ;}'. \item[\tb{\#help}] Show the help menu. Default key is `{\tt ?}', -and also `{\tt h}' if {\it number\verb+_+pad\/} is on. +and also `{\tt h}' if {\it number\textunderscore pad\/} is on. %.lp \item[\tb{\#herecmdmenu}] Show a menu of possible actions directed at your current location. @@ -1444,7 +1441,7 @@ The menu is limited to a subset of the likeliest actions, not an exhaustive set of all possibilities. Autocompletes.\\ %.lp "" -If mouse support is enabled and the {\it herecmd\verb+_+menu\/} +If mouse support is enabled and the {\it herecmd\textunderscore menu\/} option is On, clicking on the hero (or steed when mounted) will execute this command. %.lp @@ -1463,12 +1460,12 @@ Invoke an object's special powers. Autocompletes. Default key is `{\tt M-i}'. \item[\tb{\#jump}] Jump to another location. Autocompletes. Default key is `{\tt M-j}', -and also `{\tt j}' if {\it number\verb+_+pad\/} is on. +and also `{\tt j}' if {\it number\textunderscore pad\/} is on. %.lp \item[\tb{\#kick}] Kick something. Default key is `{\tt \^{}D}', -and also `{\tt k}' if {\it number\verb+_+pad\/} is on. +and also `{\tt k}' if {\it number\textunderscore pad\/} is on. %.lp \item[\tb{\#known}] Show what object types have been discovered. @@ -1508,7 +1505,7 @@ from a steed standing next to you. Autocompletes. Precede with the `{\tt m}' prefix to skip containers at your location and go directly to removing a saddle. Default key is `{\tt M-l}', -and also `{\tt l}' if {\it number\verb+_+pad\/} is on. +and also `{\tt l}' if {\it number\textunderscore pad\/} is on. %.lp \item[\tb{\#monster}] Use a monster's special ability (when polymorphed into monster form). @@ -1574,7 +1571,7 @@ Debug mode only.\\ Asks for confirmation; default is `{\tt n}' (no); continue playing. To really panic, respond with `{\tt y}'. You can set the -{\it paranoid\verb+_+confirmation:quit\/} +{\it paranoid\textunderscore confirmation:quit\/} option to require a response of ``{\tt yes}'' instead. %.lp \item[\tb{\#pay}] @@ -1582,7 +1579,7 @@ Pay your shopping bill. Default key is `{\tt p}'. %.lp \item[\tb{\#perminv}] If persistent inventory display is supported and enabled (with the -{\it perm\verb+_+invent\/} option), interact with it instead of with the map. +{\it perm\textunderscore invent\/} option), interact with it instead of with the map. You'll be prompted for menu scrolling keystrokes such as `{\tt \verb+>+}' and `{\tt \verb+<+}'. Press {\tt Return} or {\tt Escape} to resume normal play. @@ -1606,7 +1603,7 @@ You probably shouldn't start off a new game by praying right away.) Since using this command by accident can cause trouble, there is an option to make you confirm your intent before praying. It is enabled by default, and you can reset the -{\it paranoid\verb+_+confirmation\/} +{\it paranoid\textunderscore confirmation\/} option to disable it. %.lp \item[\tb{\#prevmsg}] @@ -1629,7 +1626,7 @@ you are asked to confirm your intent before quitting. Default response is `{\tt n}' (no); continue playing. To really quit, respond with `{\tt y}'. You can set the -{\it paranoid\verb+_+confirmation:quit\/} +{\it paranoid\textunderscore confirmation:quit\/} option to require a response of ``{\tt yes}'' instead. %.lp \item[\tb{\#quiver}] @@ -1641,7 +1638,7 @@ Read a scroll, a spellbook, or something else. Default key is `{\tt r}'. \item[\tb{\#redraw}] Redraw the screen. Default key is `{\tt \^{}R}', -and also `{\tt \^{}L}' if {\it number\verb+_+pad\/} is on. +and also `{\tt \^{}L}' if {\it number\textunderscore pad\/} is on. %.lp \item[\tb{\#remove}] Remove an accessory (ring, amulet, etc). Default key is `{\tt R}'. @@ -1657,7 +1654,7 @@ Default key is `{\tt m}'. %.lp \item[\tb{\#retravel}] Travel to a previously selected travel destination. -Default key is `{\tt C-\verb+_+}'. +Default key is `{\tt C-\textunderscore }'. See also {\tt \#travel}. %.lp \item[\tb{\#ride}] @@ -1670,20 +1667,20 @@ Rub a lamp or a stone. Autocompletes. Default key is `{\tt M-r}'. \item[\tb{\#run}] Prefix key to run towards a direction. Default key is `{\tt G}' when -{\it number\verb+_+pad\/} +{\it number\textunderscore pad\/} is off, `{\tt 5}' when -{\it number\verb+_+pad\/} +{\it number\textunderscore pad\/} is set to 1~or~3, otherwise `{\tt M-5}' when it is set to 2~or~4. %.lp \item[\tb{\#rush}] Prefix key to rush towards a direction. Default key is `{\tt g}' when -{\it number\verb+_+pad\/} +{\it number\textunderscore pad\/} is off, `{\tt M-5}' when -{\it number\verb+_+pad\/} +{\it number\textunderscore pad\/} is set to 1~or~3, otherwise `{\tt 5}' when it is set to 2~or~4. %.lp @@ -1823,7 +1820,7 @@ Autocompletes. %%--invoking it by mouse seems to be broken %% \\ %% .lp "" -%% If mouse support is enabled and the {\it herecmd\verb+_+menu\/} +%% If mouse support is enabled and the {\it herecmd\textunderscore menu\/} %% option is On, clicking on an adjacent location will execute this command. %.lp \item[\tb{\#throw}] @@ -1853,7 +1850,7 @@ Autocompletes. Default key is `{\tt M-T}'. %.lp \item[\tb{\#travel}] Travel to a specific location on the map. -Default key is `{\tt \verb+_+}'. +Default key is `{\tt \textunderscore }'. Using the ``request menu'' prefix shows a menu of interesting targets in sight without asking to move the cursor. When picking a target with cursor and the {\it autodescribe\/} @@ -1867,7 +1864,7 @@ Turn undead away. Autocompletes. Default key is `{\tt M-t}'. \item[\tb{\#twoweapon}] Toggle two-weapon combat on or off. Autocompletes. Default key is `{\tt X}', -and also `{\tt M-2}' if {\it number\verb+_+pad\/} is off.\\ +and also `{\tt M-2}' if {\it number\textunderscore pad\/} is off.\\ %.lp "" Note that you must use suitable weapons for this type of combat, or it will @@ -1875,7 +1872,7 @@ be automatically turned off. %.lp \item[\tb{\#untrap}] Untrap something (trap, door, or chest). -Default key is `{\tt M-u}', and `{\tt u}' if {\it number\verb+_+pad\/} is on.\\ +Default key is `{\tt M-u}', and `{\tt u}' if {\it number\textunderscore pad\/} is on.\\ %.lp "" In some circumstances it can also be used to rescue trapped monsters. %.lp @@ -1932,7 +1929,7 @@ Debug mode only. \item[\tb{\#wait}] Rest one move while doing nothing. Default key is `{\tt .}', and also `{\tt{ }}' if -{\it rest\verb+_+on\verb+_+space\/} is on. +{\it rest\textunderscore on\textunderscore space\/} is on. %.lp \item[\tb{\#wear}] Wear a piece of armor. Default key is `{\tt W}'. @@ -2081,7 +2078,7 @@ equivalent is used for another command, so the three key combination {\tt\#?} (not supported by all platforms) %.lp \item[\tb{M-2}] -{\tt\#twoweapon} (unless the {\it number\verb+_+pad\/} option is enabled) +{\tt\#twoweapon} (unless the {\it number\textunderscore pad\/} option is enabled) %.lp \item[\tb{M-a}] {\tt\#adjust} @@ -2163,7 +2160,7 @@ equivalent is used for another command, so the three key combination \elist %.pg -\nd If the {\it number\verb+_+pad\/} option is on, some additional letter commands +\nd If the {\it number\textunderscore pad\/} option is on, some additional letter commands are available: \blist{} %.lp @@ -2471,7 +2468,7 @@ There are several options which can be used to augment the normal feedback. %.pg The -{\it pile\verb+_+limit\/} +{\it pile\textunderscore limit\/} option controls how many objects can be in a pile---sharing the same map location---for the game to state ``there are objects here'' instead of listing them. @@ -2493,7 +2490,7 @@ auto-pickup and without giving feedback about them. %.pg The -{\it mention\verb+_+walls\/} +{\it mention\textunderscore walls\/} option controls whether you get feedback if you try to walk into a wall or solid stone or off the edge of the map. Normally nothing happens (unless the hero is blind and no wall is shown, @@ -2503,7 +2500,7 @@ some non-obvious reason. %.pg The -{\it mention\verb+_+decor\/} +{\it mention\textunderscore decor\/} option controls whether you get feedback when walking on ``furniture.'' Normally stepping onto stairs or a fountain or an altar or various other things doesn't elicit anything unless it is covered by one or more objects @@ -2524,7 +2521,7 @@ case the back on land circumstance is implied. The {\it confirm\/} and -{\it safe\verb+_+pet\/} +{\it safe\textunderscore pet\/} options control what happens when you try to move onto a peaceful monster's spot or a tame one's spot. @@ -2536,13 +2533,13 @@ onto a visible monster's spot without the move being considered an attack (see the {\it Fighting\/} subsection of {\it Monsters\/} below). The `{\tt fight}' command prefix (default `{\tt F}'; also `{\tt -}' if -{\it number\verb+_+pad\/} +{\it number\textunderscore pad\/} is on) can be used to force an attack, when guessing where an unseen monster is or when deliberately attacking a peaceful or tame creature. %.pg The -{\it run\verb+_+mode} +{\it run\textunderscore mode} option controls how frequently the map gets redrawn when moving more than one step in a single command (so when rushing, running, or traveling). @@ -2605,7 +2602,7 @@ In most circumstances, if you attempt to attack a peaceful monster by moving into its location, you'll be asked to confirm your intent. By default an answer of `{\tt y}' acknowledges that intent, which can be error prone if you're using `{\tt y}' to move. You can set the -{\it paranoid\verb+_+confirmation:attack\/} +{\it paranoid\textunderscore confirmation:attack\/} option to require a response of ``{\tt yes}'' instead. %.pg @@ -2804,7 +2801,7 @@ by the presence of the word {\tt cursed}, {\tt uncursed} or In some cases {\tt uncursed} will be omitted as being redundant when enough other information is displayed. The -{\it implicit\verb+_+uncursed\/} +{\it implicit\textunderscore uncursed\/} option can be used to control this; toggle it off to have {\tt uncursed} be displayed even when that can be deduced from other attributes. @@ -2941,7 +2938,7 @@ The number of items that the character has a chance to fire varies from turn to turn. You can explicitly limit the number of shots by using a numeric prefix before the `{\tt t}' or `{\tt f}' command. For example, ``{\tt 2f}'' (or ``{\tt n2f}'' if using -{\it number\verb+_+pad\/} +{\it number\textunderscore pad\/} mode) would ensure that at most 2 arrows are shot even if you could have fired 3. If you specify a larger number than would have been shot (``{\tt 4f}'' in this example), @@ -3490,7 +3487,7 @@ you are carrying (shopkeepers aside). Gold pieces are the only type of object where bless/curse state does not apply. They're always uncursed but never described as uncursed even if you turn -off the ``{\it implicit\verb+_+uncursed\/}'' option. +off the ``{\it implicit\textunderscore uncursed\/}'' option. You can set the ``{\it goldX\/}'' option if you prefer to have gold pieces be treated as bless/curse state {\it unknown\/} rather than as known to be uncursed. @@ -3520,7 +3517,7 @@ again you will re-discover the object and resume remembering it. The situation is the same for a pile of objects, except that only the top item of the pile is displayed. The -{\it hilite\verb+_+pile\/} +{\it hilite\textunderscore pile\/} option can be enabled in order to show an item differently when it is the top one of a pile. @@ -4153,7 +4150,7 @@ Cannot be set with the `{\tt O}' command. Persistent. \item[\ib{autodescribe}] Automatically describe the terrain under cursor when asked to get a location on the map (default true). -The {\it whatis\verb+_+coord\/} +The {\it whatis\textunderscore coord\/} option controls whether the description includes map coordinates. %.lp \item[\ib{autodig}] @@ -4169,8 +4166,8 @@ Automatically pick up things onto which you move (default off). Persistent. \\ %.lp "" -See ``{\it pickup\verb+_+types\/}'' and also -``{\it autopickup\verb+_+exception\/}'' for ways to refine the behavior. +See ``{\it pickup\textunderscore types\/}'' and also +``{\it autopickup\textunderscore exception\/}'' for ways to refine the behavior. \\ %.lp "" Note: prior to version 3.7.0, the default for {\it autopickup\/} was {\it on}. @@ -4269,16 +4266,16 @@ players if it detects some anticipated mistakes (default on). Have user confirm attacks on pets, shopkeepers, and other peaceable creatures (default on). Persistent. %.lp -\item[\ib{dark\verb+_+room}] +\item[\ib{dark\textunderscore room}] Show out-of-sight areas of lit rooms (default on). Persistent. %.lp \item[\ib{deaf}] Start the character permanently deaf (default false). Persistent. %.lp -\item[\ib{dropped\verb+_+nopick}] +\item[\ib{dropped\textunderscore nopick}] If this option is on, items you dropped will not be automatically picked up, even if ``{\it autopickup\/}'' is also on and they are in -``{\it pickup\verb+_+types\/}'' or match a positive autopickup exception +``{\it pickup\textunderscore types\/}'' or match a positive autopickup exception (default on). Persistent. %.lp \item[\ib{disclose}] @@ -4411,7 +4408,7 @@ When filtering objects based on bless/curse state (BUCX), whether to treat gold pieces as {\tt X} (unknown bless/curse state, when `on') or {\tt U} (known to be uncursed, when `off', the default). Gold is never blessed or cursed, but it is not described as ``uncursed'' -even when the {\it implicit\verb+_+uncursed\/} option is `off'. +even when the {\it implicit\textunderscore uncursed\/} option is `off'. %.lp \item[\ib{help}] If more information is available for an object looked at @@ -4420,12 +4417,12 @@ Turning help off makes just looking at things faster, since you aren't interrupted with the ``{\tt More info?}'' prompt, but it also means that you might miss some interesting and/or important information. Persistent. %.lp -\item[\ib{herecmd\verb+_+menu}] +\item[\ib{herecmd\textunderscore menu}] When using a windowport that supports mouse and clicking on yourself or next to you, show a menu of possible actions for the location. Same as ``{\tt \#herecmdmenu}'' and ``{\tt \#therecmdmenu}'' commands. %.lp -\item[\ib{hilite\verb+_+pet}] +\item[\ib{hilite\textunderscore pet}] Visually distinguish pets from similar animals (default off). The behavior of this option depends on the type of windowing you use. In text windowing, text highlighting or inverse video is often used; @@ -4434,9 +4431,9 @@ with tiles, generally displays a heart symbol near pets. %.lp "" With the tty or curses interface, the {\it petattr\/} option controls how to highlight pets and setting it will turn the -{\it hilite\verb+_+pet\/} option on or off as warranted. +{\it hilite\textunderscore pet\/} option on or off as warranted. %.lp -\item[\ib{hilite\verb+_+pile}] +\item[\ib{hilite\textunderscore pile}] Visually distinguish piles of objects from individual objects (default off). The behavior of this option depends on the type of windowing you use. In text windowing, text highlighting or inverse video is often used; @@ -4477,7 +4474,7 @@ Cannot be set with the `{\tt O}' command. \item[\ib{ignintr}] Ignore interrupt signals, including breaks (default off). Persistent. %.lp -\item[\ib{implicit\verb+_+uncursed}] +\item[\ib{implicit\textunderscore uncursed}] Omit ``uncursed'' from object descriptions when it can be deduced from other aspects of the description (default on). Persistent. @@ -4489,7 +4486,7 @@ If you use menu coloring, you may want to turn this off. Display an introductory message when starting the game (default on). Persistent. %.lp -\item[\ib{lit\verb+_+corridor}] +\item[\ib{lit\textunderscore corridor}] Show corridor squares seen by night vision or a light source held by your character as lit (default off). Persistent. %.lp @@ -4506,15 +4503,15 @@ Enable mail delivery during the game (default on). Persistent. An obsolete synonym for ``{\tt gender:male}''. Cannot be set with the `{\tt O}' command. %.lp -\item[\ib{mention\verb+_+decor}] +\item[\ib{mention\textunderscore decor}] Give feedback when walking onto various dungeon features such as stairs, fountains, or altars which are ordinarily only described when covered by one or more objects (default off). Persistent. %.lp -\item[\ib{mention\verb+_+map}] +\item[\ib{mention\textunderscore map}] Give feedback when interesting map locations change (default off). %.lp -\item[\ib{mention\verb+_+walls}] +\item[\ib{mention\textunderscore walls}] Give feedback when walking against a wall (default off). Persistent. %.lp \item[\ib{menucolors}] @@ -4542,43 +4539,43 @@ object classes rather than a character prompt, and then a menu of matching objects for selection. (Choosing its `A' (Autoselect-All) choice skips the second menu. To avoid choosing that by accident, -set {\it paranoid\verb+_+confirm:AutoAll\/} to require confirmation.) +set {\it paranoid\textunderscore confirm:AutoAll\/} to require confirmation.) Partial skips the object class filtering and immediately displays a menu of all objects. -\item[\ib{menu\verb+_+deselect\verb+_+all}] +\item[\ib{menu\textunderscore deselect\textunderscore all}] Key to deselect all items in a menu. Implemented by the Amiga, Gem, X11 and tty ports. Default `-'. -\item[\ib{menu\verb+_+deselect\verb+_+page}] +\item[\ib{menu\textunderscore deselect\textunderscore page}] Key to deselect all items on this page of a menu. Implemented by the Amiga, Gem and tty ports. Default `\verb+\+'. -\item[\ib{menu\verb+_+first\verb+_+page}] +\item[\ib{menu\textunderscore first\textunderscore page}] Key to jump to the first page in a menu. Implemented by the Amiga, Gem and tty ports. Default `\verb+^+'. -\item[\ib{menu\verb+_+headings}] +\item[\ib{menu\textunderscore headings}] Controls how the headings in a menu are highlighted. Takes a text attribute, or text color and attribute separated by ampersand. For allowed attributes and colors, see ``{\it Configuring Menu Colors\/}``. Not all ports can actually display all types. -\item[\ib{menu\verb+_+invert\verb+_+all}] +\item[\ib{menu\textunderscore invert\textunderscore all}] Key to invert all items in a menu. Implemented by the Amiga, Gem, X11 and tty ports. Default `@'. -\item[\ib{menu\verb+_+invert\verb+_+page}] +\item[\ib{menu\textunderscore invert\textunderscore page}] Key to invert all items on this page of a menu. Implemented by the Amiga, Gem and tty ports. Default `\verb+~+'. -\item[\ib{menu\verb+_+last\verb+_+page}] +\item[\ib{menu\textunderscore last\textunderscore page}] Key to jump to the last page in a menu. Implemented by the Amiga, Gem and tty ports. Default `\verb+|+'. -\item[\ib{menu\verb+_+next\verb+_+page}] +\item[\ib{menu\textunderscore next\textunderscore page}] Key to go to the next menu page. Implemented by the Amiga, Gem and tty ports. Default `\verb+>+'. -\item[\ib{menu\verb+_+objsyms}] +\item[\ib{menu\textunderscore objsyms}] % [originally menu_objsyms was a boolean] % Show object symbols in menu headings in menus where % the object symbols act as menu accelerators (default off). @@ -4613,46 +4610,46 @@ objects among classes. Supported by tty and curses. When setting the value, it can be specified by digit or keyword. The default value is {\tt Conditional} (4). -\item[\ib{menu\verb+_+overlay}] +\item[\ib{menu\textunderscore overlay}] Do not clear the screen before drawing menus, and align menus to the right edge of the screen. Only for the tty port. (default on) -\item[\ib{menu\verb+_+previous\verb+_+page}] +\item[\ib{menu\textunderscore previous\textunderscore page}] Key to go to the previous menu page. Implemented by the Amiga, Gem and tty ports. Default `\verb+<+'. -\item[\ib{menu\verb+_+search}] +\item[\ib{menu\textunderscore search}] Key to search for some text and toggle selection state of matching menu items. Default `:'. -\item[\ib{menu\verb+_+select\verb+_+all}] +\item[\ib{menu\textunderscore select\textunderscore all}] Key to select all items in a menu. Implemented by the Amiga, Gem, X11 and tty ports. Default `.'. -\item[\ib{menu\verb+_+select\verb+_+page}] +\item[\ib{menu\textunderscore select\textunderscore page}] Key to select all items on this page of a menu. Implemented by the Amiga, Gem and tty ports. Default `,'. %.lp -\item[\ib{menu\verb+_+shift\verb+_+left}] +\item[\ib{menu\textunderscore shift\textunderscore left}] Key to scroll a menu---one which has been scrolled right---back to the left. -Implemented for {\it perm\verb+_+invent\/} only by curses and X11. +Implemented for {\it perm\textunderscore invent\/} only by curses and X11. Default `{\tt \verb+{+}'. %.lp -\item[\ib{menu\verb+_+shift\verb+_+right}] +\item[\ib{menu\textunderscore shift\textunderscore right}] Key to scroll a menu which has text beyond the right edge to the right. -Implemented for {\it perm\verb+_+invent\/} only by curses by X11. +Implemented for {\it perm\textunderscore invent\/} only by curses by X11. Default `{\tt \verb+}+}'. % %.lp -% \item[\ib{menu\verb+_+tab\verb+_+sep}] +% \item[\ib{menu\textunderscore tab\textunderscore sep}] % Format menu entries using TAB to separate columns (default off). % Only applicable to some menus, and only useful to some interfaces. % Debug mode only. %.lp -\item[\ib{mon\verb+_+movement}] +\item[\ib{mon\textunderscore movement}] Show a message when hero notices a monster movement (default is off). %.lp \item[\ib{monpolycontrol}] @@ -4663,7 +4660,7 @@ Debug mode only. Prompt for destination whenever any monster gets teleported (default off). Debug mode only. %.lp -\item[\ib{mouse\verb+_+support}] +\item[\ib{mouse\textunderscore support}] Allow use of the mouse for input and travel. Valid settings are: @@ -4677,7 +4674,7 @@ Valid settings are: Omitting a value is the same as specifying {\tt 1} and negating -{\it mouse\verb+_+support\/} +{\it mouse\textunderscore support\/} is the same as specifying {\tt 0}. %.lp \item[\ib{msghistory}] @@ -4685,7 +4682,7 @@ The number of top line messages to save (and be able to recall with `{\tt \^{}P}') (default 20). Cannot be set with the `{\tt O}' command. %.lp -\item[\ib{msg\verb+_+window}] +\item[\ib{msg\textunderscore window}] Allows you to change the way recalled messages are displayed. Currently it is only supported for tty (all four choices) and for curses (`{\tt f}' and `{\tt r}' choices, default `{\tt r}'). @@ -4730,7 +4727,7 @@ Start the character with no armor (default false). Persistent. \item[\ib{null}] Send padding nulls to the terminal (default on). Persistent. %.lp -\item[\ib{number\verb+_+pad}] +\item[\ib{number\textunderscore pad}] Use digit keys instead of letters to move (default 0 or off).\\ Valid settings are: @@ -4750,7 +4747,7 @@ Valid settings are: For backward compatibility, omitting a value is the same as specifying {\tt 1} and negating -{\it number\verb+_+pad\/} +{\it number\textunderscore pad\/} is the same as specifying {\tt 0}. (Settings {\tt 2} and {\tt 4} are for compatibility with MS-DOS or old PC Hack; in addition to the different behavior for `{\tt 5}', `{\tt Alt-5}' acts as `{\tt G}' @@ -4767,10 +4764,10 @@ Specify the order to list object types in (default containing the symbols for the various object types. Any omitted types are filled in at the end from the previous order. %.lp -\item[\ib{paranoid\verb+_+confirmation}] +\item[\ib{paranoid\textunderscore confirmation}] A space separated list of specific situations where alternate prompting is desired. -The default is ``{\it paranoid\verb+_+confirmation:pray swim trap}''. +The default is ``{\it paranoid\textunderscore confirmation:pray swim trap}''. %.sd %.si \newlength{\pcwidth} @@ -4831,17 +4828,17 @@ turn on all of the above. %.ed By default, the pray, swim, and trap choices are enabled, the others disabled. To disable them without setting -any of the other choices, use ``{\it paranoid\verb+_+confirmation:none}''. +any of the other choices, use ``{\it paranoid\textunderscore confirmation:none}''. To keep them enabled while setting any of the others, you can include them in the list, such as -``{\it par\-a\-noid\verb+_+con\-fir\-ma\-tion:attack~pray~swim~Remove\/}'' +``{\it par\-a\-noid\textunderscore con\-fir\-ma\-tion:attack~pray~swim~Remove\/}'' or you can precede the first entry in the list with a plus sign, -``{\it paranoid\verb+_+confirmation:\verb|+|attack~Remove\/}''. +``{\it paranoid\textunderscore confirmation:\verb|+|attack~Remove\/}''. To remove an entry that has been previously set without removing others, precede the first entry in the list with a minus sign, -``{\it paranoid\verb+_+confirmation:-swim\/}. +``{\it paranoid\textunderscore confirmation:-swim\/}. To both add some new entries and remove some old ones, you can use -multiple {\it paranoid\verb+_+confirmation\/} option settings, or you can +multiple {\it paranoid\textunderscore confirmation\/} option settings, or you can use the `{\tt \verb|+|}' form and list entries to be added by their name and entries to be removed by `{\tt !}' and name. The positive (no `!') and negative (with `!') entries @@ -4850,22 +4847,22 @@ can be intermixed. \item[\ib{pauper}] Start the character with no possessions (default false). Persistent. %.lp -\item[\ib{perm\verb+_+invent}] +\item[\ib{perm\textunderscore invent}] If true, always display your current inventory in a window (default is false). %.lp "" \\ This only makes sense for windowing system interfaces that implement this feature. For those that do, the -{\tt perminv\verb+_+mode} +{\tt perminv\textunderscore mode} option can be used to refine what gets displayed -for {\it perm\verb+_+invent\/}. +for {\it perm\textunderscore invent\/}. Setting that to a value other than {\it none\/} -while {\it perm\verb+_+invent\/} is false will change it to true. +while {\it perm\textunderscore invent\/} is false will change it to true. %.lp -\item[\ib{perminv\verb+_+mode}] +\item[\ib{perminv\textunderscore mode}] Augments the -{\tt perm\verb+_+invent} +{\tt perm\textunderscore invent} option. Value is one of %.PS "\f(CRin-use\fP" @@ -4874,7 +4871,7 @@ Value is one of \blist{\leftmargin \pcwidth \topsep 1mm \itemsep 0mm} %.PL \item[{\tt none}] -behave as if {\it perm\verb+_+invent\/} is false; +behave as if {\it perm\textunderscore invent\/} is false; \item[{all}] show all inventory except for gold; \item[{full}] @@ -4883,7 +4880,7 @@ show full inventory including gold; only show items which are in use (worn, wielded, lit lamp). %.PE \elist -Default is {\it none\/} but if {\it perm\verb+_+invent\/} gets set to true +Default is {\it none\/} but if {\it perm\textunderscore invent\/} gets set to true while it is {\it none\/} it will be changed to {\it all\/}. %.lp "" \\ @@ -4894,7 +4891,7 @@ included for {\it all\/} despite that mode normally omitting gold. \item[\ib{petattr}] Specifies one or more text highlighting attributes to use when showing pets on the map. -Effectively a superset of the {\it hilite\verb+_+pet\/} boolean option. +Effectively a superset of the {\it hilite\textunderscore pet\/} boolean option. Curses or tty interface only; value is one of none, bold, dim, underline, italic, blink, and inverse. Some of those choices might not work, @@ -4911,29 +4908,29 @@ it will be silently ignored. For example, ``{\tt horse}'' will only be honored when playing a knight. Cannot be set with the `{\tt O}' command. %.lp -\item[\ib{pickup\verb+_+burden}] +\item[\ib{pickup\textunderscore burden}] When you pick up an item that would exceed this encumbrance level (Unencumbered, Burdened, streSsed, straiNed, overTaxed, or overLoaded), you will be asked if you want to continue. (Default `S'). Persistent. %.lp -\item[\ib{pickup\verb+_+stolen}] +\item[\ib{pickup\textunderscore stolen}] If this option is on and ``{\it autopickup\/}'' is also on, try to pick up things that a monster stole from you, even if they aren't in -``{\it pickup\verb+_+types\/}'' or +``{\it pickup\textunderscore types\/}'' or match an autopickup exception. Default is on. Persistent. %.lp -\item[\ib{pickup\verb+_+thrown}] +\item[\ib{pickup\textunderscore thrown}] If this option is on and ``{\it autopickup\/}'' is also on, try to pick up things that you threw, even if they aren't in -``{\it pickup\verb+_+types\/}'' or +``{\it pickup\textunderscore types\/}'' or match an autopickup exception. Default is on. Persistent. %.lp -\item[\ib{pickup\verb+_+types}] +\item[\ib{pickup\textunderscore types}] Specify the object types to be picked up when ``{\it autopickup\/}'' is on. Default is all types. @@ -4943,20 +4940,20 @@ Persistent. The value is a list of object symbols, such as {\tt \verb&pickup_types:$?!&} to pick up gold, scrolls, and potions. You can use -``{\it autopickup\verb+_+exception\/}'' +``{\it autopickup\textunderscore exception\/}'' configuration file lines to further refine ``{\it autopickup\/}'' behavior. \\ %.lp "" -There is no way to set {\it pickup\verb+_+types\/} to ``{\it none}''. +There is no way to set {\it pickup\textunderscore types\/} to ``{\it none}''. (Setting it to an empty value reverts to ``{\it all}''.) If you want to avoid automatically picking up any types of items but do want to have {\it autopickup\/} on in order to have -{\it autopickup\verb+_+exceptions\/} control what you do and don't pick -up, you can set {\it pickup\verb+_+types\/} to `{\tt .}'. +{\it autopickup\textunderscore exceptions\/} control what you do and don't pick +up, you can set {\it pickup\textunderscore types\/} to `{\tt .}'. That is the type symbol for {\it venom\/} and you won't come across any venom items so won't unintentionally pick such up. %.lp -\item[\ib{pile\verb+_+limit}] +\item[\ib{pile\textunderscore limit}] When walking across a pile of objects on the floor, threshold at which the message ``there are few/several/many objects here'' is given instead of showing a popup list of those objects. A value of 0 means ``no limit'' @@ -4980,10 +4977,10 @@ something pushes the old item into your alternate weapon slot (default off). Likewise for the `{\tt a}' (apply) command if it causes the applied item to become wielded. Persistent. %.lp -\item[\ib{query\verb+_+menu}] +\item[\ib{query\textunderscore menu}] Use a menu when asked specific yes/no queries, instead of a prompt. %.lp -\item[\ib{quick\verb+_+farsight}] +\item[\ib{quick\textunderscore farsight}] When set, usually prevents the ``you sense your surroundings'' message where play pauses to allow you to browse the map whenever clairvoyance randomly activates. @@ -5004,7 +5001,7 @@ player will be prompted unless role forces a choice for race. unless role forces a choice for race. Cannot be set with the `{\tt O}' command. Persistent. %.lp -\item[\ib{rest\verb+_+on\verb+_+space}] +\item[\ib{rest\textunderscore on\textunderscore space}] Make the space bar a synonym for the `{\tt .}' (\#wait) command (default off). Persistent. %.lp @@ -5071,14 +5068,14 @@ results of moving. The default is {\it run\/}; versions prior to 3.4.1 used {\it teleport\/} only. Whether or not the effect is noticeable will depend upon the window port used or on the type of terminal. Persistent. %.lp -\item[\ib{safe\verb+_+pet}] +\item[\ib{safe\textunderscore pet}] Prevent you from (knowingly) attacking your pets (default on). Persistent. %.lp -\item[\ib{safe\verb+_+wait}] +\item[\ib{safe\textunderscore wait}] Prevents you from waiting or searching when next to a hostile monster (default on). Persistent. %.lp -\item[\ib{sanity\verb+_+check}] +\item[\ib{sanity\textunderscore check}] Evaluate monsters, objects, and map prior to each turn (default off). Debug mode only. %.lp @@ -5213,7 +5210,7 @@ Allow sounds to be emitted from an integrated sound library (default on). Display a sparkly effect when a monster (including yourself) is hit by an attack to which it is resistant (default on). Persistent. %.lp -\item[\ib{spot\verb+_+monsters}] +\item[\ib{spot\textunderscore monsters}] Show a message when hero notices a monster (default is off). %.lp \item[\ib{standout}] @@ -5224,13 +5221,13 @@ Controls how many turns status hilite behaviors highlight the field. If negated or set to zero, disables status hiliting. See ``{\it Configuring Status Hilites\/}'' for further information. %.lp -\item[\ib{status\verb+_+updates}] +\item[\ib{status\textunderscore updates}] Allow updates to the status lines at the bottom of the screen (default true). %.lp -\item[\ib{suppress\verb+_+alert}] +\item[\ib{suppress\textunderscore alert}] This option may be set to a {\it NetHack\/} version level to suppress alert notification messages about feature changes for that -and prior versions (for example, ``{\tt suppress\verb+_+alert:3.3.1}'') +and prior versions (for example, ``{\tt suppress\textunderscore alert:3.3.1}'') %.lp \item[\ib{symset}] This option may be used to select one of the named symbol sets found within @@ -5240,7 +5237,7 @@ Use ``{\tt symset:default}'' to explicitly select the default symbols. \item[\ib{time}] Show the elapsed game time in turns on bottom line (default off). Persistent. %.lp -\item[\ib{timed\verb+_+delay}] +\item[\ib{timed\textunderscore delay}] When pausing momentarily for display effect, such as with explosions and moving objects, use a timer rather than sending extra characters to the screen. (Applies to ``tty'' and ``curses'' interfaces only; ``X11'' interface always @@ -5263,10 +5260,10 @@ the score list around after game end on a terminal or emulating window. Allow the travel command via mouse click (default on). Turning this option off will prevent the game from attempting unintended moves if you make inadvertent mouse clicks on the map window. -Does not affect traveling via the `{\tt \verb+_+}' (``{\tt \#travel}'') +Does not affect traveling via the `{\tt \textunderscore }' (``{\tt \#travel}'') command. Persistent. % %.lp -% \item[ib{travel\verb+_+debug}] +% \item[ib{travel\textunderscore debug}] % Display intended path during each step of travel (default off). % Debug mode only. %.lp @@ -5277,7 +5274,7 @@ Setting this option on or off in the config file will skip the query. \item[\ib{verbose}] Provide more commentary during the game (default on). Persistent. %.lp -\item[\ib{whatis\verb+_+coord}] +\item[\ib{whatis\textunderscore coord}] When using the `{\tt /}' or `{\tt ;}' commands to look around on the map with ``{\tt autodescribe}'' on, display coordinates after the description. @@ -5298,13 +5295,13 @@ The possible settings are: %.lp "" The -{\it whatis\verb+_+coord\/} +{\it whatis\textunderscore coord\/} option is also used with the `{\tt /m}', `{\tt /M}', `{\tt /o}', and `{\tt /O}' sub-commands of `{\tt /}', where the `{\it none\/}' setting is overridden with `{\it map}'. %.lp -\item[\ib{whatis\verb+_+filter}] +\item[\ib{whatis\textunderscore filter}] When getting a location on the map, and using the keys to cycle through next and previous targets, allows filtering the possible targets. (default none)\\ @@ -5327,12 +5324,12 @@ the door you were last moving towards.\\ Filtering can also be changed when getting a location with the ``getpos.filter'' key. %.lp -\item[\ib{whatis\verb+_+menu}] +\item[\ib{whatis\textunderscore menu}] When getting a location on the map, and using a key to cycle through next and previous targets, use a menu instead to pick a target. (default off) %.lp -\item[\ib{whatis\verb+_+moveskip}] +\item[\ib{whatis\textunderscore moveskip}] When getting a location on the map, and using shifted movement keys or meta-digit keys to fast-move, instead of moving 8 units at a time, move by skipping the same glyphs. @@ -5382,13 +5379,13 @@ with the `{\tt O}' command. \blist{} %.lp -\item[\ib{align\verb+_+message}] +\item[\ib{align\textunderscore message}] Where to align or place the message window (top, bottom, left, or right) %.lp -\item[\ib{align\verb+_+status}] +\item[\ib{align\textunderscore status}] Where to align or place the status window (top, bottom, left, or right). %.lp -\item[\ib{ascii\verb+_+map}] +\item[\ib{ascii\textunderscore map}] %.hw DECgraphics IBMgraphics \% don't hyphenate these \hyphenation{DECgraphics IBMgraphics} If {\it NetHack\/} can, it should display the map using simple @@ -5397,47 +5394,47 @@ In some cases, characters can be augmented with line-drawing symbols; use the {\tt symset} option to select a symbol set such as {\it DECgraphics\/} or {\it IBMgraphics\/} if your display supports them. -Setting {\tt ascii\verb+_+map} to {\it True\/} forces -{\tt tiled\verb+_+map} to be {\it False}. +Setting {\tt ascii\textunderscore map} to {\it True\/} forces +{\tt tiled\textunderscore map} to be {\it False}. %.lp \item[\ib{color}] If {\it NetHack\/} can, it should display color for different monsters, objects, and dungeon features (default on). %.lp -\item[\ib{eight\verb+_+bit\verb+_+tty}] +\item[\ib{eight\textunderscore bit\textunderscore tty}] If {\it NetHack\/} can, it should pass eight-bit character values (for example, specified with the {\it traps \/} option) straight through to your terminal (default off). %.lp -\item[\ib{font\verb+_+map}] +\item[\ib{font\textunderscore map}] If {\it NetHack\/} can, it should use a font by the chosen name for the map window. %.lp -\item[\ib{font\verb+_+menu}] +\item[\ib{font\textunderscore menu}] If {\it NetHack\/} can, it should use a font by the chosen name for menu windows. %.lp -\item[\ib{font\verb+_+message}] +\item[\ib{font\textunderscore message}] If {\it NetHack\/} can, it should use a font by the chosen name for the message window. %.lp -\item[\ib{font\verb+_+status}] +\item[\ib{font\textunderscore status}] If {\it NetHack\/} can, it should use a font by the chosen name for the status window. %.lp -\item[\ib{font\verb+_+text}] +\item[\ib{font\textunderscore text}] If {\it NetHack\/} can, it should use a font by the chosen name for text windows. %.lp -\item[\ib{font\verb+_+size\verb+_+map}] +\item[\ib{font\textunderscore size\textunderscore map}] If {\it NetHack\/} can, it should use this size font for the map window. %.lp -\item[\ib{font\verb+_+size\verb+_+menu}] +\item[\ib{font\textunderscore size\textunderscore menu}] If {\it NetHack\/} can, it should use this size font for menu windows. %.lp -\item[\ib{font\verb+_+size\verb+_+message}] +\item[\ib{font\textunderscore size\textunderscore message}] If {\it NetHack\/} can, it should use this size font for the message window. %.lp -\item[\ib{font\verb+_+size\verb+_+status}] +\item[\ib{font\textunderscore size\textunderscore status}] If {\it NetHack\/} can, it should use this size font for the status window. %.lp -\item[\ib{font\verb+_+size\verb+_+text}] +\item[\ib{font\textunderscore size\textunderscore text}] If {\it NetHack\/} can, it should use this size font for text windows. %.lp \item[\ib{fullscreen}] @@ -5448,30 +5445,30 @@ Use color text and/or highlighting attributes when displaying some non-map data (such as menu selector letters). Curses interface only; default is on. %.lp -\item[\ib{large\verb+_+font}] +\item[\ib{large\textunderscore font}] If {\it NetHack\/} can, it should use a large font. %.lp -\item[\ib{map\verb+_+mode}] +\item[\ib{map\textunderscore mode}] If {\it NetHack\/} can, it should display the map in the manner specified. %.lp -\item[\ib{player\verb+_+selection}] +\item[\ib{player\textunderscore selection}] If {\it NetHack\/} can, it should pop up dialog boxes or use prompts for character selection. %.lp -\item[\ib{popup\verb+_+dialog}] +\item[\ib{popup\textunderscore dialog}] If {\it NetHack\/} can, it should pop up dialog boxes for input. %.lp -\item[\ib{preload\verb+_+tiles}] +\item[\ib{preload\textunderscore tiles}] If {\it NetHack\/} can, it should preload tiles into memory. For example, in the protected mode MS-DOS version, control whether tiles get pre-loaded into RAM at the start of the game. Doing so enhances performance of the tile graphics, but uses more memory. (default on). Cannot be set with the `{\tt O}' command. %.lp -\item[\ib{scroll\verb+_+amount}] +\item[\ib{scroll\textunderscore amount}] If {\it NetHack\/} can, it should scroll the display by this number of cells -when the hero reaches the scroll\verb+_+margin. +when the hero reaches the scroll\textunderscore margin. %.lp -\item[\ib{scroll\verb+_+margin}] +\item[\ib{scroll\textunderscore margin}] If {\it NetHack\/} can, it should scroll the display when the hero or cursor is this number of cells away from the edge of the window. %.lp @@ -5483,7 +5480,7 @@ choose from at game startup, if it can. Not all ports support this option. If {\it NetHack\/} can, it should display an onscreen keyboard. Handhelds are most likely to support this option. %.lp -\item[\ib{splash\verb+_+screen}] +\item[\ib{splash\textunderscore screen}] If {\it NetHack\/} can, it should display an opening splash screen when it starts up (default yes). %.lp @@ -5500,7 +5497,7 @@ command. %.lp "" The {\tt curses} interface does likewise if the -{\it align\verb+_+status\/} +{\it align\textunderscore status\/} option is set to {\it top\/} or {\it bottom\/} but ignores {\it statuslines\/} when set to {\it left\/} or {\it right}. @@ -5521,16 +5518,16 @@ older than {\tt qt-5.9}, can only be set in the run-time configuration file or via NETHACKOPTIONS, not during play with the `{\tt O}' command.) %.lp -\item[\ib{term\verb+_+cols} {\normalfont and}] +\item[\ib{term\textunderscore cols} {\normalfont and}] %.lp -\item[\ib{term\verb+_+rows}] +\item[\ib{term\textunderscore rows}] Curses interface only. Number of columns and rows to use for the display. Curses will attempt to resize to the values specified but will settle for smaller sizes if they are too big. Default is the current window size. %.lp -\item[\ib{tile\verb+_+file}] +\item[\ib{tile\textunderscore file}] Specify the name of an alternative tile file to override the default. \\ %.lp "" @@ -5538,30 +5535,30 @@ Note: the X11 interface uses X resources rather than NetHack's options to select an alternate tile file. See {\tt NetHack.ad}, the sample X ``application defaults'' file. %.lp -\item[\ib{tile\verb+_+height}] +\item[\ib{tile\textunderscore height}] Specify the preferred height of each tile in a tile capable port. %.lp -\item[\ib{tile\verb+_+width}] +\item[\ib{tile\textunderscore width}] Specify the preferred width of each tile in a tile capable port %.lp -\item[\ib{tiled\verb+_+map}] +\item[\ib{tiled\textunderscore map}] If {\it NetHack\/} can, it should display the map using {\it tiles} graphics rather than simple characters (letters and punctuation, possibly augmented by line-drawing symbols). -Setting {\tt tiled\verb+_+map} to {\it True\/} forces -{\tt ascii\verb+_+map} to be {\it False}. +Setting {\tt tiled\textunderscore map} to {\it True\/} forces +{\tt ascii\textunderscore map} to be {\it False}. %.lp -\item[\ib{use\verb+_+darkgray}] +\item[\ib{use\textunderscore darkgray}] Use bold black instead of blue for black glyphs (TTY only). %.lp -\item[\ib{use\verb+_+inverse}] +\item[\ib{use\textunderscore inverse}] If {\it NetHack\/} can, it should display inverse when the game specifies it. %.lp -\item[\ib{use\verb+_+menu\verb+_+glyphs}] +\item[\ib{use\textunderscore menu\textunderscore glyphs}] If {\it NetHack\/} can, it should display glyphs next to objects in the inventory. %.lp -\item[\ib{vary\verb+_+msgcount}] +\item[\ib{vary\textunderscore msgcount}] If {\it NetHack\/} can, it should display this number of messages at a time in the message window. %.lp @@ -5577,8 +5574,8 @@ Acceptable values are {\tt 1} --- on, always show borders\\ {\tt 2} --- auto, on display is at least (\verb&24+2&)x(\verb&80+2&) [default]\\ -{\tt 3} --- on, except forced off for perm\verb+_+invent\\ -{\tt 4} --- auto, except forced off for perm\verb+_+invent\\ +{\tt 3} --- on, except forced off for perm\textunderscore invent\\ +{\tt 4} --- auto, except forced off for perm\textunderscore invent\\ %.ei %.ed @@ -5677,7 +5674,7 @@ ESC into a meta-shifted version of the second character (default off). %.lp "" This conversion is only done for commands, not for other input prompts. Note that typing one or more digits as a count prefix prior to a -command---preceded by {\tt n} if the {\it number\verb+_+pad\/} +command---preceded by {\tt n} if the {\it number\textunderscore pad\/} option is set---is also subject to this conversion, so attempting to abort the count by typing ESC will leave {\it NetHack\/} waiting for another @@ -5720,15 +5717,15 @@ Setting {\it autodetect\/} attempts {\it vesa\/}, then {\it vga\/}, and finally sets {\it default\/} if neither of those modes works. Cannot be set with the `{\tt O}' command. %.lp -\item[\ib{video\verb+_+height}] +\item[\ib{video\textunderscore height}] Set the VGA mode resolution height (MS-DOS only, with video:vesa) %.lp -\item[\ib{video\verb+_+width}] +\item[\ib{video\textunderscore width}] Set the VGA mode resolution width (MS-DOS only, with video:vesa) %.lp \item[\ib{videocolors}] \begin{sloppypar} -Set the color palette for PC systems using NO\verb+_+TERMS +Set the color palette for PC systems using NO\textunderscore TERMS (default 4-2-6-1-5-3-15-12-10-14-9-13-11, {\it PC\/ NetHack\/} only). The order of colors is red, green, brown, blue, magenta, cyan, bright.white, bright.red, bright.green, yellow, bright.blue, @@ -5762,18 +5759,18 @@ and User sounds. %.pg You can further refine the behavior of the ``{\tt autopickup}'' option -beyond what is available through the ``{\tt pickup\verb+_+types}'' option. +beyond what is available through the ``{\tt pickup\textunderscore types}'' option. %.pg -By placing ``{\tt autopickup\verb+_+exception}'' lines in your configuration +By placing ``{\tt autopickup\textunderscore exception}'' lines in your configuration file, you can define patterns to be checked when the game is about to autopickup something. \blist{} %.lp -\item[\ib{autopickup\verb+_+exception}] -Sets an exception to the ``{\it pickup\verb+_+types}'' option. -The {\it autopickup\verb+_+exception\/} option should be followed by a regular +\item[\ib{autopickup\textunderscore exception}] +Sets an exception to the ``{\it pickup\textunderscore types}'' option. +The {\it autopickup\textunderscore exception\/} option should be followed by a regular expression to be used as a pattern to match against the singular form of the description of an object at your location. @@ -5787,7 +5784,7 @@ character in the pattern, specifically: %.ei %.ed -The {\it autopickup\verb+_+exception\/} rules are processed in the order +The {\it autopickup\textunderscore exception\/} rules are processed in the order in which they appear in your configuration file, thus allowing a later rule to override an earlier rule. @@ -5795,7 +5792,7 @@ later rule to override an earlier rule. Exceptions can be set with the `{\tt O}' command, but because they are not included in your configuration file, they won't be in effect if you save and then restore your game. -{\it autopickup\verb+_+exception\/} rules are not saved with the game. +{\it autopickup\textunderscore exception\/} rules are not saved with the game. \elist %.lp "Here are some examples:" @@ -5866,7 +5863,7 @@ can only be bound to a single key. %.lp \item[{\bb{count}}] Prefix key to start a count, to repeat a command this many times. -With {\it number\verb+_+pad\/} only. Default is~`{\tt n}'. +With {\it number\textunderscore pad\/} only. Default is~`{\tt n}'. %.lp \item[{\bb{getdir.help}}] When asked for a direction, the key to show the help. Default is~`{\tt ?}'. @@ -5879,7 +5876,7 @@ the getpos.pick.once key (default `{\tt ,}') or the getpos.pick key (default `{\tt .}') to finish as if performing a left or right click. Only useful when using the {\tt \#therecmdmenu} command. -Default is~`{\tt \verb+_+}'. +Default is~`{\tt \textunderscore }'. %.lp \item[{\bb{getdir.self}}] When asked for a direction, the key to target yourself. Default is~`{\tt .}'. @@ -6111,7 +6108,7 @@ a menu line will be used for the line. %.pg Note that if you intend to have one or more color specifications match ``~uncursed~'', you will probably want to turn the -{\it implicit\verb+_+uncursed\/} +{\it implicit\textunderscore uncursed\/} option off so that all items known to be uncursed are actually displayed with the ``uncursed'' description. @@ -6389,190 +6386,190 @@ character. Default & Symbol Name & Description\\ \hline \hline \endhead -\verb@ @ & S\verb+_+air & (air)\\ -\_ & S\verb+_+altar & (altar)\\ -\verb@"@ & S\verb+_+amulet & (amulet)\\ -\verb@A@ & S\verb+_+angel & (angelic being)\\ -\verb@a@ & S\verb+_+ant & (ant or other insect)\\ -\verb@^@ & S\verb+_+anti\verb+_+magic\verb+_+trap & (anti-magic field)\\ -\verb@[@ & S\verb+_+armor & (suit or piece of armor)\\ -\verb@[@ & S\verb+_+armour & (suit or piece of armor)\\ -\verb@^@ & S\verb+_+arrow\verb+_+trap & (arrow trap)\\ -\verb@0@ & S\verb+_+ball & (iron ball)\\ -\# & S\verb+_+bars & (iron bars)\\ -\verb@B@ & S\verb+_+bat & (bat or bird)\\ -\verb@^@ & S\verb+_+bear\verb+_+trap & (bear trap)\\ -\verb@-@ & S\verb+_+blcorn & (bottom left corner)\\ -\verb@b@ & S\verb+_+blob & (blob)\\ -\verb@+@ & S\verb+_+book & (spellbook)\\ -\verb@)@ & S\verb+_+boomleft & (boomerang open left)\\ -\verb@(@ & S\verb+_+boomright & (boomerang open right)\\ -\verb@`@ & S\verb+_+boulder & (boulder)\\ -\verb@-@ & S\verb+_+brcorn & (bottom right corner)\\ -\verb@>@ & S\verb+_+brdnladder & (branch ladder down)\\ -\verb@>@ & S\verb+_+brdnstair & (branch staircase down)\\ -\verb@<@ & S\verb+_+brupladder & (branch ladder up)\\ -\verb@<@ & S\verb+_+brupstair & (branch staircase up)\\ -\verb@C@ & S\verb+_+centaur & (centaur)\\ -\verb@_@ & S\verb+_+chain & (iron chain)\\ -\# & S\verb+_+cloud & (cloud)\\ -\verb@c@ & S\verb+_+cockatrice & (cockatrice)\\ -\$ & S\verb+_+coin & (pile of coins)\\ -\# & S\verb+_+corr & (corridor)\\ -\verb@-@ & S\verb+_+crwall & (wall)\\ -\verb@-@ & S\verb+_+darkroom & (dark room)\\ -\verb@^@ & S\verb+_+dart\verb+_+trap & (dart trap)\\ -\verb@&@ & S\verb+_+demon & (major demon)\\ -\verb@*@ & S\verb+_+digbeam & (dig beam)\\ -\verb@>@ & S\verb+_+dnladder & (ladder down)\\ -\verb@>@ & S\verb+_+dnstair & (staircase down)\\ -\verb@d@ & S\verb+_+dog & (dog or other canine)\\ -\verb@D@ & S\verb+_+dragon & (dragon)\\ -\verb@;@ & S\verb+_+eel & (sea monster)\\ -\verb@E@ & S\verb+_+elemental & (elemental)\\ -\# & S\verb+_+engrcorr & (engraving in a corridor)\\ -\verb@`@ & S\verb+_+engroom & (engraving in a room)\\ -\verb@/@ & S\verb+_+expl\verb+_+tl & (explosion top left)\\ -\verb@-@ & S\verb+_+expl\verb+_+tc & (explosion top center)\\ -\verb@\@ & S\verb+_+expl\verb+_+tr & (explosion top right)\\ -\verb@|@ & S\verb+_+expl\verb+_+ml & (explosion middle left)\\ -\verb@ @ & S\verb+_+expl\verb+_+mc & (explosion middle center)\\ -\verb@|@ & S\verb+_+expl\verb+_+mr & (explosion middle right)\\ -\verb@\@ & S\verb+_+expl\verb+_+bl & (explosion bottom left)\\ -\verb@-@ & S\verb+_+expl\verb+_+bc & (explosion bottom center)\\ -\verb@/@ & S\verb+_+expl\verb+_+br & (explosion bottom right)\\ -\verb@e@ & S\verb+_+eye & (eye or sphere)\\ -\verb@^@ & S\verb+_+falling\verb+_+rock\verb+_+trap & (falling rock trap)\\ -\verb@f@ & S\verb+_+feline & (cat or other feline)\\ -\verb@^@ & S\verb+_+fire\verb+_+trap & (fire trap)\\ -\verb@!@ & S\verb+_+flashbeam & (flash beam)\\ -\% & S\verb+_+food & (piece of food)\\ -\{ & S\verb+_+fountain & (fountain)\\ -\verb@F@ & S\verb+_+fungus & (fungus or mold)\\ -\verb@*@ & S\verb+_+gem & (gem or rock)\\ -\verb@ @ & S\verb+_+ghost & (ghost)\\ -\verb@H@ & S\verb+_+giant & (giant humanoid)\\ -\verb@G@ & S\verb+_+gnome & (gnome)\\ -\verb@'@ & S\verb+_+golem & (golem)\\ -\verb@|@ & S\verb+_+grave & (grave)\\ -\verb@g@ & S\verb+_+gremlin & (gremlin)\\ -\verb@-@ & S\verb+_+hbeam & (wall)\\ -\# & S\verb+_+hcdbridge & (horizontal raised drawbridge)\\ -\verb@+@ & S\verb+_+hcdoor & (closed door)\\ -\verb@.@ & S\verb+_+hodbridge & (horizontal lowered drawbridge)\\ -\verb@|@ & S\verb+_+hodoor & (open door)\\ -\verb\^\ & S\verb+_+hole & (hole)\\ -\verb~@~ & S\verb+_+human & (human or elf)\\ -\verb@h@ & S\verb+_+humanoid & (humanoid)\\ -\verb@-@ & S\verb+_+hwall & (horizontal wall)\\ -\verb@.@ & S\verb+_+ice & (ice)\\ -\verb@i@ & S\verb+_+imp & (imp or minor demon)\\ -\verb@I@ & S\verb+_+invisible & (invisible monster)\\ -\verb@J@ & S\verb+_+jabberwock & (jabberwock)\\ -\verb@j@ & S\verb+_+jelly & (jelly)\\ -\verb@k@ & S\verb+_+kobold & (kobold)\\ -\verb@K@ & S\verb+_+kop & (Keystone Kop)\\ -\verb@^@ & S\verb+_+land\verb+_+mine & (land mine)\\ -\verb@}@ & S\verb+_+lava & (molten lava)\\ -\verb@}@ & S\verb+_+lavawall & (wall of lava)\\ -\verb@l@ & S\verb+_+leprechaun & (leprechaun)\\ -\verb@^@ & S\verb+_+level\verb+_+teleporter & (level teleporter)\\ -\verb@L@ & S\verb+_+lich & (lich)\\ -\verb@y@ & S\verb+_+light & (light)\\ -\# & S\verb+_+litcorr & (lit corridor)\\ -\verb@:@ & S\verb+_+lizard & (lizard)\\ -\verb@\@ & S\verb+_+lslant & (wall)\\ -\verb@^@ & S\verb+_+magic\verb+_+portal & (magic portal)\\ -\verb@^@ & S\verb+_+magic\verb+_+trap & (magic trap)\\ -\verb@m@ & S\verb+_+mimic & (mimic)\\ -\verb@]@ & S\verb+_+mimic\verb+_+def & (mimic)\\ -\verb@M@ & S\verb+_+mummy & (mummy)\\ -\verb@N@ & S\verb+_+naga & (naga)\\ -\verb@.@ & S\verb+_+ndoor & (doorway)\\ -\verb@n@ & S\verb+_+nymph & (nymph)\\ -\verb@O@ & S\verb+_+ogre & (ogre)\\ -\verb@o@ & S\verb+_+orc & (orc)\\ -\verb@p@ & S\verb+_+piercer & (piercer)\\ -\verb@^@ & S\verb+_+pit & (pit)\\ -\# & S\verb+_+poisoncloud & (poison cloud)\\ -\verb@^@ & S\verb+_+polymorph\verb+_+trap & (polymorph trap)\\ -\verb@}@ & S\verb+_+pool & (water)\\ -\verb@!@ & S\verb+_+potion & (potion)\\ -\verb@P@ & S\verb+_+pudding & (pudding or ooze)\\ -\verb@q@ & S\verb+_+quadruped & (quadruped)\\ -\verb@Q@ & S\verb+_+quantmech & (quantum mechanic)\\ -\verb@=@ & S\verb+_+ring & (ring)\\ -\verb@`@ & S\verb+_+rock & (boulder or statue)\\ -\verb@r@ & S\verb+_+rodent & (rodent)\\ -\verb@^@ & S\verb+_+rolling\verb+_+boulder\verb+_+trap & (rolling boulder trap)\\ -\verb@.@ & S\verb+_+room & (floor of a room)\\ -\verb@/@ & S\verb+_+rslant & (wall)\\ -\verb@^@ & S\verb+_+rust\verb+_+trap & (rust trap)\\ -\verb@R@ & S\verb+_+rustmonst & (rust monster or disenchanter)\\ -\verb@?@ & S\verb+_+scroll & (scroll)\\ -\# & S\verb+_+sink & (sink)\\ -\verb@^@ & S\verb+_+sleeping\verb+_+gas\verb+_+trap & (sleeping gas trap)\\ -\verb@S@ & S\verb+_+snake & (snake)\\ -\verb@s@ & S\verb+_+spider & (arachnid or centipede)\\ -\verb@^@ & S\verb+_+spiked\verb+_+pit & (spiked pit)\\ -\verb@^@ & S\verb+_+squeaky\verb+_+board & (squeaky board)\\ -\verb@0@ & S\verb+_+ss1 & (magic shield 1 of 4)\\ -\# & S\verb+_+ss2 & (magic shield 2 of 4)\\ -\verb+@+ & S\verb+_+ss3 & (magic shield 3 of 4)\\ -\verb@*@ & S\verb+_+ss4 & (magic shield 4 of 4)\\ -\verb@^@ & S\verb+_+statue\verb+_+trap & (statue trap)\\ -\verb@ @ & S\verb+_+stone & (solid rock)\\ -\verb@]@ & S\verb+_+strange\verb+_+obj & (strange object)\\ -\verb@-@ & S\verb+_+sw\verb+_+bc & (swallow bottom center)\\ -\verb@\@ & S\verb+_+sw\verb+_+bl & (swallow bottom left)\\ -\verb@/@ & S\verb+_+sw\verb+_+br & (swallow bottom right )\\ -\verb@|@ & S\verb+_+sw\verb+_+ml & (swallow middle left)\\ -\verb@|@ & S\verb+_+sw\verb+_+mr & (swallow middle right)\\ -\verb@-@ & S\verb+_+sw\verb+_+tc & (swallow top center)\\ -\verb@/@ & S\verb+_+sw\verb+_+tl & (swallow top left)\\ -\verb@\@ & S\verb+_+sw\verb+_+tr & (swallow top right)\\ -\verb@-@ & S\verb+_+tdwall & (wall)\\ -\verb@^@ & S\verb+_+teleportation\verb+_+trap & (teleportation trap)\\ -\verb@\@ & S\verb+_+throne & (opulent throne)\\ -\verb@-@ & S\verb+_+tlcorn & (top left corner)\\ -\verb@|@ & S\verb+_+tlwall & (wall)\\ -\verb@(@ & S\verb+_+tool & (useful item (pick-axe, key, lamp...))\\ -\verb@^@ & S\verb+_+trap\verb+_+door & (trap door)\\ -\verb@t@ & S\verb+_+trapper & (trapper or lurker above)\\ -\verb@-@ & S\verb+_+trcorn & (top right corner)\\ -\# & S\verb+_+tree & (tree)\\ -\verb@T@ & S\verb+_+troll & (troll)\\ -\verb@|@ & S\verb+_+trwall & (wall)\\ -\verb@-@ & S\verb+_+tuwall & (wall)\\ -\verb@U@ & S\verb+_+umber & (umber hulk)\\ -\verb@ @ & S\verb+_+unexplored & (unexplored terrain)\\ -\verb@u@ & S\verb+_+unicorn & (unicorn or horse)\\ -\verb@<@ & S\verb+_+upladder & (ladder up)\\ -\verb@<@ & S\verb+_+upstair & (staircase up)\\ -\verb@V@ & S\verb+_+vampire & (vampire)\\ -\verb@|@ & S\verb+_+vbeam & (wall)\\ -\# & S\verb+_+vcdbridge & (vertical raised drawbridge)\\ -\verb@+@ & S\verb+_+vcdoor & (closed door)\\ -\verb@.@ & S\verb+_+venom & (splash of venom)\\ -\verb@^@ & S\verb+_+vibrating\verb+_+square & (vibrating square)\\ -\verb@.@ & S\verb+_+vodbridge & (vertical lowered drawbridge)\\ -\verb@-@ & S\verb+_+vodoor & (open door)\\ -\verb@v@ & S\verb+_+vortex & (vortex)\\ -\verb@|@ & S\verb+_+vwall & (vertical wall)\\ -\verb@/@ & S\verb+_+wand & (wand)\\ -\verb@}@ & S\verb+_+water & (water)\\ -\verb@)@ & S\verb+_+weapon & (weapon)\\ -\verb@"@ & S\verb+_+web & (web)\\ -\verb@w@ & S\verb+_+worm & (worm)\\ -\verb@~@ & S\verb+_+worm\verb+_+tail & (long worm tail)\\ -\verb@W@ & S\verb+_+wraith & (wraith)\\ -\verb@x@ & S\verb+_+xan & (xan or other extraordinary insect)\\ -\verb@X@ & S\verb+_+xorn & (xorn)\\ -\verb@Y@ & S\verb+_+yeti & (apelike creature)\\ -\verb@Z@ & S\verb+_+zombie & (zombie)\\ -\verb@z@ & S\verb+_+zruty & (zruty)\\ -\verb@ @ & S\verb+_+pet\verb+_+override & (any pet if ACCESSIBILITY=1 is set)\\ -\verb@ @ & S\verb+_+hero\verb+_+override & (hero if ACCESSIBILITY=1 is set) +\verb@ @ & S\textunderscore air & (air)\\ +\_ & S\textunderscore altar & (altar)\\ +\verb@"@ & S\textunderscore amulet & (amulet)\\ +\verb@A@ & S\textunderscore angel & (angelic being)\\ +\verb@a@ & S\textunderscore ant & (ant or other insect)\\ +\verb@^@ & S\textunderscore anti\textunderscore magic\textunderscore trap & (anti-magic field)\\ +\verb@[@ & S\textunderscore armor & (suit or piece of armor)\\ +\verb@[@ & S\textunderscore armour & (suit or piece of armor)\\ +\verb@^@ & S\textunderscore arrow\textunderscore trap & (arrow trap)\\ +\verb@0@ & S\textunderscore ball & (iron ball)\\ +\# & S\textunderscore bars & (iron bars)\\ +\verb@B@ & S\textunderscore bat & (bat or bird)\\ +\verb@^@ & S\textunderscore bear\textunderscore trap & (bear trap)\\ +\verb@-@ & S\textunderscore blcorn & (bottom left corner)\\ +\verb@b@ & S\textunderscore blob & (blob)\\ +\verb@+@ & S\textunderscore book & (spellbook)\\ +\verb@)@ & S\textunderscore boomleft & (boomerang open left)\\ +\verb@(@ & S\textunderscore boomright & (boomerang open right)\\ +\verb@`@ & S\textunderscore boulder & (boulder)\\ +\verb@-@ & S\textunderscore brcorn & (bottom right corner)\\ +\verb@>@ & S\textunderscore brdnladder & (branch ladder down)\\ +\verb@>@ & S\textunderscore brdnstair & (branch staircase down)\\ +\verb@<@ & S\textunderscore brupladder & (branch ladder up)\\ +\verb@<@ & S\textunderscore brupstair & (branch staircase up)\\ +\verb@C@ & S\textunderscore centaur & (centaur)\\ +\verb@_@ & S\textunderscore chain & (iron chain)\\ +\# & S\textunderscore cloud & (cloud)\\ +\verb@c@ & S\textunderscore cockatrice & (cockatrice)\\ +\$ & S\textunderscore coin & (pile of coins)\\ +\# & S\textunderscore corr & (corridor)\\ +\verb@-@ & S\textunderscore crwall & (wall)\\ +\verb@-@ & S\textunderscore darkroom & (dark room)\\ +\verb@^@ & S\textunderscore dart\textunderscore trap & (dart trap)\\ +\verb@&@ & S\textunderscore demon & (major demon)\\ +\verb@*@ & S\textunderscore digbeam & (dig beam)\\ +\verb@>@ & S\textunderscore dnladder & (ladder down)\\ +\verb@>@ & S\textunderscore dnstair & (staircase down)\\ +\verb@d@ & S\textunderscore dog & (dog or other canine)\\ +\verb@D@ & S\textunderscore dragon & (dragon)\\ +\verb@;@ & S\textunderscore eel & (sea monster)\\ +\verb@E@ & S\textunderscore elemental & (elemental)\\ +\# & S\textunderscore engrcorr & (engraving in a corridor)\\ +\verb@`@ & S\textunderscore engroom & (engraving in a room)\\ +\verb@/@ & S\textunderscore expl\textunderscore tl & (explosion top left)\\ +\verb@-@ & S\textunderscore expl\textunderscore tc & (explosion top center)\\ +\verb@\@ & S\textunderscore expl\textunderscore tr & (explosion top right)\\ +\verb@|@ & S\textunderscore expl\textunderscore ml & (explosion middle left)\\ +\verb@ @ & S\textunderscore expl\textunderscore mc & (explosion middle center)\\ +\verb@|@ & S\textunderscore expl\textunderscore mr & (explosion middle right)\\ +\verb@\@ & S\textunderscore expl\textunderscore bl & (explosion bottom left)\\ +\verb@-@ & S\textunderscore expl\textunderscore bc & (explosion bottom center)\\ +\verb@/@ & S\textunderscore expl\textunderscore br & (explosion bottom right)\\ +\verb@e@ & S\textunderscore eye & (eye or sphere)\\ +\verb@^@ & S\textunderscore falling\textunderscore rock\textunderscore trap & (falling rock trap)\\ +\verb@f@ & S\textunderscore feline & (cat or other feline)\\ +\verb@^@ & S\textunderscore fire\textunderscore trap & (fire trap)\\ +\verb@!@ & S\textunderscore flashbeam & (flash beam)\\ +\% & S\textunderscore food & (piece of food)\\ +\{ & S\textunderscore fountain & (fountain)\\ +\verb@F@ & S\textunderscore fungus & (fungus or mold)\\ +\verb@*@ & S\textunderscore gem & (gem or rock)\\ +\verb@ @ & S\textunderscore ghost & (ghost)\\ +\verb@H@ & S\textunderscore giant & (giant humanoid)\\ +\verb@G@ & S\textunderscore gnome & (gnome)\\ +\verb@'@ & S\textunderscore golem & (golem)\\ +\verb@|@ & S\textunderscore grave & (grave)\\ +\verb@g@ & S\textunderscore gremlin & (gremlin)\\ +\verb@-@ & S\textunderscore hbeam & (wall)\\ +\# & S\textunderscore hcdbridge & (horizontal raised drawbridge)\\ +\verb@+@ & S\textunderscore hcdoor & (closed door)\\ +\verb@.@ & S\textunderscore hodbridge & (horizontal lowered drawbridge)\\ +\verb@|@ & S\textunderscore hodoor & (open door)\\ +\verb\^\ & S\textunderscore hole & (hole)\\ +\verb~@~ & S\textunderscore human & (human or elf)\\ +\verb@h@ & S\textunderscore humanoid & (humanoid)\\ +\verb@-@ & S\textunderscore hwall & (horizontal wall)\\ +\verb@.@ & S\textunderscore ice & (ice)\\ +\verb@i@ & S\textunderscore imp & (imp or minor demon)\\ +\verb@I@ & S\textunderscore invisible & (invisible monster)\\ +\verb@J@ & S\textunderscore jabberwock & (jabberwock)\\ +\verb@j@ & S\textunderscore jelly & (jelly)\\ +\verb@k@ & S\textunderscore kobold & (kobold)\\ +\verb@K@ & S\textunderscore kop & (Keystone Kop)\\ +\verb@^@ & S\textunderscore land\textunderscore mine & (land mine)\\ +\verb@}@ & S\textunderscore lava & (molten lava)\\ +\verb@}@ & S\textunderscore lavawall & (wall of lava)\\ +\verb@l@ & S\textunderscore leprechaun & (leprechaun)\\ +\verb@^@ & S\textunderscore level\textunderscore teleporter & (level teleporter)\\ +\verb@L@ & S\textunderscore lich & (lich)\\ +\verb@y@ & S\textunderscore light & (light)\\ +\# & S\textunderscore litcorr & (lit corridor)\\ +\verb@:@ & S\textunderscore lizard & (lizard)\\ +\verb@\@ & S\textunderscore lslant & (wall)\\ +\verb@^@ & S\textunderscore magic\textunderscore portal & (magic portal)\\ +\verb@^@ & S\textunderscore magic\textunderscore trap & (magic trap)\\ +\verb@m@ & S\textunderscore mimic & (mimic)\\ +\verb@]@ & S\textunderscore mimic\textunderscore def & (mimic)\\ +\verb@M@ & S\textunderscore mummy & (mummy)\\ +\verb@N@ & S\textunderscore naga & (naga)\\ +\verb@.@ & S\textunderscore ndoor & (doorway)\\ +\verb@n@ & S\textunderscore nymph & (nymph)\\ +\verb@O@ & S\textunderscore ogre & (ogre)\\ +\verb@o@ & S\textunderscore orc & (orc)\\ +\verb@p@ & S\textunderscore piercer & (piercer)\\ +\verb@^@ & S\textunderscore pit & (pit)\\ +\# & S\textunderscore poisoncloud & (poison cloud)\\ +\verb@^@ & S\textunderscore polymorph\textunderscore trap & (polymorph trap)\\ +\verb@}@ & S\textunderscore pool & (water)\\ +\verb@!@ & S\textunderscore potion & (potion)\\ +\verb@P@ & S\textunderscore pudding & (pudding or ooze)\\ +\verb@q@ & S\textunderscore quadruped & (quadruped)\\ +\verb@Q@ & S\textunderscore quantmech & (quantum mechanic)\\ +\verb@=@ & S\textunderscore ring & (ring)\\ +\verb@`@ & S\textunderscore rock & (boulder or statue)\\ +\verb@r@ & S\textunderscore rodent & (rodent)\\ +\verb@^@ & S\textunderscore rolling\textunderscore boulder\textunderscore trap & (rolling boulder trap)\\ +\verb@.@ & S\textunderscore room & (floor of a room)\\ +\verb@/@ & S\textunderscore rslant & (wall)\\ +\verb@^@ & S\textunderscore rust\textunderscore trap & (rust trap)\\ +\verb@R@ & S\textunderscore rustmonst & (rust monster or disenchanter)\\ +\verb@?@ & S\textunderscore scroll & (scroll)\\ +\# & S\textunderscore sink & (sink)\\ +\verb@^@ & S\textunderscore sleeping\textunderscore gas\textunderscore trap & (sleeping gas trap)\\ +\verb@S@ & S\textunderscore snake & (snake)\\ +\verb@s@ & S\textunderscore spider & (arachnid or centipede)\\ +\verb@^@ & S\textunderscore spiked\textunderscore pit & (spiked pit)\\ +\verb@^@ & S\textunderscore squeaky\textunderscore board & (squeaky board)\\ +\verb@0@ & S\textunderscore ss1 & (magic shield 1 of 4)\\ +\# & S\textunderscore ss2 & (magic shield 2 of 4)\\ +\verb+@+ & S\textunderscore ss3 & (magic shield 3 of 4)\\ +\verb@*@ & S\textunderscore ss4 & (magic shield 4 of 4)\\ +\verb@^@ & S\textunderscore statue\textunderscore trap & (statue trap)\\ +\verb@ @ & S\textunderscore stone & (solid rock)\\ +\verb@]@ & S\textunderscore strange\textunderscore obj & (strange object)\\ +\verb@-@ & S\textunderscore sw\textunderscore bc & (swallow bottom center)\\ +\verb@\@ & S\textunderscore sw\textunderscore bl & (swallow bottom left)\\ +\verb@/@ & S\textunderscore sw\textunderscore br & (swallow bottom right )\\ +\verb@|@ & S\textunderscore sw\textunderscore ml & (swallow middle left)\\ +\verb@|@ & S\textunderscore sw\textunderscore mr & (swallow middle right)\\ +\verb@-@ & S\textunderscore sw\textunderscore tc & (swallow top center)\\ +\verb@/@ & S\textunderscore sw\textunderscore tl & (swallow top left)\\ +\verb@\@ & S\textunderscore sw\textunderscore tr & (swallow top right)\\ +\verb@-@ & S\textunderscore tdwall & (wall)\\ +\verb@^@ & S\textunderscore teleportation\textunderscore trap & (teleportation trap)\\ +\verb@\@ & S\textunderscore throne & (opulent throne)\\ +\verb@-@ & S\textunderscore tlcorn & (top left corner)\\ +\verb@|@ & S\textunderscore tlwall & (wall)\\ +\verb@(@ & S\textunderscore tool & (useful item (pick-axe, key, lamp...))\\ +\verb@^@ & S\textunderscore trap\textunderscore door & (trap door)\\ +\verb@t@ & S\textunderscore trapper & (trapper or lurker above)\\ +\verb@-@ & S\textunderscore trcorn & (top right corner)\\ +\# & S\textunderscore tree & (tree)\\ +\verb@T@ & S\textunderscore troll & (troll)\\ +\verb@|@ & S\textunderscore trwall & (wall)\\ +\verb@-@ & S\textunderscore tuwall & (wall)\\ +\verb@U@ & S\textunderscore umber & (umber hulk)\\ +\verb@ @ & S\textunderscore unexplored & (unexplored terrain)\\ +\verb@u@ & S\textunderscore unicorn & (unicorn or horse)\\ +\verb@<@ & S\textunderscore upladder & (ladder up)\\ +\verb@<@ & S\textunderscore upstair & (staircase up)\\ +\verb@V@ & S\textunderscore vampire & (vampire)\\ +\verb@|@ & S\textunderscore vbeam & (wall)\\ +\# & S\textunderscore vcdbridge & (vertical raised drawbridge)\\ +\verb@+@ & S\textunderscore vcdoor & (closed door)\\ +\verb@.@ & S\textunderscore venom & (splash of venom)\\ +\verb@^@ & S\textunderscore vibrating\textunderscore square & (vibrating square)\\ +\verb@.@ & S\textunderscore vodbridge & (vertical lowered drawbridge)\\ +\verb@-@ & S\textunderscore vodoor & (open door)\\ +\verb@v@ & S\textunderscore vortex & (vortex)\\ +\verb@|@ & S\textunderscore vwall & (vertical wall)\\ +\verb@/@ & S\textunderscore wand & (wand)\\ +\verb@}@ & S\textunderscore water & (water)\\ +\verb@)@ & S\textunderscore weapon & (weapon)\\ +\verb@"@ & S\textunderscore web & (web)\\ +\verb@w@ & S\textunderscore worm & (worm)\\ +\verb@~@ & S\textunderscore worm\textunderscore tail & (long worm tail)\\ +\verb@W@ & S\textunderscore wraith & (wraith)\\ +\verb@x@ & S\textunderscore xan & (xan or other extraordinary insect)\\ +\verb@X@ & S\textunderscore xorn & (xorn)\\ +\verb@Y@ & S\textunderscore yeti & (apelike creature)\\ +\verb@Z@ & S\textunderscore zombie & (zombie)\\ +\verb@z@ & S\textunderscore zruty & (zruty)\\ +\verb@ @ & S\textunderscore pet\textunderscore override & (any pet if ACCESSIBILITY=1 is set)\\ +\verb@ @ & S\textunderscore hero\textunderscore override & (hero if ACCESSIBILITY=1 is set) \end{longtable}% } @@ -6582,16 +6579,16 @@ Notes: %.lp "*" Several symbols in this table appear to be blank. -They are the space character, except for S\verb+_+pet\verb+_+override -and S\verb+_+hero\verb+_+override which don't have any default value +They are the space character, except for S\textunderscore pet\textunderscore override +and S\textunderscore hero\textunderscore override which don't have any default value and can only be used if enabled in the ``sysconf'' file. %.lp "*" -S\verb+_+rock is misleadingly named; rocks and stones use S\verb+_+gem. +S\textunderscore rock is misleadingly named; rocks and stones use S\textunderscore gem. Statues and boulders are the rock being referred to, but since version 3.6.0, statues are displayed as the monster they depict. -So S\verb+_+rock is only used for boulders and not used at all if -overridden by the more specific S\verb+_+boulder. +So S\textunderscore rock is only used for boulders and not used at all if +overridden by the more specific S\textunderscore boulder. %.lp %.hn 2 @@ -6615,7 +6612,7 @@ character sequences and explicit 24-bit red-green-blue colors in order for the g representation to be visible as specified. For example, the following line in your configuration file will cause -the glyph representation for glyphid G\verb+_+pool to use Unicode codepoint +the glyph representation for glyphid G\textunderscore pool to use Unicode codepoint U+224B and the color represented by R-G-B value 0-0-160:\\ \begin{verbatim} OPTIONS=glyph:G_pool/U+224B/0-0-160 @@ -6625,8 +6622,8 @@ The list of acceptable glyphid's can be produced by \begin{verbatim} nethack --glyphids \end{verbatim} -Individual NetHack glyphs can be specified using the G\verb+_+ prefix, -or you can use an S\verb+_+ symbol for a glyphid and store the custom +Individual NetHack glyphs can be specified using the G\textunderscore prefix, +or you can use an S\textunderscore symbol for a glyphid and store the custom representation for all NetHack glyphs that would map to that particular symbol. @@ -6678,50 +6675,50 @@ Load a symbol set appropriate for use by blind players. \item[\ib{menustyle:traditional}] This will assist in the interface to speech synthesizers. %.lp -\item[\ib{nomenu\verb+_+overlay}] +\item[\ib{nomenu\textunderscore overlay}] Show menus on a cleared screen and aligned to the left edge. %.lp -\item[\ib{number\verb+_+pad}] +\item[\ib{number\textunderscore pad}] A lot of speech access programs use the number-pad to review the screen. -If this is the case, disable the number\verb+_+pad option and use the +If this is the case, disable the number\textunderscore pad option and use the traditional Rogue-like commands. %.lp -\item[\ib{paranoid\verb+_+confirmation:swim}] +\item[\ib{paranoid\textunderscore confirmation:swim}] Prevent walking into water or lava. %.lp \item[\ib{accessiblemsg}] Adds direction or location information to messages. %.lp -\item[\ib{spot\verb+_+monsters}] +\item[\ib{spot\textunderscore monsters}] Shows a message when hero notices a monster; combine with accessiblemsg. %.lp -\item[\ib{mon\verb+_+movement}] +\item[\ib{mon\textunderscore movement}] Shows a message when hero notices a monster movement; -combine with spot\verb+_+monsters and accessiblemsg. +combine with spot\textunderscore monsters and accessiblemsg. %.lp \item[\ib{autodescribe}] Automatically describe the terrain under the cursor when targeting. %.lp -\item[\ib{mention\verb+_+map}] +\item[\ib{mention\textunderscore map}] Give feedback messages when interesting map locations change. %.lp -\item[\ib{mention\verb+_+walls}] +\item[\ib{mention\textunderscore walls}] Give feedback messages when walking towards a wall or when travel command was interrupted. %.lp -\item[\ib{whatis\verb+_+coord:compass}] +\item[\ib{whatis\textunderscore coord:compass}] When targeting with cursor, describe the cursor position with coordinates relative to your character. %.lp -\item[\ib{whatis\verb+_+filter:area}] +\item[\ib{whatis\textunderscore filter:area}] When targeting with cursor, filter possible locations so only those in the same area (eg. same room, or same corridor) are considered. %.lp -\item[\ib{whatis\verb+_+moveskip}] +\item[\ib{whatis\textunderscore moveskip}] When targeting with cursor and using fast-move, skip the same glyphs instead of moving 8 units at a time. %.lp -\item[\ib{nostatus\verb+_+updates}] +\item[\ib{nostatus\textunderscore updates}] Prevent updates to the status lines at the bottom of the screen, if your screen-reader reads those lines. The same information can be seen via the {\tt \#attributes} command. @@ -6779,11 +6776,11 @@ A string explaining how to recover a game on this system (no default value). 0 or 1 to disable or enable, respectively, the SEDUCE option. When disabled, incubi and succubi behave like nymphs. %.lp -\item[\ib{CHECK\verb+_+PLNAME}] +\item[\ib{CHECK\textunderscore PLNAME}] Setting this to 1 will make the EXPLORERS, WIZARDS, and SHELLERS check for the player name instead of the user's login name. %.lp -\item[\ib{CHECK\verb+_+SAVE\verb+_+UID}] +\item[\ib{CHECK\textunderscore SAVE\textunderscore UID}] 0 or 1 to disable or enable, respectively, the UID (used identification number) checking for save files (to verify that the user who is restoring is the same one who saved). @@ -6803,7 +6800,7 @@ Maximum number of entries in the score file. \item[\ib{POINTSMIN}] Minimum number of points to get an entry in the score file. %.lp -\item[\ib{PERS\verb+_+IS\verb+_+UID}] +\item[\ib{PERS\textunderscore IS\textunderscore UID}] 0 or 1 to use user names or numeric userids, respectively, to identify unique people for the score file. %.lp @@ -6811,16 +6808,16 @@ unique people for the score file. 0 or 1 to control whether the help menu entry for command line usage is shown or suppressed. %.lp -\item[\ib{MAX\verb+_+STATUENAME\verb+_+RANK}] +\item[\ib{MAX\textunderscore STATUENAME\textunderscore RANK}] Maximum number of score file entries to use for random statue names (default is 10). %.lp \item[\ib{ACCESSIBILITY}] 0 or 1 to disable or enable, respectively, the ability for players -to set S\verb+_+pet\verb+_+override and S\verb+_+hero\verb+_+override +to set S\textunderscore pet\textunderscore override and S\textunderscore hero\textunderscore override symbols in their configuration file. %.lp -\item[\ib{PORTABLE\verb+_+DEVICE\verb+_+PATHS}] +\item[\ib{PORTABLE\textunderscore DEVICE\textunderscore PATHS}] 0 or 1 Windows OS only, the game will look for all of its external files, and write to all of its output files in one place rather than at the standard locations. From d59add7100a08072808b861deea52577d61141d0 Mon Sep 17 00:00:00 2001 From: danielclow <106956386+danielclow@users.noreply.github.com> Date: Sat, 3 Jan 2026 21:10:37 +0800 Subject: [PATCH 166/442] Remove underscore hack The redefinition of the underscore's category code is replaced with textunderscore as needed throughout to prevent future confusion. --- doc/Guidebook.tex | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index df0db5dfd..99046ac60 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -25,10 +25,6 @@ \hyphenation{CRASHREPORTURL} -% this will make \tt underscores look better, but requires that -% math subscripts will never be used in this document -\catcode`\_=12 - \begin{document} % % input file: guidebook.mn @@ -3936,8 +3932,8 @@ Example: %.ed %.lp -\item[\bb{AUTOPICKUP\_EXCEPTION}] -Set exceptions to the {{\it pickup\_types\/}} +\item[\bb{AUTOPICKUP\textunderscore EXCEPTION}] +Set exceptions to the {{\it pickup\textunderscore types\/}} option. See the ``Configuring Autopickup Exceptions'' section. %.lp \item[\bb{BINDINGS}] @@ -4377,7 +4373,7 @@ An object's inventory letter sticks to it when it's dropped (default on). If this is off, dropping an object shifts all the remaining inventory letters. Persistent. %.lp -\item[\ib{force\_invmenu}] +\item[\ib{force\textunderscore invmenu}] Commands asking for an inventory item show a menu instead of a text query with possible menu letters. Default is off. %.lp @@ -5583,7 +5579,7 @@ Acceptable values are (The 26x82 size threshold for `2' refers to number of rows and columns of the display. A width of at least 110 columns (\verb&80+2+26+2&) is needed for -{\it align_status\/} +{\it align\textunderscore status\/} set to {\tt left} or {\tt right}.) %.lp "" @@ -5633,9 +5629,9 @@ computer unless you manually click submit on a form. %.si \blist{} %.lp -\item[OPTION=crash_email:{\it email_address}] +\item[OPTION=crash\textunderscore email:{\it email\textunderscore address}] %.lp -\item[OPTION=crash_name:{\it your_name}] +\item[OPTION=crash\textunderscore name:{\it your\textunderscore name}] %.ei \elist These options are used only to save you some typing on the crash @@ -5643,7 +5639,7 @@ report and \#bugreport forms. %.si \blist{} %.lp -\item[OPTION=crash_urlmax:{\it bytes}] +\item[OPTION=crash\textunderscore urlmax:{\it bytes}] %.ei \elist This option is used to limit the length of the URLs generated and is only @@ -6244,8 +6240,8 @@ Instead of a behavior, `condition' takes the following condition flags: {\it stone}, {\it slime}, {\it strngl}, {\it foodpois}, {\it termill}, {\it blind}, {\it deaf}, {\it stun}, {\it conf}, {\it hallu}, {\it lev}, {\it fly}, and {\it ride}. -You can use `major\_troubles' as an alias -for stone through termill, `minor\_troubles' for blind through hallu, +You can use `major\textunderscore troubles' as an alias +for stone through termill, `minor\textunderscore troubles' for blind through hallu, `movement' for lev, fly, and ride, and `all' for every condition. %.lp "" @@ -6387,7 +6383,7 @@ Default & Symbol Name & Description\\ \hline \hline \endhead \verb@ @ & S\textunderscore air & (air)\\ -\_ & S\textunderscore altar & (altar)\\ +\textunderscore & S\textunderscore altar & (altar)\\ \verb@"@ & S\textunderscore amulet & (amulet)\\ \verb@A@ & S\textunderscore angel & (angelic being)\\ \verb@a@ & S\textunderscore ant & (ant or other insect)\\ @@ -6660,7 +6656,7 @@ has been compiled with the capability. When compiling {\it NetHack\/} from source on Linux and other POSIX systems, define {\tt MSGHANDLER\/} to enable it. To use -the capability, set the environment variable {\tt NETHACK\_MSGHANDLER\/} to +the capability, set the environment variable {\tt NETHACK\textunderscore MSGHANDLER\/} to an executable, which will be executed with the game message as the program's only parameter. %.pg From 2f4595bb9ddd09d305ad088dca293a591040e680 Mon Sep 17 00:00:00 2001 From: danielclow <106956386+danielclow@users.noreply.github.com> Date: Sat, 3 Jan 2026 21:26:23 +0800 Subject: [PATCH 167/442] Use geometry package to define page layout This sets the size of the text on the page by using the geometry package, which is now essentially standard procedure. --- doc/Guidebook.tex | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 99046ac60..e9028c23f 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -1,13 +1,8 @@ % NetHack 3.7 Guidebook.tex $NHDT-Date: 1745139202 2025/04/20 00:53:22 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.579 $ */ -\documentclass[titlepage, a4paper]{article} +\documentclass[titlepage]{article} \usepackage{hyperref} \usepackage{longtable} - -\textheight 220mm -\textwidth 160mm -\oddsidemargin 0mm -\evensidemargin 0mm -\topmargin 0mm +\usepackage[a4paper, text={160mm, 220mm}, centering]{geometry} \newcommand{\nd}{\noindent} From ea925fa5025257a7c5cce7ee61fb404c0c150f23 Mon Sep 17 00:00:00 2001 From: danielclow <106956386+danielclow@users.noreply.github.com> Date: Sat, 3 Jan 2026 23:44:46 +0800 Subject: [PATCH 168/442] Refactor description lists using enumitem This replaces the custom blist with the LaTex built-in description list and adds enumitem to style the lists more easily. A default style for the description list is defined instead of specifying the font in every list item. Some special characters in list items are also replaced with textcomp options such as textasciigrave, etc. --- doc/Guidebook.tex | 1337 ++++++++++++++++++++++----------------------- 1 file changed, 666 insertions(+), 671 deletions(-) diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index e9028c23f..89bfaed00 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -2,22 +2,17 @@ \documentclass[titlepage]{article} \usepackage{hyperref} \usepackage{longtable} +\usepackage{enumitem} \usepackage[a4paper, text={160mm, 220mm}, centering]{geometry} +\setlist[description]{leftmargin=30mm, topsep=2mm, partopsep=0mm, parsep=0mm, itemsep=1mm, labelwidth=28mm, labelsep=2mm} + \newcommand{\nd}{\noindent} \newcommand{\tb}[1]{\tt #1 \hfill} \newcommand{\bb}[1]{\bf #1 \hfill} \newcommand{\ib}[1]{\it #1 \hfill} -\newcommand{\blist}[1] -{\begin{list}{$\bullet$} - {\leftmargin 30mm \topsep 2mm \partopsep 0mm \parsep 0mm \itemsep 1mm - \labelwidth 28mm \labelsep 2mm - #1}} - -\newcommand{\elist}{\end{list}} - \hyphenation{CRASHREPORTURL} \begin{document} @@ -104,24 +99,24 @@ will vary with your background and training: %.pg % -\blist{} -\item[\bb{Archeologists}]% +\begin{description} +\item[Archeologists] understand dungeons pretty well; this enables them to move quickly and sneak up on the local nasties. They start equipped with the tools for a proper scientific expedition. %.pg % -\item[\bb{Barbarians}]% +\item[Barbarians] are warriors out of the hinterland, hardened to battle. They begin their quests with naught but uncommon strength, a trusty hauberk, and a great two-handed sword. %.pg % -\item[\bb{Cavemen {\rm and} Cavewomen}] +\item[Cavemen \textrm{and} Cavewomen] start with exceptional strength, but unfortunately, neolithic weapons. %.pg % -\item[\bb{Healers}]% +\item[Healers] are wise in medicine and apothecary. They know the herbs and simples that can restore vitality, ease pain, anesthetize, and neutralize @@ -130,61 +125,61 @@ of health or sickness. Their medical practice earns them quite reasonable amounts of money, with which they enter the dungeon. %.pg % -\item[\bb{Knights}]% +\item[Knights] are distinguished from the common skirmisher by their devotion to the ideals of chivalry and by the surpassing excellence of their armor. %.pg % -\item[\bb{Monks}]% +\item[Monks] are ascetics, who by rigorous practice of physical and mental disciplines have become capable of fighting as effectively without weapons as with. They wear no armor but make up for it with increased mobility. %.pg % -\item[\bb{Priests {\rm and} Priestesses}]% +\item[Priests \textrm{and} Priestesses] are clerics militant, crusaders advancing the cause of righteousness with arms, armor, and arts thaumaturgic. Their ability to commune with deities via prayer occasionally extricates them from peril, but can also put them in it. %.pg % -\item[\bb{Rangers}]% +\item[Rangers] are most at home in the woods, and some say slightly out of place in a dungeon. They are, however, experts in archery as well as tracking and stealthy movement. %.pg % -\item[\bb{Rogues}]% +\item[Rogues] are agile and stealthy thieves, with knowledge of locks, traps, and poisons. Their advantage lies in surprise, which they employ to great advantage. %.pg % -\item[\bb{Samurai}]% +\item[Samurai] are the elite warriors of feudal Nippon. They are lightly armored and quick, and wear the % {\it dai-sho}, two swords of the deadliest keenness. %.pg % -\item[\bb{Tourists}]% +\item[Tourists] start out with lots of gold (suitable for shopping with), a credit card, lots of food, some maps, and an expensive camera. Most monsters don't like being photographed. %.pg % -\item[\bb{Valkyries}]% +\item[Valkyries] are hardy warrior women. Their upbringing in the harsh Northlands makes them strong, inures them to extremes of cold, and instills in them stealth and cunning. %.pg % -\item[\bb{Wizards}]% +\item[Wizards] start out with a knowledge of magic, a selection of magical items, and a particular affinity for dweomercraft. Although seemingly weak and easy to overcome at first sight, an experienced Wizard is a deadly foe. -\elist +\end{description} %.pg You may also choose the race of your character (within limits; most @@ -192,39 +187,39 @@ roles have restrictions on which races are eligible for them): %.pg % -\blist{} -\item[\bb{Dwarves}]% +\begin{description} +\item[Dwarves] are smaller than humans or elves, but are stocky and solid individuals. Dwarves' most notable trait is their great expertise in mining and metalwork. Dwarvish armor is said to be second in quality not even to the mithril armor of the Elves. %.pg % -\item[\bb{Elves}]% +\item[Elves] are agile, quick, and perceptive; very little of what goes on will escape an Elf. The quality of Elven craftsmanship often gives them an advantage in arms and armor. %.pg % -\item[\bb{Gnomes}]% +\item[Gnomes] are smaller than but generally similar to dwarves. Gnomes are known to be expert miners, and it is known that a secret underground mine complex built by this race exists within the Mazes of Menace, filled with both riches and danger. %.pg % -\item[\bb{Humans}]% +\item[Humans] are by far the most common race of the surface world, and are thus the norm to which other races are often compared. Although they have no special abilities, they can succeed in any role. %.pg % -\item[\bb{Orcs}]% +\item[Orcs] are a cruel and barbaric race that hate every living thing (including other orcs). Above all others, Orcs hate Elves with a passion unequalled, and will go out of their way to kill one at any opportunity. The armor and weapons fashioned by the Orcs are typically of inferior quality. -\elist +\end{description} %.hn 1 \section{What do all those things on the screen mean?} @@ -339,12 +334,12 @@ the third line. Here are explanations of what the various status items mean: %.lp -\blist{} -\item[\bb{Title}] +\begin{description} +\item[Title] Your character's name and professional ranking (based on role and {\it experience level\/}, see below). %.lp -\item[\bb{Strength}] +\item[Strength] A measure of your character's strength; one of your six basic attributes. A human character's attributes can range from 3 to 18 inclusive; non-humans may exceed these limits @@ -354,29 +349,29 @@ higher your strength, the stronger you are. Strength affects how successfully you perform physical tasks, how much damage you do in combat, and how much loot you can carry. %.lp -\item[\bb{Dexterity}] +\item[Dexterity] Dexterity affects your chances to hit in combat, to avoid traps, and do other tasks requiring agility or manipulation of objects. %.lp -\item[\bb{Constitution}] +\item[Constitution] Constitution affects your ability to recover from injuries and other strains on your stamina. When strength is low or modest, constitution also affects how much you can carry. With sufficiently high strength, the contribution to carrying capacity from your constitution no longer matters. %.lp -\item[\bb{Intelligence}] +\item[Intelligence] Intelligence affects your ability to cast spells and read spellbooks. %.lp -\item[\bb{Wisdom}] +\item[Wisdom] Wisdom comes from your practical experience (especially when dealing with magic). It affects your magical energy. %.lp -\item[\bb{Charisma}] +\item[Charisma] Charisma affects how certain creatures react toward you. In particular, it can affect the prices shopkeepers offer you. %.lp -\item[\bb{Alignment}] +\item[Alignment] % {\it Lawful}, {\it Neutral\/} or {\it Chaotic}. Often, Lawful is taken as good and Chaotic as evil, but legal and ethical do not always @@ -385,35 +380,35 @@ monsters react toward you. Monsters of a like alignment are more likely to be non-aggressive, while those of an opposing alignment are more likely to be seriously offended at your presence. %.lp -\item[\bb{Dungeon Level}] +\item[Dungeon Level] How deep you are in the dungeon. You start at level one and the number increases as you go deeper into the dungeon. Some levels are special, and are identified by a name and not a number. The Amulet of Yendor is reputed to be somewhere beneath the twentieth level. %.lp -\item[\bb{Gold}] +\item[Gold] The number of gold pieces you are openly carrying. Gold which you have concealed in containers is not counted. %.lp -\item[\bb{Hit Points}] +\item[Hit Points] Your current and maximum hit points. Hit points indicate how much damage you can take before you die. The more you get hit in a fight, the lower they get. You can regain hit points by resting, or by using certain magical items or spells. The number in parentheses is the maximum number your hit points can reach. %.lp -\item[\bb{Power}] +\item[Power] Spell points. This tells you how much mystic energy ({\it mana\/}) you have available for spell casting. Again, resting will regenerate the amount available. %.lp -\item[\bb{Armor Class}] +\item[Armor Class] A measure of how effectively your armor stops blows from unfriendly creatures. The lower this number is, the more effective the armor; it is quite possible to have negative armor class. See the {\it Armor\/} subsection of {\it Objects\/} for more information. %.lp -\item[\bb{Experience}] +\item[Experience] Your current experience level. If the {\it showexp\/} option is set, it will be followed by a slash and experience points. @@ -426,11 +421,11 @@ the points with it has dropped significantly. You can use the `{\tt O}' command to turn {\it showexp\/} off to avoid using up the limited status line space.) %.lp -\item[\bb{Time}] +\item[Time] The number of turns elapsed so far, displayed if you have the {\it time\/} option set. %.lp -\item[\bb{Status}] +\item[Status] Hunger: your current hunger status. Values are {\it Satiated}, {\it Not~Hungry\/} (or {\it Normal\/}), @@ -472,7 +467,7 @@ all current status information in unabbreviated format. It also shows other information which might be included on the status lines if those had more room. -\elist +\end{description} %.hn 2 \subsection*{The message line (top)} @@ -501,88 +496,88 @@ options to change some of the symbols the game uses; otherwise, the game will use default symbols. Here is a list of what the default symbols mean: -\blist{} -\item[\tb{-}] +\begin{description}[font=\ttfamily] +\item[-] The horizontal or corner walls of a room, or an open east/west door. -\item[\tb{|}] +\item[|] The vertical walls of a room, or an open north/south door, or a grave. -\item[\tb{.}] +\item[.] The floor of a room, or ice, or a doorless doorway, or the span of an open drawbridge. -\item[\tb{\#}] +\item[\#] A corridor, or iron bars, or a tree, or the portcullis of a closed drawbridge.\\ %.lp "" Note: engravings in corridors also appear as \# but are shown in a different color from normal corridor locations. -\item[\tb{>}] +\item[>] Stairs down: a way to the next level. -\item[\tb{<}] +\item[<] Stairs up: a way to the previous level. -\item[\tb{+}] +\item[+] A closed door, or a spellbook containing a spell you may be able to learn. -\item[\tb{@}] +\item[@] Your character or a human or an elf. -\item[\tb{\$}] +\item[\textdollar] A pile of gold. -\item[\tb{\^}] +\item[\textasciicircum] A trap (once you have detected it). -\item[\tb{)}] +\item[)] A weapon. -\item[\tb{[}] +\item[{[}] A suit or piece of armor. -\item[\tb{\%}] +\item[\%] Something edible (not necessarily healthy). -\item[\tb{?}] +\item[?] A scroll. -\item[\tb{/}] +\item[/] A wand. -\item[\tb{=}] +\item[=] A ring. -\item[\tb{!}] +\item[!] A potion. -\item[\tb{(}] +\item[(] A useful item (pick-axe, key, lamp \ldots). -\item[\tb{"}] +\item["] An amulet or a spider web. -\item[\tb{*}] +\item[*] A gem or rock (possibly valuable, possibly worthless). -\item[\tb{\`}] +\item[\textasciigrave] A boulder or statue or an engraving on the floor of a room.\\ %.lp "" Note: statues are displayed as if they were the monsters they depict so won't appear as a {\it grave accent\/} (aka {\it back-tick}). -\item[\tb{0}] +\item[0] An iron ball. -\item[\tb{\textunderscore}] +\item[\textunderscore] An altar, or an iron chain. -\item[\tb{\{}] +\item[\textbraceleft] A fountain or a sink. -\item[\tb{\}}] +\item[\textbraceright] A pool of water or moat or a wall of water or a pool of lava or a wall of lava. -\item[\tb{$\backslash$}] +\item[\textbackslash] An opulent throne. -\item[\tb{a-z}] {\normalfont and}] -\item[\tb{A-HJ-Z}] {\normalfont and}] -%should probably change \item[\tb{@\&\textquotesingle:;}] to \item[\tb{\verb+@&':;+}] -\item[\tb{@\&\textquotesingle:;}] +\item[a-z \textrm{\textmd{and}}] +\item[A-HJ-Z \textrm{\textmd{and}}] +%should probably change \item[@\&\textquotesingle:;] to \item[\verb+@&':;+] +\item[@\&\textquotesingle :;] Letters and certain other symbols represent the various inhabitants of the Mazes of Menace. Watch out, they can be nasty and vicious. Sometimes, however, they can be helpful. -\item[\tb{I}] +\item[I] Rather than a specific type of monster, this marks the last known location of an invisible or otherwise unseen monster. Note that the monster could have moved. The `{\tt s}', `{\tt F}', and `{\tt m}' commands may be useful here. -\item[\tb{1-5}] +\item[1-5] The digits 1 through 5 may be displayed, marking unseen monsters sensed via the {\it Warning\/} attribute. Less dangerous monsters are indicated by lower values, more dangerous by higher values. -\elist +\end{description} %.pg You need not memorize all these symbols; you can ask the game what any symbol represents with the `{\tt /}' command (see the next section for @@ -632,12 +627,12 @@ The list of commands is rather long, but it can be read at any time during the game through the `{\tt ?}' command, which accesses a menu of helpful texts. Here are the default key bindings for your reference: -\blist{} +\begin{description}[font=\ttfamily] %.lp -\item[\tb{?}] +\item[?] Help menu: display one of several help texts available. %.lp -\item[\tb{/}] +\item[/] The {\tt whatis} command, to tell what a symbol represents. You may choose to specify a location or type a symbol (or even a whole word) to explain. @@ -677,16 +672,16 @@ The option controls which format of map coordinate is included with their descriptions. %.lp -\item[\tb{\&}] +\item[\&] Tell what a command does. %.lp -\item[\tb{<}] +\item[<] Go up to the previous level (if you are on a staircase or ladder). %.lp -\item[\tb{>}] +\item[>] Go down to the next level (if you are on a staircase or ladder). %.lp -\item[\tb{[yuhjklbn]}] +\item[{[yuhjklbn]}] Go one step in the direction indicated (see Figure 3). If you sense or remember a monster there, you will fight the monster instead. Only these @@ -708,10 +703,10 @@ one-step movement commands cause you to fight monsters; the others Figure 3 \end{center} %.lp -\item[\tb{[YUHJKLBN]}] +\item[{[YUHJKLBN]}] Go in that direction until you hit a wall or run into something. %.lp -\item[\tb{m[yuhjklbn]}] +\item[m{[yuhjklbn]}] Prefix: move without picking up objects or fighting (even if you remember a monster there).\\ %.lp "" @@ -746,13 +741,13 @@ skip containers and go straight to adjacent monsters. In debug mode (aka ``wizard mode''), the `{\tt m}' prefix may also be used with the ``{\tt \#teleport}'' and ``{\tt \#wizlevelport}'' commands. %.lp -\item[\tb{F[yuhjklbn]}] +\item[F{[yuhjklbn]}] Prefix: fight a monster (even if you only guess one is there). %.lp -\item[\tb{g[yuhjklbn]}] +\item[g{[yuhjklbn]}] Prefix: Move until something interesting is found. %.lp -\item[\tb{G[yuhjklbn] {\rm or} +[yuhjklbn]}] +\item[G{[yuhjklbn]} \textrm{\textmd{or}} +{[yuhjklbn]}] Prefix: Similar to `{\tt g}', but forking of corridors is not considered interesting. \\ @@ -762,7 +757,7 @@ Note: {\tt +} means holding the {\tt } or shorthand elsewhere in the Guidebook to mean the same thing. Control characters are case-insensitive so {\tt \^{}x} and {\tt \^{}X} are the same. %.lp -\item[\tb{M[yuhjklbn]}] +\item[M{[yuhjklbn]}] Old versions supported `{\tt M}' as a movement prefix which combined the effect of `{\tt m}' with {\tt +}. That is no longer supported as a prefix but similar effect can be achieved @@ -770,7 +765,7 @@ by using {\tt m} and {\tt G} in combination. {\tt m} can also be used in combination with {\tt g}, {\tt +}, or {\tt +}. %.lp -\item[\tb{\tt \textunderscore }] +\item[\textunderscore] Travel to a map location via a shortest-path algorithm.\\ %.lp "" The shortest path @@ -784,45 +779,45 @@ For ports with mouse support, the command is also invoked when a mouse-click takes place on a location other than the current position. %.lp -\item[\tb{.}] +\item[.] Wait or rest, do nothing for one turn. Precede with the `{\tt m}' prefix to wait for a turn even next to a hostile monster, if {\it safe\textunderscore wait\/} is on. %.lp -\item[\tb{a}] +\item[a] Apply (use) a tool (pick-axe, key, lamp \ldots).\\ %.lp "" If used on a wand, that wand will be broken, releasing its magic in the process. Confirmation is required. %.lp -\item[\tb{A}] +\item[A] Remove one or more worn items, such as armor.\\ %.lp "" Use `{\tt T}' (take off) to take off only one piece of armor or `{\tt R}' (remove) to take off only one accessory. %.lp -\item[\tb{\^{}A}] +\item[\textasciicircum A] Repeat the previous command. %.lp -\item[\tb{c}] +\item[c] Close a door. %.lp -\item[\tb{C}] +\item[C] Call (name) a monster, an individual object, or a type of object.\\ %.lp "" Same as extended command ``{\tt \#name}''. %.lp -\item[\tb{\^{}C}] +\item[\textasciicircum C] Panic button. Quit the game. %.lp -\item[\tb{d}] +\item[d] Drop something.\\ For example {\tt d7a} --- drop seven items of object {\it a}. %.lp -\item[\tb{D}] +\item[D] Drop several things.\\ %.lp "" In answer to the question\\ @@ -866,10 +861,10 @@ Lastly, you may specify multiple values within multiple categories: blessed or uncursed. (In versions prior to 3.6, filter combinations behaved differently.) %.lp -\item[\tb{\^{}D}] +\item[\textasciicircum D] Kick something (usually a door). %.lp -\item[\tb{e}] +\item[e] Eat food.\\ %.lp "" Normally checks for edible item(s) on the floor, then if none are found @@ -888,7 +883,7 @@ option to require a response of ``{\tt yes}'' instead of just `{\tt y}'. % (Only specified here to parallel Guidebook.mn; use of \tt font implicitly % prevents automatic hyphenation in TeX and LaTeX.) \hyphenation{Elbereth} %override the deduced syllable breaks -\item[\tb{E}] +\item[E] Engrave a message on the floor.\\ %.sd %.si @@ -900,7 +895,7 @@ Engraving the word ``{\tt Elbereth}'' will cause most monsters to not attack you hand-to-hand (but if you attack, you will rub it out); this is often useful to give yourself a breather. %.lp -\item[\tb{f}] +\item[f] Fire (shoot or throw) one of the objects placed in your quiver (or quiver sack, or that you have at the ready). You may select ammunition with a previous `{\tt Q}' command, or let the @@ -917,10 +912,10 @@ Remember to swap back to your main melee weapon afterwards. \\ See also `{\tt t}' (throw) for more general throwing and shooting. %.lp -\item[\tb{i}] +\item[i] List your inventory (everything you're carrying). %.lp -\item[\tb{I}] +\item[I] List selected parts of your inventory, usually be specifying the character for a particular set of objects, like `{\tt [}' for armor or `{\tt !}' for potions.\\ @@ -938,10 +933,10 @@ for potions.\\ %.ei %.ed %.lp -\item[\tb{o}] +\item[o] Open a door. %.lp -\item[\tb{O}] +\item[O] Set options.\\ %.lp "" A menu showing the current option values will be @@ -954,7 +949,7 @@ are listed later in this Guidebook. Options are usually set before the game rather than with the `{\tt O}' command; see the section on options below. Precede {\tt O} with the {\tt m} prefix to show advanced options. %.lp -\item[\tb{\^{}O}] +\item[\textasciicircum O] Show overview.\\ %.lp "" Shortcut for ``{\tt \#overview}'': @@ -964,10 +959,10 @@ list interesting dungeon levels visited.\\ the placement of all special levels. Use ``{\tt \#wizwhere}'' to run that command.) %.lp -\item[\tb{p}] +\item[p] Pay your shopping bill. %.lp -\item[\tb{P}] +\item[P] Put on an accessory (ring, amulet, or blindfold).\\ %.lp "" This command may also be used to wear armor. The prompt for @@ -976,14 +971,14 @@ an unlisted item of armor will attempt to wear it. (See the `{\tt W}' command below. It lists armor as the inventory choices but will accept an accessory and attempt to put that on.) %.lp -\item[\tb{\^{}P}] +\item[\textasciicircum P] Repeat previous message.\\ %.lp "" Subsequent {\tt \^{}P}'s repeat earlier messages. For some interfaces, the behavior can be varied via the {\it msg\textunderscore window\/} option. %.lp -\item[\tb{q}] +\item[q] Quaff (drink) something (potion, water, etc).\\ %.lp "" When there is a fountain or sink present, it asks whether to drink @@ -993,15 +988,15 @@ inventory. Precede {\tt q} with the {\tt m} prefix to skip asking about drinking from a fountain or sink. %.lp -\item[\tb{Q}] +\item[Q] Select an object for your quiver, quiver sack, or just generally at the ready (only one of these is available at a time). You can then throw this (or one of these) using the `{\tt f}' command. %.lp -\item[\tb{r}] +\item[r] Read a scroll or spellbook. %.lp -\item[\tb{R}] +\item[R] Remove a worn accessory (ring, amulet, or blindfold).\\ %.lp "" If you're wearing more than one, you'll be prompted for which one to @@ -1016,10 +1011,10 @@ worn armor can be chosen. (See the `{\tt T}' command below. It lists armor as the inventory choices but will accept an accessory and attempt to remove it.) %.lp -\item[\tb{\^{}R}] +\item[\textasciicircum R] Redraw the screen. %.lp -\item[\tb{s}] +\item[s] Search for secret doors and traps around you. It usually takes several tries to find something. Precede with the `{\tt m}' prefix to wait for a turn @@ -1029,7 +1024,7 @@ is on.\\ Can also be used to figure out whether there is still a monster at an adjacent ``remembered, unseen monster'' marker. %.lp -\item[\tb{S}] +\item[S] Save the game (which suspends play and exits the program). The saved game will be restored automatically the next time you play using the same character name.\\ @@ -1044,7 +1039,7 @@ without saving and later restore again.\\ There is no ``save current game state and keep playing'' command, not even in explore mode where saved game files can be kept and re-used. %.lp -\item[\tb{t}] +\item[t] Throw an object or shoot a projectile.\\ %.lp "" There's no separate ``shoot'' command. @@ -1056,7 +1051,7 @@ it by hand and it will generally be less effective than when shot.\\ See also `{\tt f}' (fire) for throwing or shooting an item pre-selected via the `{\tt Q}' (quiver) command, with some extra assistance. %.lp -\item[\tb{T}] +\item[T] Take off armor.\\ %.lp "" If you're wearing more than one piece, you'll be prompted for which @@ -1074,16 +1069,16 @@ accessory can be chosen. (See the `{\tt R}' command above. It lists accessories as the inventory choices but will accept an item of armor and attempt to take it off.) %.lp -\item[\tb{\^{}T}] +\item[\textasciicircum T] Teleport, if you have the ability. %.lp -\item[\tb{v}] +\item[v] Display version number. %.lp -\item[\tb{V}] +\item[V] Display the game history. %.lp -\item[\tb{w}] +\item[w] Wield weapon.\\ %.sd %.si @@ -1093,7 +1088,7 @@ Wield weapon.\\ Some characters can wield two weapons at once; use the `{\tt X}' command (or the ``{\tt \#twoweapon}'' extended command) to do so. %.lp -\item[\tb{W}] +\item[W] Wear armor.\\ %.lp "" This command may also be used to put on an accessory (ring, amulet, or @@ -1102,14 +1097,14 @@ armor, but choosing an unlisted accessory will attempt to put it on. (See the `{\tt P}' command above. It lists accessories as the inventory choices but will accept an item of armor and attempt to wear it.) %.lp -\item[\tb{x}] +\item[x] Exchange your wielded weapon with the item in your alternate weapon slot.\\ %.lp "" The latter is used as your secondary weapon when engaging in two-weapon combat. Note that if one of these slots is empty, the exchange still takes place. %.lp -\item[\tb{X}] +\item[X] Toggle two-weapon combat, if your character can do it. Also available via the ``{\tt \#twoweapon}'' extended command.\\ %.lp "" @@ -1117,7 +1112,7 @@ via the ``{\tt \#twoweapon}'' extended command.\\ play to ``explore mode'', also known as ``discovery mode'', which has now been moved to ``{\tt \#exploremode}'' and {\tt M-X}.) %.lp -\item[\tb{\^{}X}] +\item[\textasciicircum X] Display basic information about your character.\\ %.lp "" Displays name, role, race, gender (unless role name makes that @@ -1131,7 +1126,7 @@ In normal play, that's all that `{\tt \^{}X}' displays. In explore mode, the role and status feedback is augmented by the information provided by {\it enlightenment\/} magic. %.lp -\item[\tb{z}] +\item[z] Zap a wand.\\ %.sd %.si @@ -1139,7 +1134,7 @@ Zap a wand.\\ %.ei %.ed %.lp -\item[\tb{Z}] +\item[Z] Zap (cast) a spell.\\ %.sd %.si @@ -1147,52 +1142,52 @@ Zap (cast) a spell.\\ %.ei %.ed %.lp -\item[\tb{\^{}Z}] +\item[\textasciicircum Z] Suspend the game (UNIX versions with job control only). See ``\#suspend'' below for more details. %.lp -\item[\tb{:}] +\item[:] Look at what is here. %.lp -\item[\tb{;}] +\item[;] Show what type of thing a visible symbol corresponds to. %.lp -\item[\tb{,}] +\item[,] Pick up some things from the floor beneath you.\\ %.lp "" May be preceded by `{\tt m}' to force a selection menu. %.lp -\item[\tb{@}] +\item[@] Toggle the {\it autopickup\/} option on and off. %.lp -\item[\tb{\^{}}] +\item[\textasciicircum] Ask for the type of an adjacent trap you found earlier. %.lp -\item[\tb{)}] +\item[)] Tell what weapon you are wielding. %.lp -\item[\tb{[}] +\item[{]}] Tell what armor you are wearing. %.lp -\item[\tb{=}] +\item[=] Tell what rings you are wearing. %.lp -\item[\tb{"}] +\item["] Tell what amulet you are wearing. %.lp -\item[\tb{(}] +\item[(] Tell what tools you are using. %.lp -\item[\tb{*}] +\item[*] Tell what equipment you are using.\\ %.lp "" Combines the preceding five type-specific commands into one. %.lp -\item[\tb{\$}] +\item[\$] Report the gold you're carrying, possibly shop credit and/or debt too. %.lp -\item[\tb{+}] +\item[+] List the spells you know.\\ %.lp "" Using this command, you can also rearrange @@ -1206,20 +1201,20 @@ pick ``reassign casting letters''. (Any spells learned after that will be added to the end of the list rather than be inserted into the sorted ordering.) %.lp -\item[\tb{$\backslash$}] +\item[\textbackslash] Show what types of objects have been discovered. \\ %.lp "" May be preceded by `{\tt m}' to select preferred display order. %.lp -\item[\tb{\`}] +\item[\textasciigrave] Show discovered types for one class of objects. \\ -.lp "" +%.lp "" May be preceded by `{\tt m}' to select preferred display order. %.lp -\item[\tb{|}] +\item[|] If persistent inventory display is supported and enabled (with the {\it perm\textunderscore invent\/} option), interact with it instead of with the map. @@ -1235,11 +1230,11 @@ Use the {\it Return\/} (aka {\it Enter\/}) or {\it Escape\/} key to resume play. %.lp -\item[\tb{!}] +\item[!] Escape to a shell. See ``\#shell'' below for more details. %.lp -\item[\tb{Del}] +\item[Del] Show map without obstructions. You can view the explored portion of the current level's map without monsters; without monsters and objects; or without monsters, objects, @@ -1254,7 +1249,7 @@ Many terminals have an option to swap the {\tt } and {\tt } keys, so typing the {\tt } key might not execute this command. If that happens, you can use the extended command ``{\tt \#terrain}'' instead. %.lp -\item[\tb{\#}] +\item[\#] Perform an extended command.\\ %.lp "" As you can see, the authors of {\it NetHack\/} @@ -1263,7 +1258,7 @@ used commands. What extended commands are available depends on what features the game was compiled with. %.lp -\item[\tb{\#adjust}] +\item[\#adjust] Adjust inventory letters (most useful when the {\it fixinv\/} option is ``on''). Autocompletes. Default key is `{\tt M-a}'.\\ @@ -1292,14 +1287,14 @@ name when the other doesn't and give that name to the result, while splitting (count given) will ignore the source stack's name when deciding whether to merge with the destination stack. %.lp -\item[\tb{\#annotate}] +\item[\#annotate] Allows you to specify one line of text to associate with the current dungeon level. All levels with annotations are displayed by the ``{\tt \#overview}'' command. Autocompletes. Default key is `{\tt M-A}', and also `{\tt \^{}N}' if {\it number\textunderscore pad\/} is on. %.lp -\item[\tb{\#apply}] +\item[\#apply] Apply (use) a tool such as a pick-axe, a key, or a lamp. Default key is `{\tt a}'.\\ %.lp "" @@ -1309,72 +1304,72 @@ skips those items.\\ If used on a wand, that wand will be broken, releasing its magic in the process. Confirmation is required. %.lp -\item[\tb{\#attributes}] +\item[\#attributes] Show your attributes. Default key is `{\tt \^{}X}'. %.lp -\item[\tb{\#autopickup}] +\item[\#autopickup] Toggle the {\it autopickup\/} option. Default key is `{\tt @}'. %.lp -\item[\tb{\#bugreport}] +\item[\#bugreport] Bring up a browser window to submit a report to the {\it NetHack Development Team}. Can be disabled at the time the program is built; when enabled, CRASHREPORTURL must be set in the system configuration file. %.lp -\item[\tb{\#call}] +\item[\#call] Call (name) a monster, or an object in inventory, on the floor, or in the discoveries list, or add an annotation for the current level (same as ``{\tt \#annotate}''). Default key is `{\tt C}'. %.lp -\item[\tb{\#cast}] +\item[\#cast] Cast a spell. Default key is `{\tt Z}'. %.lp -\item[\tb{\#chat}] +\item[\#chat] Talk to someone. Default key is `{\tt M-c}'. %.lp -\item[\tb{\#chronicle}] +\item[\#chronicle] Show a list of important game events. %.lp -\item[\tb{\#close}] +\item[\#close] Close a door. Default key is `{\tt c}'. %.lp -\item[\tb{\#conduct}] +\item[\#conduct] List voluntary challenges you have maintained. Autocompletes. Default key is `{\tt M-C}'.\\ %.lp "" See the section below entitled ``Conduct'' for details. %.lp -\item[\tb{\#debugfuzzer}] +\item[\#debugfuzzer] Start the fuzz tester. Debug mode only. %.lp -\item[\tb{\#dip}] +\item[\#dip] Dip an object into something. Autocompletes. Default key is `{\tt M-d}'.\\ %.lp "" The {\tt m} prefix skips dipping into a fountain or pool if there is one at your location. %.lp -\item[\tb{\#down}] +\item[\#down] Go down a staircase. Default key is `{\tt >}'. %.lp -\item[\tb{\#drop}] +\item[\#drop] Drop an item. Default key is `{\tt d}'. %.lp -\item[\tb{\#droptype}] +\item[\#droptype] Drop specific item types. Default key is `{\tt D}'. %.lp -\item[\tb{\#eat}] +\item[\#eat] Eat something. Default key is `{\tt e}'. The `{\tt m}' prefix skips eating items on the floor. %.lp -\item[\tb{\#engrave}] +\item[\#engrave] Engrave writing on the floor. Default key is `{\tt E}'. %.lp -\item[\tb{\#enhance}] +\item[\#enhance] Advance or check weapon and spell skills. Autocompletes. Default key is `{\tt M-e}'. %.lp -\item[\tb{\#exploremode}] +\item[\#exploremode] Switch from normal play to non-scoring explore mode. Default key is `{\tt M-X}'.\\ %.lp "" @@ -1384,21 +1379,21 @@ You can set the {\it paranoid\textunderscore confirmation:quit\/} option to require a response of ``{\tt yes}'' instead. %.lp -\item[\tb{\#fight}] +\item[\#fight] Prefix key to force fight a direction, even if you see nothing to fight there. Default key is `{\tt F}', or `{\tt -}' with {\it number\textunderscore pad\/} %.lp -\item[\tb{\#fire}] +\item[\#fire] Fire ammunition from quiver, possibly autowielding a launcher, or hit with a wielded polearm. Default key is `{\tt f}'. %.lp -\item[\tb{\#force}] +\item[\#force] Force a lock. Autocompletes. Default key is `{\tt M-f}'. %.lp -\item[\tb{\#genocided}] +\item[\#genocided] List any monster types which have been genocided. In explore mode and debug mode it also shows types which have become extinct. @@ -1418,15 +1413,15 @@ The menu omits those two choices when used for {\tt \#genocide}. Autocompletes. Default key is `{\tt M-g}'. %.lp -\item[\tb{\#glance}] +\item[\#glance] Show what type of thing a map symbol corresponds to. Default key is `{\tt ;}'. %.lp -\item[\tb{\#help}] +\item[\#help] Show the help menu. Default key is `{\tt ?}', and also `{\tt h}' if {\it number\textunderscore pad\/} is on. %.lp -\item[\tb{\#herecmdmenu}] +\item[\#herecmdmenu] Show a menu of possible actions directed at your current location. The menu is limited to a subset of the likeliest actions, not an exhaustive set of all possibilities. @@ -1436,29 +1431,29 @@ If mouse support is enabled and the {\it herecmd\textunderscore menu\/} option is On, clicking on the hero (or steed when mounted) will execute this command. %.lp -\item[\tb{\#history}] +\item[\#history] Show long version and game history. Default key is `{\tt V}'. %.lp -\item[\tb{\#inventory}] +\item[\#inventory] Show your inventory. Default key is `{\tt i}'. %.lp -\item[\tb{\#inventtype}] +\item[\#inventtype] Inventory specific item types. Default key is `{\tt I}'. %.lp -\item[\tb{\#invoke}] +\item[\#invoke] Invoke an object's special powers. Autocompletes. Default key is `{\tt M-i}'. %.lp -\item[\tb{\#jump}] +\item[\#jump] Jump to another location. Autocompletes. Default key is `{\tt M-j}', and also `{\tt j}' if {\it number\textunderscore pad\/} is on. %.lp -\item[\tb{\#kick}] +\item[\#kick] Kick something. Default key is `{\tt \^{}D}', and also `{\tt k}' if {\it number\textunderscore pad\/} is on. %.lp -\item[\tb{\#known}] +\item[\#known] Show what object types have been discovered. Default key is `{\tt $\backslash$}'. \\ @@ -1467,30 +1462,30 @@ The `{\tt m}' prefix allows assigning a new value to the {\it sortdiscoveries\/} option to control the order in which the discoveries are displayed. %.lp -\item[\tb{\#knownclass}] +\item[\#knownclass] Show discovered types for one class of objects. Default key is `{\tt `}'. \\ %.lp "" The `{\tt m}' prefix operates the same as for {\tt \#known}. %.lp -\item[\tb{\#levelchange}] +\item[\#levelchange] Change your experience level. Autocompletes. Debug mode only. %.lp -\item[\tb{\#lightsources}] +\item[\#lightsources] Show mobile light sources. Autocompletes. Debug mode only. %.lp -\item[\tb{\#look}] +\item[\#look] Look at what is here, under you. Default key is `{\tt :}'. %.lp -\item[\tb{\#lookaround}] +\item[\#lookaround] Describe what you can see, or remember, of your surroundings. %.lp -\item[\tb{\#loot}] +\item[\#loot] Loot a box or bag on the floor beneath you, or the saddle from a steed standing next to you. Autocompletes. Precede with the `{\tt m}' prefix to skip containers at your location @@ -1498,17 +1493,17 @@ and go directly to removing a saddle. Default key is `{\tt M-l}', and also `{\tt l}' if {\it number\textunderscore pad\/} is on. %.lp -\item[\tb{\#monster}] +\item[\#monster] Use a monster's special ability (when polymorphed into monster form). Autocompletes. Default key is `{\tt M-m}'. %.lp -\item[\tb{\#name}] +\item[\#name] Name a monster, an individual object, or a type of object. Same as ``{\tt \#call}''. Autocompletes. Default keys are `{\tt N}', `{\tt M-n}', and `{\tt M-N}'. %.lp -\item[\tb{\#offer}] +\item[\#offer] Offer a sacrifice to the gods. Autocompletes. Default key is `{\tt M-o}'.\\ %.lp "" You'll need to find an altar to have any chance at success. @@ -1516,21 +1511,21 @@ Corpses of recently killed monsters are the fodder of choice. %.lp "" The `{\tt m}' prefix skips offering any items which are on the altar.\\ %.lp -\item[\tb{\#open}] +\item[\#open] Open a door. Default key is `{\tt o}'. %.lp -\item[\tb{\#options}] +\item[\#options] Show and change option settings. Default key is `{\tt O}'. Precede with the {\tt m} prefix to show advanced options. %.lp -\item[\tb{\#optionsfull}] +\item[\#optionsfull] Show advanced game option settings. No default key. Precede with the `{\tt m}' prefix to execute the simpler options command. (Mainly useful if you use {\tt BINDING=O:optionsfull} to switch `{\tt O}' from simple options back to traditional advanced options.) %.lp -\item[\tb{\#overview}] +\item[\#overview] Display information you've discovered about the dungeon. Any visited level % [note: amnesia no longer causes levels to be forgotten so exclude this] @@ -1553,7 +1548,7 @@ Autocompletes. Default keys are `{\tt \^{}O}', and `{\tt M-O}'. % DON'T PANIC! %.lp -\item[\tb{\#panic}] +\item[\#panic] Test the panic routine. Terminates the current game. Autocompletes. @@ -1565,10 +1560,10 @@ You can set the {\it paranoid\textunderscore confirmation:quit\/} option to require a response of ``{\tt yes}'' instead. %.lp -\item[\tb{\#pay}] +\item[\#pay] Pay your shopping bill. Default key is `{\tt p}'. %.lp -\item[\tb{\#perminv}] +\item[\#perminv] If persistent inventory display is supported and enabled (with the {\it perm\textunderscore invent\/} option), interact with it instead of with the map. You'll be prompted for menu scrolling keystrokes such @@ -1576,16 +1571,16 @@ as `{\tt \verb+>+}' and `{\tt \verb+<+}'. Press {\tt Return} or {\tt Escape} to resume normal play. Default key is {\tt \verb+|+}. %.lp -\item[\tb{\#pickup}] +\item[\#pickup] Pick up things at the current location. Default key is `{\tt ,}'. The `{\tt m}' prefix forces use of a menu. %.lp -\item[\tb{\#polyself}] +\item[\#polyself] Polymorph self. Autocompletes. Debug mode only. %.lp -\item[\tb{\#pray}] +\item[\#pray] Pray to the gods for help. Autocompletes. Default key is `{\tt M-p}'.\\ %.lp "" Praying too soon after receiving prior help is a bad idea. @@ -1597,19 +1592,19 @@ by default, and you can reset the {\it paranoid\textunderscore confirmation\/} option to disable it. %.lp -\item[\tb{\#prevmsg}] +\item[\#prevmsg] Show previously displayed game messages. Default key is `{\tt \^{}P}'. %.lp -\item[\tb{\#puton}] +\item[\#puton] Put on an accessory (ring, amulet, etc). Default key is `{\tt P}'. %.lp -\item[\tb{\#quaff}] +\item[\#quaff] Quaff (drink) something. Default key is `{\tt q}'.\\ %.lp "" The {\tt m} prefix skips drinking from a fountain or sink if there is one at your location. %.lp -\item[\tb{\#quit}] +\item[\#quit] Quit the program without saving your game. Autocompletes.\\ %.lp "" Since using this command by accident would throw away the current game, @@ -1620,42 +1615,42 @@ You can set the {\it paranoid\textunderscore confirmation:quit\/} option to require a response of ``{\tt yes}'' instead. %.lp -\item[\tb{\#quiver}] +\item[\#quiver] Select ammunition for quiver. Default key is `{\tt Q}'. %.lp -\item[\tb{\#read}] +\item[\#read] Read a scroll, a spellbook, or something else. Default key is `{\tt r}'. %.lp -\item[\tb{\#redraw}] +\item[\#redraw] Redraw the screen. Default key is `{\tt \^{}R}', and also `{\tt \^{}L}' if {\it number\textunderscore pad\/} is on. %.lp -\item[\tb{\#remove}] +\item[\#remove] Remove an accessory (ring, amulet, etc). Default key is `{\tt R}'. %.lp -\item[{\tb{\#repeat}}] +\item[\#repeat] Repeat the previous command. Default key is~`{\tt \^{}A}'. %.lp -\item[\tb{\#reqmenu}] +\item[\#reqmenu] Prefix key to modify the behavior or request menu from some commands. Prevents autopickup when used with movement commands. Default key is `{\tt m}'. %.lp -\item[\tb{\#retravel}] +\item[\#retravel] Travel to a previously selected travel destination. Default key is `{\tt C-\textunderscore }'. See also {\tt \#travel}. %.lp -\item[\tb{\#ride}] +\item[\#ride] Ride (or stop riding) a saddled creature. Autocompletes. Default key is `{\tt M-R}'. %.lp -\item[\tb{\#rub}] +\item[\#rub] Rub a lamp or a stone. Autocompletes. Default key is `{\tt M-r}'. %.lp -\item[\tb{\#run}] +\item[\#run] Prefix key to run towards a direction. Default key is `{\tt G}' when {\it number\textunderscore pad\/} @@ -1665,7 +1660,7 @@ is off, is set to 1~or~3, otherwise `{\tt M-5}' when it is set to 2~or~4. %.lp -\item[\tb{\#rush}] +\item[\#rush] Prefix key to rush towards a direction. Default key is `{\tt g}' when {\it number\textunderscore pad\/} @@ -1675,45 +1670,45 @@ is off, is set to 1~or~3, otherwise `{\tt 5}' when it is set to 2~or~4. %.lp -\item[\tb{\#save}] +\item[\#save] Save the game and exit the program. Default key is `{\tt S}'. %.lp -\item[\tb{\#saveoptions}] +\item[\#saveoptions] Save configuration options to the config file. This will overwrite the file, removing all comments, so if you have manually edited the config file, don't use this. %.lp -\item[\tb{\#search}] +\item[\#search] Search for traps and secret doors around you. Default key is `{\tt s}'. %.lp -\item[\tb{\#seeall}] +\item[\#seeall] Show all equipment in use. Default key is `{\tt *}'. %.lp "" \\ Will display in-use items in a menu even when there is only one. %.lp -\item[\tb{\#seeamulet}] +\item[\#seeamulet] Show the amulet currently worn. Default key is `{\tt "}'. %.lp "" \\ Using the `{\tt m}' prefix will force the display of a worn amulet in a menu rather than with just a message. %.lp -\item[\tb{\#seearmor}] +\item[\#seearmor] Show the armor currently worn. Default key is `{\tt [}'. %.lp "" \\ Will display worn armor in a menu even when there is only thing worn. %.lp -\item[\tb{\#seerings}] +\item[\#seerings] Show the ring(s) currently worn. Default key is `{\tt =}'. %.lp "" Will display worn rings in a menu if there are two (or there is just one and is a meat ring rather than a ``real'' ring). Use the `{\tt m}' prefix to force a menu for one ring. %.lp -\item[\tb{\#seetools}] +\item[\#seetools] Show the tools currently in use. Default key is `{\tt (}'. %.lp "" Will display the result in a message if there is one tool in use (worn @@ -1722,7 +1717,7 @@ attached to pets). Will display a menu if there are more than one or if the command is preceded by the `{\tt m}' prefix. %.lp -\item[\tb{\#seeweapon}] +\item[\#seeweapon] Show the weapon currently wielded. Default key is `{\tt )}'. %.lp "" If dual-wielding, a separate message about the secondary weapon will be @@ -1731,7 +1726,7 @@ Using the `{\tt m}' prefix will force a menu and it will include primary weapon, alternate weapon even when not dual-wielding, and also whatever is currently assigned to the quiver slot. %.lp -\item[\tb{\#shell}] +\item[\#shell] Do a shell escape, switching from NetHack to a subprocess. Can be disabled at the time the program is built. When enabled, access for specific users can be controlled by the system @@ -1739,17 +1734,17 @@ configuration file. Use the shell command `{\tt exit}' to return to the game. Default key is `{\tt !}'. %.lp -\item[\tb{\#showgold}] +\item[\#showgold] Report the gold in your inventory, including gold you know about in containers you're carrying. If you are inside a shop, report any credit or debt you have in that shop. Default key is `{\tt \$}'. %.lp -\item[\tb{\#showspells}] +\item[\#showspells] List and reorder known spells. Default key is `{\tt +}'. %.lp -\item[\tb{\#showtrap}] +\item[\#showtrap] Describe an adjacent trap, possibly covered by objects or a monster. To be eligible, the trap must already be discovered. (The ``{\tt \#terrain}'' command can display your map with all objects and @@ -1757,15 +1752,15 @@ monsters temporarily removed, making it possible to see all discovered traps.) Default key is `{\tt \^{}}'. %.lp -\item[\tb{\#sit}] +\item[\#sit] Sit down. Autocompletes. Default key is `{\tt M-s}'. %.lp -\item[\tb{\#stats}] +\item[\#stats] Show memory usage statistics. Autocompletes. Debug mode only. %.lp -\item[\tb{\#suspend}] +\item[\#suspend] Suspend the game, switching from NetHack to the terminal it was started from without performing save-and-exit. Can be disabled at the time the program is built. @@ -1775,19 +1770,19 @@ UNIX. Use the shell command `{\tt fg}' to return to the game. Default key is `{\tt \^{}Z}'. %.lp -\item[\tb{\#swap}] +\item[\#swap] Swap wielded and secondary weapons. Default key is `{\tt x}'. %.lp -\item[\tb{\#takeoff}] +\item[\#takeoff] Take off one piece of armor. Default key is `{\tt T}'. %.lp -\item[\tb{\#takeoffall}] +\item[\#takeoffall] Remove all armor. Default key is `{\tt A}'. %.lp -\item[\tb{\#teleport}] +\item[\#teleport] Teleport around the level. Default key is `{\tt \^{}T}'. %.lp -\item[\tb{\#terrain}] +\item[\#terrain] Show map without obstructions. In normal play you can view the explored portion of the current level's map without monsters; without monsters and objects; or without monsters, @@ -1803,7 +1798,7 @@ In debug mode there are additional choices.\\ Autocompletes. Default key is `{\tt }' or `{\tt }' (see {\it Del\/} above). %.lp -\item[\tb{\#therecmdmenu}] +\item[\#therecmdmenu] Show a menu of possible actions directed at a location next to you. The menu is limited to a subset of the likeliest actions, not an exhaustive set of all possibilities. @@ -1814,15 +1809,15 @@ Autocompletes. %% If mouse support is enabled and the {\it herecmd\textunderscore menu\/} %% option is On, clicking on an adjacent location will execute this command. %.lp -\item[\tb{\#throw}] +\item[\#throw] Throw something. Default key is `{\tt t}'. %.lp -\item[\tb{\#timeout}] +\item[\#timeout] Look at the timeout queue. Autocompletes. Debug mode only. %.lp -\item[\tb{\#tip}] +\item[\#tip] Tip over a container (bag or box) to pour out its contents. When there are containers on the floor, the game will prompt to pick one of them or ``tip something being carried''. @@ -1839,7 +1834,7 @@ floor container menu. %.lp "" Autocompletes. Default key is `{\tt M-T}'. %.lp -\item[\tb{\#travel}] +\item[\#travel] Travel to a specific location on the map. Default key is `{\tt \textunderscore }'. Using the ``request menu'' prefix shows a menu of interesting targets in sight @@ -1849,10 +1844,10 @@ option is on, the top line will show ``(no travel path)'' if your character does not know of a path to that location. See also {\tt \#retravel}. %.lp -\item[\tb{\#turn}] +\item[\#turn] Turn undead away. Autocompletes. Default key is `{\tt M-t}'. %.lp -\item[\tb{\#twoweapon}] +\item[\#twoweapon] Toggle two-weapon combat on or off. Autocompletes. Default key is `{\tt X}', and also `{\tt M-2}' if {\it number\textunderscore pad\/} is off.\\ @@ -1861,16 +1856,16 @@ Note that you must use suitable weapons for this type of combat, or it will be automatically turned off. %.lp -\item[\tb{\#untrap}] +\item[\#untrap] Untrap something (trap, door, or chest). Default key is `{\tt M-u}', and `{\tt u}' if {\it number\textunderscore pad\/} is on.\\ %.lp "" In some circumstances it can also be used to rescue trapped monsters. %.lp -\item[\tb{\#up}] +\item[\#up] Go up a staircase. Default key is `{\tt <}'. %.lp -\item[\tb{\#vanquished}] +\item[\#vanquished] List vanquished monsters by type and count. \\ %.lp "" @@ -1896,7 +1891,7 @@ monsters answering `{\tt a}' will let you choose from the sort menu. Autocompletes. Default key is `{\tt M-V}'. %.lp -\item[\tb{\#version}] +\item[\#version] Print compile time options for this version of {\it NetHack\/}. %.lp @@ -1907,50 +1902,50 @@ option in your run-time configuration file to select the one you want. %.lp Autocompletes. Default key is `{\tt M-v}'. %.lp -\item[\tb{\#versionshort}] +\item[\#versionshort] Show the program's version number, plus the date and time that the running copy was built from sources (not the version's release date). Default key is `{\tt v}'. %.lp -\item[\tb{\#vision}] +\item[\#vision] Show vision array. Autocompletes. Debug mode only. %.lp -\item[\tb{\#wait}] +\item[\#wait] Rest one move while doing nothing. Default key is `{\tt .}', and also `{\tt{ }}' if {\it rest\textunderscore on\textunderscore space\/} is on. %.lp -\item[\tb{\#wear}] +\item[\#wear] Wear a piece of armor. Default key is `{\tt W}'. %.lp -\item[\tb{\#whatdoes}] +\item[\#whatdoes] Tell what a key does. Default key is `{\tt \&}'. %.lp -\item[\tb{\#whatis}] +\item[\#whatis] Show what type of thing a symbol corresponds to. Default key is `{\tt /}'. %.lp -\item[\tb{\#wield}] +\item[\#wield] Wield a weapon. Default key is `{\tt w}'. %.lp -\item[\tb{\#wipe}] +\item[\#wipe] Wipe off your face. Autocompletes. Default key is `{\tt M-w}'. %.lp -\item[\tb{\#wizborn}] +\item[\#wizborn] Show monster birth, death, genocide, and extinct statistics. Debug mode only. %.lp -\item[\tb{\#wizbury}] +\item[\#wizbury] Bury objects under and around you. Autocompletes. Debug mode only. %.lp -\item[\tb{\#wizcast}] +\item[\#wizcast] Cast any spell. Debug mode only. %.lp -\item[\tb{\#wizdetect}] +\item[\#wizdetect] Reveal hidden things (secret doors or traps or unseen monsters) within a modest radius. No time elapses. @@ -1958,44 +1953,44 @@ Autocompletes. Debug mode only. Default key is `{\tt \^{}E}'. %.lp -\item[\tb{\#wizgenesis}] +\item[\#wizgenesis] Create a monster. May be prefixed by a count to create more than one. Autocompletes. Debug mode only. Default key is `{\tt \^{}G}'. %.lp -\item[\tb{\#wizidentify}] +\item[\#wizidentify] Identify all items in inventory. Autocompletes. Debug mode only. Default key is `{\tt \^{}I}'. %.lp -\item[\tb{\#wizintrinsic}] +\item[\#wizintrinsic] Set one or more intrinsic attributes. Autocompletes. Debug mode only. %.lp -\item[\tb{\#wizkill}] +\item[\#wizkill] Remove monsters from play by just pointing at them. By default the hero gets credit or blame for killing the targets. Precede this command with the `{\tt m}' prefix to override that. Autocompletes. Debug mode only. %.lp -\item[\tb{\#wizlevelport}] +\item[\#wizlevelport] Teleport to another level. Autocompletes. Debug mode only. Default key is `{\tt \^{}V}'. %.lp -\item[\tb{\#wizmap}] +\item[\#wizmap] Map the level. Autocompletes. Debug mode only. Default key is `{\tt \^{}F}'. %.lp -\item[\tb{\#wizrumorcheck}] +\item[\#wizrumorcheck] Verify rumor boundaries by displaying first and last true rumors and first and last false rumors.\\ %.lp "" @@ -2005,38 +2000,38 @@ and hallucinatory monsters.\\ Autocompletes. Debug mode only. %.lp -\item[\tb{\#wizseenv}] +\item[\#wizseenv] Show map locations' seen vectors. Autocompletes. Debug mode only. %.lp -\item[\tb{\#wizsmell}] +\item[\#wizsmell] Smell monster. Autocompletes. Debug mode only. %.lp -\item[\tb{\#wizwhere}] +\item[\#wizwhere] Show locations of special levels. Autocompletes. Debug mode only. %.lp -\item[\tb{\#wizwish}] +\item[\#wizwish] Wish for something. Autocompletes. Debug mode only. Default key is `{\tt \^{}W}'. %.lp -\item[\tb{\#wmode}] +\item[\#wmode] Show wall modes. Autocompletes. Debug mode only. %.lp -\item[\tb{\#zap}] +\item[\#zap] Zap a wand. Default key is `{\tt z}'. %.lp -\item[\tb{\#?}] +\item[\#?] Help menu: get the list of available extended commands. -\elist +\end{description} %.pg \nd If your keyboard has a meta key (which, when pressed in combination @@ -2063,116 +2058,116 @@ equivalent is used for another command, so the three key combination {\tt meta+Shift+letter} is needed. %.BR 1 -\blist{} +\begin{description}[font=\ttfamily] %.lp -\item[\tb{M-?}] +\item[M-?] {\tt\#?} (not supported by all platforms) %.lp -\item[\tb{M-2}] +\item[M-2] {\tt\#twoweapon} (unless the {\it number\textunderscore pad\/} option is enabled) %.lp -\item[\tb{M-a}] +\item[M-a] {\tt\#adjust} %.lp -\item[\tb{M-A}] +\item[M-A] {\tt\#annotate} %.lp -\item[\tb{M-c}] +\item[M-c] {\tt\#chat} %.lp -\item[\tb{M-C}] +\item[M-C] {\tt\#conduct} %.lp -\item[\tb{M-d}] +\item[M-d] {\tt\#dip} %.lp -\item[\tb{M-e}] +\item[M-e] {\tt\#enhance} %.lp -\item[\tb{M-f}] +\item[M-f] {\tt\#force} %.lp -\item[\tb{M-g}] +\item[M-g] {\tt\#genocided} %.lp -\item[\tb{M-i}] +\item[M-i] {\tt\#invoke} %.lp -\item[\tb{M-j}] +\item[M-j] {\tt\#jump} %.lp -\item[\tb{M-l}] +\item[M-l] {\tt\#loot} %.lp -\item[\tb{M-m}] +\item[M-m] {\tt\#monster} %.lp -\item[\tb{M-n}] +\item[M-n] {\tt\#name} %.lp -\item[\tb{M-o}] +\item[M-o] {\tt\#offer} %.lp -\item[\tb{M-O}] +\item[M-O] {\tt\#overview} %.lp -\item[\tb{M-p}] +\item[M-p] {\tt\#pray} %.Ip -\item[\tb{M-r}] +\item[M-r] {\tt\#rub} %.lp -\item[\tb{M-R}] +\item[M-R] {\tt\#ride} %.lp -\item[\tb{M-s}] +\item[M-s] {\tt\#sit} %.lp -\item[\tb{M-t}] +\item[M-t] {\tt\#turn} %.lp -\item[\tb{M-T}] +\item[M-T] {\tt\#tip} %.lp -\item[\tb{M-u}] +\item[M-u] {\tt\#untrap} %.lp -\item[\tb{M-v}] +\item[M-v] {\tt\#version} %.lp -\item[\tb{M-V}] +\item[M-V] {\tt\#vanquished} %.lp -\item[\tb{M-w}] +\item[M-w] {\tt\#wipe} %.lp -\item[\tb{M-X}] +\item[M-X] {\tt\#exploremode} -\elist +\end{description} %.pg \nd If the {\it number\textunderscore pad\/} option is on, some additional letter commands are available: -\blist{} +\begin{description}[font=\ttfamily] %.lp -\item[\tb{h}] +\item[h] {\tt\#help} %.lp -\item[\tb{j}] +\item[j] {\tt\#jump} %.lp -\item[\tb{k}] +\item[k] {\tt\#kick} %.lp -\item[\tb{l}] +\item[l] {\tt\#loot} %.lp -\item[\tb{N}] +\item[N] {\tt\#name} %.lp -\item[\tb{u}] +\item[u] {\tt\#untrap} -\elist +\end{description} %.BR 1 \"blank line for extra separation; plain text output looks better @@ -2429,21 +2424,21 @@ Several aspects of shop behavior might be unexpected. \begin{itemize} % note: a bullet is the default item label so we could omit [$\bullet$] here %.lp \(bu 2 -\item[$\bullet$] +\item The price of a given item can vary due to a variety of factors. %.lp \(bu 2 -\item[$\bullet$] +\item A shopkeeper treats the spot immediately inside the door as if it were outside the shop. %.lp \(bu 2 -\item[$\bullet$] +\item While the shopkeeper watches you like a hawk, he or she will generally ignore any other customers. %.lp \(bu 2 -\item[$\bullet$] +\item If a shop is ``closed for inventory,'' it will not open of its own accord. %.lp \(bu 2 -\item[$\bullet$] +\item Shops do not get restocked with new items, regardless of inventory depletion. \end{itemize} @@ -3678,62 +3673,62 @@ in which you might accomplish them. %.PS "Mines'\~End\~" \settowidth{\achwidth}{\tt Mines'~End~} \addtolength{\achwidth}{\labelsep} -\blist{\leftmargin \achwidth \topsep 1mm \itemsep 0mm} +\begin{description}[leftmargin=\achwidth, topsep=1mm, itemsep=0mm, labelwidth=*, font=\ttfamily, align=right] %.PL Shop -\item[{\tt }] +\item[] Attained rank title {\it Rank}. -\item[{\tt Shop}] +\item[Shop] Entered a shop. -\item[{\tt Temple}] +\item[Temple] Entered a temple. -\item[{\tt Mines}] +\item[Mines] Entered the Gnomish Mines. -\item[{\tt Town}] +\item[Town] Entered Mine Town. -\item[{\tt Oracle}] +\item[Oracle] Consulted the Oracle of Delphi. -\item[{\tt Novel}] +\item[Novel] Read a passage from a Discworld Novel. -\item[{\tt Sokoban}] +\item[Sokoban] Entered Sokoban. -\item[{\tt "Big~Room"}] +\item["Big~Room"] Entered the Big Room. -\item[{\tt "Soko-Prize"}] +\item["Soko-Prize"] Explored to the top of Sokoban and found a special item there. -\item[{\tt Mines'~End}] +\item[Mines'~End] Explored to the bottom of the Gnomish Mines and found a special item there. -\item[{\tt Medusa}] +\item[Medusa] Defeated Medusa. -\item[{\tt Tune}] +\item[Tune] Discovered the tune that can be used to open and close the drawbridge on the Castle level. -\item[{\tt Bell}] +\item[Bell] Acquired the Bell of Opening. -\item[{\tt Gehennom}] +\item[Gehennom] Entered Gehennom. -\item[{\tt Candle}] +\item[Candle] Acquired the Candelabrum of Invocation. -\item[{\tt Book}] +\item[Book] Acquired the Book of the Dead. -\item[{\tt Invocation}] +\item[Invocation] Gained access to the bottommost level of Gehennom. -\item[{\tt Amulet}] +\item[Amulet] Acquired the fabled Amulet of Yendor. -\item[{\tt Endgame}] +\item[Endgame] Reached the Elemental Planes. -\item[{\tt Astral}] +\item[Astral] Reached the Astral Plane level. -\item[{\tt Blind}] +\item[Blind] Blind from birth. -\item[{\tt Deaf}] +\item[Deaf] Deaf from birth. -\item[{\tt Nudist}] +\item[Nudist] Never wore any armor. -\item[{\tt Pauper}] +\item[Pauper] Started out with no possessions. -\item[{\tt Ascended}] +\item[Ascended] Delivered the Amulet to its final destination. -\elist +\end{description} %.PE %.ED @@ -3860,8 +3855,8 @@ settings particular to that directive. Here is a list of allowed directives: %.lp -\blist{} -\item[\bb{OPTIONS}] +\begin{description} +\item[OPTIONS] There are two types of options, boolean and compound options. Boolean options toggle a setting on or off, while compound options take more diverse values. @@ -3882,35 +3877,35 @@ Example: %.ed %.lp -\item[\bb{HACKDIR}] +\item[HACKDIR] Default location of files {\it NetHack\/} needs. On Windows HACKDIR defaults to the location of the {\it NetHack.exe\/} or {\it NetHackw.exe\/} file so setting HACKDIR to override that is not usually necessary or recommended. %.lp -\item[\bb{LEVELDIR}] +\item[LEVELDIR] The location that in-progress level files are stored. Defaults to HACKDIR, must be writable. %.lp -\item[\bb{SAVEDIR}] +\item[SAVEDIR] The location where saved games are kept. Defaults to HACKDIR, must be writable. %.lp -\item[\bb{BONESDIR}] +\item[BONESDIR] The location that bones files are kept. Defaults to HACKDIR, must be writable. %.lp -\item[\bb{LOCKDIR}] +\item[LOCKDIR] The location that file synchronization locks are stored. Defaults to HACKDIR, must be writable. %.lp -\item[\bb{TROUBLEDIR}] +\item[TROUBLEDIR] The location that a record of game aborts and self-diagnosed game problems is kept. Defaults to HACKDIR, must be writable. % % config file entries beyond this point are shown alphabetically % %.lp -\item[\bb{AUTOCOMPLETE}] +\item[AUTOCOMPLETE] Enable or disable an extended command autocompletion. Autocompletion has no effect for the X11 windowport. You can specify multiple autocompletions. To enable @@ -3927,11 +3922,11 @@ Example: %.ed %.lp -\item[\bb{AUTOPICKUP\textunderscore EXCEPTION}] +\item[AUTOPICKUP\textunderscore EXCEPTION] Set exceptions to the {{\it pickup\textunderscore types\/}} option. See the ``Configuring Autopickup Exceptions'' section. %.lp -\item[\bb{BINDINGS}] +\item[BINDINGS] Change the key bindings of some special keys, menu accelerators, extended commands, or mouse buttons. You can specify multiple bindings. Format is key followed by the command, separated by a colon. @@ -3946,7 +3941,7 @@ Example: %.ed %.lp -\item[\bb{CHOOSE}] +\item[CHOOSE] Chooses at random one of the comma-separated parameters as an active section name. Lines in other sections are ignored. @@ -3972,27 +3967,27 @@ section begins; whatever follows will be common to all sections. Otherwise the last section extends to the end of the options file. %.lp -\item[\bb{MENUCOLOR}] +\item[MENUCOLOR] Highlight menu lines with different colors. See the ``Configuring Menu Colors`` section. %.lp -\item[\bb{MSGTYPE}] +\item[MSGTYPE] Change the way messages are shown in the top status line. See the ``Configuring Message Types`` section. %.lp -\item[\bb{ROGUESYMBOLS}] +\item[ROGUESYMBOLS] Custom symbols for the rogue level's symbol set. See {\it SYMBOLS} below. %.lp -\item[\bb{SOUND}] +\item[SOUND] Define a sound mapping. See the ``Configuring User Sounds'' section. %.lp -\item[\bb{SOUNDDIR}] +\item[SOUNDDIR] Define the directory that contains the sound files. See the ``Configuring User Sounds'' section. %.lp -\item[\bb{SYMBOLS}] +\item[SYMBOLS] Override one or more symbols in the symbol set used for all dungeon levels except for the special rogue level. See the ``Modifying {\it NetHack\/} Symbols'' section. @@ -4008,7 +4003,7 @@ Example: %.ed %.lp -\item[\bb{WIZKIT}] +\item[WIZKIT] Debug mode only: extra items to add to initial inventory. Value is the name of a text file containing a list of item names, one per line, up to a maximum of 128 lines. @@ -4021,7 +4016,7 @@ Example: WIZKIT=~/wizkit.txt \end{verbatim} %.ed -\elist +\end{description} %.lp "" %.pg @@ -4115,17 +4110,17 @@ Some options are persistent, and are saved and reloaded along with the game. Changing a persistent option in the configuration file applies only to new games. -\blist{} +\begin{description}[font=\itshape] %.lp -\item[\ib{accessiblemsg}] +\item[accessiblemsg] Add location or direction information to messages (default is off). %.lp -\item[\ib{acoustics}] +\item[acoustics] Enable messages about what your character hears (default on). Note that this has nothing to do with your computer's audio capabilities. Persistent. %.lp -\item[\ib{alignment}] +\item[alignment] Your starting alignment ({\tt align:lawful}, {\tt align:neutral}, or {\tt align:chaotic}). You may specify just the first letter. @@ -4138,21 +4133,21 @@ If {\tt align} is not specified, there is no default value; player will be prompted unless role and/or race forces a choice for alignment. Cannot be set with the `{\tt O}' command. Persistent. %.lp -\item[\ib{autodescribe}] +\item[autodescribe] Automatically describe the terrain under cursor when asked to get a location on the map (default true). The {\it whatis\textunderscore coord\/} option controls whether the description includes map coordinates. %.lp -\item[\ib{autodig}] +\item[autodig] Automatically dig if you are wielding a digging tool and moving into a place that can be dug (default false). Persistent. %.lp -\item[\ib{autoopen}] +\item[autoopen] Walking into a closed door attempts to open it (default true). Persistent. %.lp -\item[\ib{autopickup}] +\item[autopickup] Automatically pick up things onto which you move (default off). Persistent. \\ @@ -4163,7 +4158,7 @@ See ``{\it pickup\textunderscore types\/}'' and also %.lp "" Note: prior to version 3.7.0, the default for {\it autopickup\/} was {\it on}. %.lp -\item[\ib{autoquiver}] +\item[autoquiver] This option controls what happens when you attempt the `{\tt f}' (fire) command when nothing is quivered or readied (default false). When true, the computer will fill @@ -4176,7 +4171,7 @@ with the `{\tt Q}' command instead. If no weapon is found or the option is false, the `{\tt t}' (throw) command is executed instead. Persistent. %.lp -\item[\ib{autounlock}] +\item[autounlock] %\hyphenation{apply\-key}%this needs to be tested... Controls what action to take when attempting to walk into a locked door or to loot a locked container. @@ -4187,32 +4182,32 @@ Takes a plus-sign separated list of values: %.PS Apply-Key \settowidth{\auwidth}{\tt Apply-Key} \addtolength{\auwidth}{\labelsep} -\blist{\leftmargin \auwidth \topsep 1mm \itemsep 0mm} +\begin{description}[leftmargin=\auwidth, topsep=1mm, itemsep=0mm, labelwidth=*, font=\ttfamily, align=right] %.PL Untrap -\item[{\tt Untrap}] +\item[Untrap] prompt about whether to attempt to find a trap; it might fail to find one even when present; if it does find one, it will ask whether you want to try to disarm the trap; if you decline, your character will forget that the door or box is trapped; %.PL Apply-Key -\item[{\tt Apply-Key}] +\item[Apply-Key] if carrying a key or other unlocking tool, prompt about using it; %.PL Kick -\item[{\tt Kick}] +\item[Kick] kick the door (if you omit untrap or decline to attempt untrap and you omit apply-key or you lack a key or you decline to use the key; has no effect on containers); %.PL Force -\item[{\tt Force}] +\item[Force] try to force a container's lid with your currently wielded weapon (if you omit untrap or decline to attempt untrap and you omit apply-key or you lack a key or you decline to use the key; has no effect on doors); %.PL None -\item[{\tt None}] +\item[None] none of the above; can't be combined with the other choices. %.PE -\elist +\end{description} Omitting the value is treated as if {\tt autounlock:apply-key}. Preceding {\tt autounlock} with `{\tt !}' or ``{\tt no}'' is treated as {\tt autounlock:none}. @@ -4227,49 +4222,49 @@ destroy some of its contents or damage your weapon or both. The default is Apply-Key. Persistent. %.lp -\item[\ib{blind}] +\item[blind] Start the character permanently blind (default false). Persistent. %.lp -\item[\ib{bones}] +\item[bones] Allow saving and loading bones files (default true). Persistent. %.lp -\item[\ib{boulder}] +\item[boulder] Set the character used to display boulders (default is the ``large rock'' class symbol, `{\tt `}'). %.lp -\item[\ib{catname}] +\item[catname] Name your starting cat (for example, ``{\tt catname:Morris}''). Cannot be set with the `{\tt O}' command. %.lp character -\item[\ib{character}] +\item[character] Synonym for ``{\tt role}'' to pick the type of your character (for example ``{\tt character:Monk}''). See {\it role\/} for more details. %.lp -\item[\ib{checkpoint}] +\item[checkpoint] Save game state after each level change, for possible recovery after program crash (default on). Persistent. %.lp -\item[\ib{cmdassist}] +\item[cmdassist] Have the game provide some additional command assistance for new players if it detects some anticipated mistakes (default on). %.lp -\item[\ib{confirm}] +\item[confirm] Have user confirm attacks on pets, shopkeepers, and other peaceable creatures (default on). Persistent. %.lp -\item[\ib{dark\textunderscore room}] +\item[dark\textunderscore room] Show out-of-sight areas of lit rooms (default on). Persistent. %.lp -\item[\ib{deaf}] +\item[deaf] Start the character permanently deaf (default false). Persistent. %.lp -\item[\ib{dropped\textunderscore nopick}] +\item[dropped\textunderscore nopick] If this option is on, items you dropped will not be automatically picked up, even if ``{\it autopickup\/}'' is also on and they are in ``{\it pickup\textunderscore types\/}'' or match a positive autopickup exception (default on). Persistent. %.lp -\item[\ib{disclose}] +\item[disclose] Controls what information the program reveals when the game ends. Value is a space separated list of prompting/category pairs (default is `{\tt ni na nv ng nc no}', @@ -4339,11 +4334,11 @@ traps and each other as well as by you. And the dungeon overview shows all levels you had visited but does not reveal things about them that you hadn't discovered. %.lp -\item[\ib{dogname}] +\item[dogname] Name your starting dog (for example, ``{\tt dogname:Fang}''). Cannot be set with the `{\tt O}' command. %.lp -\item[\ib{extmenu}] +\item[extmenu] Changes the extended commands interface to pop-up a menu of available commands. It is keystroke compatible with the traditional interface except that it does not require that you hit Enter. @@ -4354,32 +4349,32 @@ command, it controls whether the menu shows all available commands (on) or just the subset of commands which have traditionally been considered extended ones (off). %.lp -\item[\ib{female}] +\item[female] An obsolete synonym for ``{\tt gender:female}''. Cannot be set with the `{\tt O}' command. %.lp -\item[\ib{fireassist}] +\item[fireassist] This option controls what happens when you attempt the `{\tt f}' (fire) and don't have an appropriate launcher, such as a bow or a sling, wielded. If on, you will automatically wield the launcher. Default is on. %.lp -\item[\ib{fixinv}] +\item[fixinv] An object's inventory letter sticks to it when it's dropped (default on). If this is off, dropping an object shifts all the remaining inventory letters. Persistent. %.lp -\item[\ib{force\textunderscore invmenu}] +\item[force\textunderscore invmenu] Commands asking for an inventory item show a menu instead of a text query with possible menu letters. Default is off. %.lp -\item[\ib{fruit}] +\item[fruit] Name a fruit after something you enjoy eating (for example, ``{\tt fruit:mango}'') (default ``{\tt slime mold}''). Basically a nostalgic whimsy that {\it NetHack\/} uses from time to time. You should set this to something you find more appetizing than slime mold. Apples, oranges, pears, bananas, and melons already exist in {\it NetHack\/}, so don't use those. %.lp -\item[\ib{gender}] +\item[gender] Your starting gender ({\tt gender:male} or {\tt gender:female}). You may specify just the first letter. Although you can @@ -4394,26 +4389,26 @@ If {\tt gender} is not specified, there is no default value; player will be prompted unless role and/or race forces a choice for gender. Cannot be set with the `{\tt O}' command. Persistent. %.lp -\item[\ib{goldX}] +\item[goldX] When filtering objects based on bless/curse state (BUCX), whether to treat gold pieces as {\tt X} (unknown bless/curse state, when `on') or {\tt U} (known to be uncursed, when `off', the default). Gold is never blessed or cursed, but it is not described as ``uncursed'' even when the {\it implicit\textunderscore uncursed\/} option is `off'. %.lp -\item[\ib{help}] +\item[help] If more information is available for an object looked at with the `{\tt /}' command, ask if you want to see it (default on). Turning help off makes just looking at things faster, since you aren't interrupted with the ``{\tt More info?}'' prompt, but it also means that you might miss some interesting and/or important information. Persistent. %.lp -\item[\ib{herecmd\textunderscore menu}] +\item[herecmd\textunderscore menu] When using a windowport that supports mouse and clicking on yourself or next to you, show a menu of possible actions for the location. Same as ``{\tt \#herecmdmenu}'' and ``{\tt \#therecmdmenu}'' commands. %.lp -\item[\ib{hilite\textunderscore pet}] +\item[hilite\textunderscore pet] Visually distinguish pets from similar animals (default off). The behavior of this option depends on the type of windowing you use. In text windowing, text highlighting or inverse video is often used; @@ -4424,14 +4419,14 @@ With the tty or curses interface, the {\it petattr\/} option controls how to highlight pets and setting it will turn the {\it hilite\textunderscore pet\/} option on or off as warranted. %.lp -\item[\ib{hilite\textunderscore pile}] +\item[hilite\textunderscore pile] Visually distinguish piles of objects from individual objects (default off). The behavior of this option depends on the type of windowing you use. In text windowing, text highlighting or inverse video is often used; with tiles, generally displays a small plus-symbol beside the object on the top of the pile. %.lp -\item[\ib{hitpointbar}] +\item[hitpointbar] Show a hit point bar graph behind your name and title in the status display (default off). \\ @@ -4458,14 +4453,14 @@ To resize that, use the {\tt \#optionsfull} command to toggle the {\it hitpointbar\/} option off, perform the resize while it's off, then use the same command to toggle it back on.) %.lp -\item[\ib{horsename}] +\item[horsename] Name your starting horse (for example, ``{\tt horsename:Trigger}''). Cannot be set with the `{\tt O}' command. %.lp -\item[\ib{ignintr}] +\item[ignintr] Ignore interrupt signals, including breaks (default off). Persistent. %.lp -\item[\ib{implicit\textunderscore uncursed}] +\item[implicit\textunderscore uncursed] Omit ``uncursed'' from object descriptions when it can be deduced from other aspects of the description (default on). Persistent. @@ -4473,43 +4468,43 @@ Persistent. %.lp "" If you use menu coloring, you may want to turn this off. %.lp -\item[\ib{legacy}] +\item[legacy] Display an introductory message when starting the game (default on). Persistent. %.lp -\item[\ib{lit\textunderscore corridor}] +\item[lit\textunderscore corridor] Show corridor squares seen by night vision or a light source held by your character as lit (default off). Persistent. %.lp -\item[\ib{lootabc}] +\item[lootabc] When using a menu to interact with a container, use the old `{\tt a}', `{\tt b}', and `{\tt c}' keyboard shortcuts rather than the mnemonics `{\tt o}', `{\tt i}', and `{\tt b}' (default off). Persistent. %.lp -\item[\ib{mail}] +\item[mail] Enable mail delivery during the game (default on). Persistent. %.lp -\item[\ib{male}] +\item[male] An obsolete synonym for ``{\tt gender:male}''. Cannot be set with the `{\tt O}' command. %.lp -\item[\ib{mention\textunderscore decor}] +\item[mention\textunderscore decor] Give feedback when walking onto various dungeon features such as stairs, fountains, or altars which are ordinarily only described when covered by one or more objects (default off). Persistent. %.lp -\item[\ib{mention\textunderscore map}] +\item[mention\textunderscore map] Give feedback when interesting map locations change (default off). %.lp -\item[\ib{mention\textunderscore walls}] +\item[mention\textunderscore walls] Give feedback when walking against a wall (default off). Persistent. %.lp -\item[\ib{menucolors}] +\item[menucolors] Enable coloring menu lines (default off). See ``{\it Configuring Menu Colors\/}'' on how to configure the colors. %.lp -\item[\ib{menustyle}] +\item[menustyle] Controls the method used when you need to choose various objects (in response to the {\tt Drop} (aka {\tt droptype}) command, for instance). The value specified should be the first letter of one of the following: @@ -4533,40 +4528,40 @@ To avoid choosing that by accident, set {\it paranoid\textunderscore confirm:AutoAll\/} to require confirmation.) Partial skips the object class filtering and immediately displays a menu of all objects. -\item[\ib{menu\textunderscore deselect\textunderscore all}] +\item[menu\textunderscore deselect\textunderscore all] Key to deselect all items in a menu. Implemented by the Amiga, Gem, X11 and tty ports. Default `-'. -\item[\ib{menu\textunderscore deselect\textunderscore page}] +\item[menu\textunderscore deselect\textunderscore page] Key to deselect all items on this page of a menu. Implemented by the Amiga, Gem and tty ports. Default `\verb+\+'. -\item[\ib{menu\textunderscore first\textunderscore page}] +\item[menu\textunderscore first\textunderscore page] Key to jump to the first page in a menu. Implemented by the Amiga, Gem and tty ports. Default `\verb+^+'. -\item[\ib{menu\textunderscore headings}] +\item[menu\textunderscore headings] Controls how the headings in a menu are highlighted. Takes a text attribute, or text color and attribute separated by ampersand. For allowed attributes and colors, see ``{\it Configuring Menu Colors\/}``. Not all ports can actually display all types. -\item[\ib{menu\textunderscore invert\textunderscore all}] +\item[menu\textunderscore invert\textunderscore all] Key to invert all items in a menu. Implemented by the Amiga, Gem, X11 and tty ports. Default `@'. -\item[\ib{menu\textunderscore invert\textunderscore page}] +\item[menu\textunderscore invert\textunderscore page] Key to invert all items on this page of a menu. Implemented by the Amiga, Gem and tty ports. Default `\verb+~+'. -\item[\ib{menu\textunderscore last\textunderscore page}] +\item[menu\textunderscore last\textunderscore page] Key to jump to the last page in a menu. Implemented by the Amiga, Gem and tty ports. Default `\verb+|+'. -\item[\ib{menu\textunderscore next\textunderscore page}] +\item[menu\textunderscore next\textunderscore page] Key to go to the next menu page. Implemented by the Amiga, Gem and tty ports. Default `\verb+>+'. -\item[\ib{menu\textunderscore objsyms}] +\item[menu\textunderscore objsyms] % [originally menu_objsyms was a boolean] % Show object symbols in menu headings in menus where % the object symbols act as menu accelerators (default off). @@ -4601,57 +4596,57 @@ objects among classes. Supported by tty and curses. When setting the value, it can be specified by digit or keyword. The default value is {\tt Conditional} (4). -\item[\ib{menu\textunderscore overlay}] +\item[menu\textunderscore overlay] Do not clear the screen before drawing menus, and align menus to the right edge of the screen. Only for the tty port. (default on) -\item[\ib{menu\textunderscore previous\textunderscore page}] +\item[menu\textunderscore previous\textunderscore page] Key to go to the previous menu page. Implemented by the Amiga, Gem and tty ports. Default `\verb+<+'. -\item[\ib{menu\textunderscore search}] +\item[menu\textunderscore search] Key to search for some text and toggle selection state of matching menu items. Default `:'. -\item[\ib{menu\textunderscore select\textunderscore all}] +\item[menu\textunderscore select\textunderscore all] Key to select all items in a menu. Implemented by the Amiga, Gem, X11 and tty ports. Default `.'. -\item[\ib{menu\textunderscore select\textunderscore page}] +\item[menu\textunderscore select\textunderscore page] Key to select all items on this page of a menu. Implemented by the Amiga, Gem and tty ports. Default `,'. %.lp -\item[\ib{menu\textunderscore shift\textunderscore left}] +\item[menu\textunderscore shift\textunderscore left] Key to scroll a menu---one which has been scrolled right---back to the left. Implemented for {\it perm\textunderscore invent\/} only by curses and X11. Default `{\tt \verb+{+}'. %.lp -\item[\ib{menu\textunderscore shift\textunderscore right}] +\item[menu\textunderscore shift\textunderscore right] Key to scroll a menu which has text beyond the right edge to the right. Implemented for {\it perm\textunderscore invent\/} only by curses by X11. Default `{\tt \verb+}+}'. % %.lp -% \item[\ib{menu\textunderscore tab\textunderscore sep}] +% \item[menu\textunderscore tab\textunderscore sep] % Format menu entries using TAB to separate columns (default off). % Only applicable to some menus, and only useful to some interfaces. % Debug mode only. %.lp -\item[\ib{mon\textunderscore movement}] +\item[mon\textunderscore movement] Show a message when hero notices a monster movement (default is off). %.lp -\item[\ib{monpolycontrol}] +\item[monpolycontrol] Prompt for new form whenever any monster changes shape (default off). Debug mode only. %.lp -\item[\ib{montelecontrol}] +\item[montelecontrol] Prompt for destination whenever any monster gets teleported (default off). Debug mode only. %.lp -\item[\ib{mouse\textunderscore support}] +\item[mouse\textunderscore support] Allow use of the mouse for input and travel. Valid settings are: @@ -4668,12 +4663,12 @@ and negating {\it mouse\textunderscore support\/} is the same as specifying {\tt 0}. %.lp -\item[\ib{msghistory}] +\item[msghistory] The number of top line messages to save (and be able to recall with `{\tt \^{}P}') (default 20). Cannot be set with the `{\tt O}' command. %.lp -\item[\ib{msg\textunderscore window}] +\item[msg\textunderscore window] Allows you to change the way recalled messages are displayed. Currently it is only supported for tty (all four choices) and for curses (`{\tt f}' and `{\tt r}' choices, default `{\tt r}'). @@ -4692,7 +4687,7 @@ For backward compatibility, no value needs to be specified (which defaults to {\it full\/}), or it can be negated (which defaults to {\it single\/}). %.lp -\item[\ib{name}] +\item[name] Set your character's name (defaults to your user name). You can also set your character's role by appending a dash and one or more letters of the role (that is, by suffixing one of @@ -4707,18 +4702,18 @@ The former can made to behave like the latter by specifying a generic name such as ``player''. Cannot be set with the `{\tt O}' command. %.lp -\item[\ib{news}] +\item[news] Read the {\it NetHack\/} news file, if present (default on). Since the news is shown at the beginning of the game, there's no point in setting this with the `{\tt O}' command. %.lp -\item[\ib{nudist}] +\item[nudist] Start the character with no armor (default false). Persistent. %.lp -\item[\ib{null}] +\item[null] Send padding nulls to the terminal (default on). Persistent. %.lp -\item[\ib{number\textunderscore pad}] +\item[number\textunderscore pad] Use digit keys instead of letters to move (default 0 or off).\\ Valid settings are: @@ -4749,13 +4744,13 @@ When moving by numbers, to enter a count prefix for those commands which accept one (such as ``{\tt 12s}'' to search twelve times), precede it with the letter `{\tt n}' (``{\tt n12s}''). %.lp -\item[\ib{packorder}] +\item[packorder] Specify the order to list object types in (default ``\verb&")[%?+!=/(*`0_&''). The value of this option should be a string containing the symbols for the various object types. Any omitted types are filled in at the end from the previous order. %.lp -\item[\ib{paranoid\textunderscore confirmation}] +\item[paranoid\textunderscore confirmation] A space separated list of specific situations where alternate prompting is desired. The default is ``{\it paranoid\textunderscore confirmation:pray swim trap}''. @@ -4764,57 +4759,57 @@ The default is ``{\it paranoid\textunderscore confirmation:pray swim trap}''. \newlength{\pcwidth} \settowidth{\pcwidth}{\tt Were-change} \addtolength{\pcwidth}{\labelsep} -\blist{\leftmargin \pcwidth \topsep 1mm \itemsep 0mm} -\item[{\tt Confirm}] +\begin{description}[leftmargin=\pcwidth, topsep=1mm, itemsep=0mm, labelwidth=*, font=\ttfamily, align=right] +\item[Confirm] for any prompts which are set to require ``yes'' rather than `y', also require ``no'' to reject instead of accepting any non-yes response as no; changes pray and AutoAll to require ``yes'' or ``no'' too; -\item[{\tt quit~~~}] +\item[quit] require ``{\tt yes}'' rather than `{\tt y}' to confirm quitting the game or switching into non-scoring explore mode; -\item[{\tt die~~~~}] +\item[die] require ``{\tt yes}'' rather than `{\tt y}' to confirm dying (not useful in normal play; applies to explore mode); -\item[{\tt bones~~}] +\item[bones] require ``{\tt yes}'' rather than `{\tt y}' to confirm saving bones data when dying in debug mode -\item[{\tt attack~}] +\item[attack] require ``{\tt yes}'' rather than `{\tt y}' to confirm attacking a peaceful monster; -\item[{\tt wand-break}] +\item[wand-break] require ``{\tt yes}'' rather than `{\tt y}' to confirm breaking a wand with the {\it apply} command; -\item[{\tt eating~}] +\item[eating] require ``{\tt yes}'' rather than `{\tt y}' to confirm whether to continue eating; -\item[{\tt Were-change}] +\item[Were-change] require ``{\tt yes}'' rather than `{\tt y}' to confirm changing form due to lycanthropy when hero has polymorph control; -\item[{\tt pray~~~}] +\item[pray] require `{\tt y}' to confirm an attempt to pray rather than immediately praying; on by default; (to require ``yes'' rather than just `y', set Confirm too); -\item[{\tt trap~~~}] +\item[trap] require `{\tt y}' to confirm an attempt to move into or onto a known trap, unless doing so is considered to be harmless; when enabled, this confirmation is also used for moving into visible gas cloud regions; (to require ``yes'' rather than just `y', set Confirm too); confirmation can be skipped by using the `{\tt m}' movement prefix; -\item[{\tt swim~~~}] +\item[swim] prevent walking into water or lava; on by default; (to deliberately step onto/into such terrain when this is set, use the `{\tt m}' movement prefix when adjacent); -\item[{\tt AutoAll}] +\item[AutoAll] require confirmation when the `A' (Autoselect-All) choice is selected in object class filtering menus for {\it menustyle:Full}; (to require ``yes'' rather than just `y', set Confirm too); -\item[{\tt Remove~}] +\item[Remove] require selection from inventory for `{\tt R}' and `{\tt T}' commands even when wearing just one applicable item; -\item[{\tt all~~~~}] +\item[all] turn on all of the above. -\elist +\end{description} %.ei %.ed By default, the pray, swim, and trap choices are enabled, the others disabled. @@ -4835,10 +4830,10 @@ and entries to be removed by `{\tt !}' and name. The positive (no `!') and negative (with `!') entries can be intermixed. %.lp -\item[\ib{pauper}] +\item[pauper] Start the character with no possessions (default false). Persistent. %.lp -\item[\ib{perm\textunderscore invent}] +\item[perm\textunderscore invent] If true, always display your current inventory in a window (default is false). %.lp "" \\ @@ -4851,7 +4846,7 @@ for {\it perm\textunderscore invent\/}. Setting that to a value other than {\it none\/} while {\it perm\textunderscore invent\/} is false will change it to true. %.lp -\item[\ib{perminv\textunderscore mode}] +\item[perminv\textunderscore mode] Augments the {\tt perm\textunderscore invent} option. @@ -4859,7 +4854,7 @@ Value is one of %.PS "\f(CRin-use\fP" \settowidth{\pcwidth}{\tt in-use} %reuse the paranoid_confirm width \addtolength{\pcwidth}{\labelsep} -\blist{\leftmargin \pcwidth \topsep 1mm \itemsep 0mm} +\begin{description}[leftmargin=\pcwidth, topsep=1mm, itemsep=0mm, labelwidth=*, font=\mdseries, align=right] %.PL \item[{\tt none}] behave as if {\it perm\textunderscore invent\/} is false; @@ -4870,7 +4865,7 @@ show full inventory including gold; \item[{in-use}] only show items which are in use (worn, wielded, lit lamp). %.PE -\elist +\end{description} Default is {\it none\/} but if {\it perm\textunderscore invent\/} gets set to true while it is {\it none\/} it will be changed to {\it all\/}. %.lp "" @@ -4879,7 +4874,7 @@ Note: if gold has been equipped in quiver/ammo-pouch then it will be included for {\it all\/} despite that mode normally omitting gold. %.lp %.\" petattr is a wincap option but we'll document it here... -\item[\ib{petattr}] +\item[petattr] Specifies one or more text highlighting attributes to use when showing pets on the map. Effectively a superset of the {\it hilite\textunderscore pet\/} boolean option. @@ -4889,7 +4884,7 @@ Some of those choices might not work, depending upon terminal hardware or terminal emulation software. %.lp -\item[\ib{pettype}] +\item[pettype] Specify the type of your initial pet, if you are playing a character class that uses multiple types of pets; or choose to have no initial pet at all. Possible values are ``{\tt cat}'', ``{\tt dog}'', ``{\tt horse}'' @@ -4899,13 +4894,13 @@ it will be silently ignored. For example, ``{\tt horse}'' will only be honored when playing a knight. Cannot be set with the `{\tt O}' command. %.lp -\item[\ib{pickup\textunderscore burden}] +\item[pickup\textunderscore burden] When you pick up an item that would exceed this encumbrance level (Unencumbered, Burdened, streSsed, straiNed, overTaxed, or overLoaded), you will be asked if you want to continue. (Default `S'). Persistent. %.lp -\item[\ib{pickup\textunderscore stolen}] +\item[pickup\textunderscore stolen] If this option is on and ``{\it autopickup\/}'' is also on, try to pick up things that a monster stole from you, even if they aren't in ``{\it pickup\textunderscore types\/}'' or @@ -4913,7 +4908,7 @@ match an autopickup exception. Default is on. Persistent. %.lp -\item[\ib{pickup\textunderscore thrown}] +\item[pickup\textunderscore thrown] If this option is on and ``{\it autopickup\/}'' is also on, try to pick up things that you threw, even if they aren't in ``{\it pickup\textunderscore types\/}'' or @@ -4921,7 +4916,7 @@ match an autopickup exception. Default is on. Persistent. %.lp -\item[\ib{pickup\textunderscore types}] +\item[pickup\textunderscore types] Specify the object types to be picked up when ``{\it autopickup\/}'' is on. Default is all types. @@ -4944,7 +4939,7 @@ up, you can set {\it pickup\textunderscore types\/} to `{\tt .}'. That is the type symbol for {\it venom\/} and you won't come across any venom items so won't unintentionally pick such up. %.lp -\item[\ib{pile\textunderscore limit}] +\item[pile\textunderscore limit] When walking across a pile of objects on the floor, threshold at which the message ``there are few/several/many objects here'' is given instead of showing a popup list of those objects. A value of 0 means ``no limit'' @@ -4952,7 +4947,7 @@ of showing a popup list of those objects. A value of 0 means ``no limit'' the objects'' since the pile size will always be at least that big; default value is 5. Persistent. %.lp -\item[\ib{playmode}] +\item[playmode] Values are {\it normal\/}, {\it explore\/}, or {\it debug\/}. Allows selection of explore mode (also known as discovery mode) or debug mode (also known as wizard mode) instead of normal play. @@ -4962,16 +4957,16 @@ name (on single-user systems) or it might be disabled entirely. Requesting it when not allowed or not possible results in explore mode instead. Default is normal play. %.lp -\item[\ib{pushweapon}] +\item[pushweapon] Using the `{\tt w}' (wield) command when already wielding something pushes the old item into your alternate weapon slot (default off). Likewise for the `{\tt a}' (apply) command if it causes the applied item to become wielded. Persistent. %.lp -\item[\ib{query\textunderscore menu}] +\item[query\textunderscore menu] Use a menu when asked specific yes/no queries, instead of a prompt. %.lp -\item[\ib{quick\textunderscore farsight}] +\item[quick\textunderscore farsight] When set, usually prevents the ``you sense your surroundings'' message where play pauses to allow you to browse the map whenever clairvoyance randomly activates. @@ -4980,7 +4975,7 @@ It does not affect the clairvoyance spell where pausing to examine revealed objects or monsters is less intrusive. Default is off. Persistent. %.lp -\item[\ib{race}] +\item[race] Choices are {\tt human}, {\tt dwarf}, {\tt elf}, {\tt gnome}, and {\tt orc} but most roles restrict which of the non-human races are allowed. See {\it role\/} @@ -4992,11 +4987,11 @@ player will be prompted unless role forces a choice for race. unless role forces a choice for race. Cannot be set with the `{\tt O}' command. Persistent. %.lp -\item[\ib{rest\textunderscore on\textunderscore space}] +\item[rest\textunderscore on\textunderscore space] Make the space bar a synonym for the `{\tt .}' (\#wait) command (default off). Persistent. %.lp -\item[\ib{role}] +\item[role] Pick your type of character (for example, ``{\tt role:Samurai}''); synonym for ``{\it character\/}''. See ``{\it name\/}'' for an alternate method of specifying your role. @@ -5028,17 +5023,17 @@ If {\tt role} is not specified, there is no default value; player will be prompted. Cannot be set with the `{\tt O}' command. Persistent. %.lp -\item[\ib{roguesymset}] +\item[roguesymset] This option may be used to select one of the named symbol sets found within {\tt symbols} to alter the symbols displayed on the screen on the rogue level. %.lp -\item[\ib{rlecomp}] +\item[rlecomp] When writing out a save file, perform run length compression of the map. Not all ports support run length compression. It has no effect on reading an existing save file. %.lp -\item[\ib{runmode}] +\item[runmode] Controls the amount of screen updating for the map window when engaged in multi-turn movement (running via {\tt shift}+direction or {\tt control}+direction @@ -5059,43 +5054,43 @@ results of moving. The default is {\it run\/}; versions prior to 3.4.1 used {\it teleport\/} only. Whether or not the effect is noticeable will depend upon the window port used or on the type of terminal. Persistent. %.lp -\item[\ib{safe\textunderscore pet}] +\item[safe\textunderscore pet] Prevent you from (knowingly) attacking your pets (default on). Persistent. %.lp -\item[\ib{safe\textunderscore wait}] +\item[safe\textunderscore wait] Prevents you from waiting or searching when next to a hostile monster (default on). Persistent. %.lp -\item[\ib{sanity\textunderscore check}] +\item[sanity\textunderscore check] Evaluate monsters, objects, and map prior to each turn (default off). Debug mode only. %.lp -\item[\ib{scores}] +\item[scores] Control what parts of the score list you are shown at the end (for example, ``{\tt scores:5top scores/4around my score/own scores}''). Only the first letter of each category (`{\tt t}', `{\tt a}' or `{\tt o}') is necessary. Persistent. %.lp -\item[\ib{showdamage}] +\item[showdamage] Whenever your character takes damage, show a message of the damage taken, and the amount of hit points left. %.lp -\item[\ib{showexp}] +\item[showexp] Show your accumulated experience points on bottom line (default off). Persistent. %.lp -\item[\ib{showrace}] +\item[showrace] Display yourself as the glyph for your race, rather than the glyph for your role (default off). Note that this setting affects only the appearance of the display, not the way the game treats you. Persistent. %.lp -\item[\ib{showscore}] +\item[showscore] Show your approximate accumulated score on bottom line (default off). By default, this feature is suppressed when building the program. Persistent. %.lp -\item[\ib{showvers}] +\item[showvers] Include the game's version number on the status lines (default off). Potentially useful if you switch between different versions or variants, or you are making screenshots or streaming video. @@ -5106,10 +5101,10 @@ status information, unless you're using nethack's {\it Qt\/} interface or your terminal emulator window displays fewer than 25 lines. Persistent. %.lp -\item[\ib{silent}] +\item[silent] Suppress terminal beeps (default on). Persistent. %.lp -\item[\ib{sortdiscoveries}] +\item[sortdiscoveries] Controls the sorting behavior for the output of the `{\tt $\backslash$}' and `{\tt \`{}}' commands. Persistent. @@ -5134,7 +5129,7 @@ Can be interactively set via the `{\tt O}' command or via using the `{\tt m}' prefix before the `{\tt $\backslash$}' or `{\tt \`{}}' command. %.lp -\item[\ib{sortloot}] +\item[sortloot] Controls the sorting behavior of pickup lists for inventory and \#loot commands and some others. Persistent. \\ @@ -5149,11 +5144,11 @@ The possible values are: %.ei %.ed %.lp -\item[\ib{sortpack}] +\item[sortpack] Sort the pack contents by type when displaying inventory (default on). Persistent. %.lp -\item[\tb{sortvanquished}] +\item[sortvanquished] Controls the sorting behavior for the output of the {\tt \#vanquished} command and also for the {\tt \#genocided} command. Persistent. @@ -5194,60 +5189,60 @@ Can be interactively set via the `{\tt m O}' command or via using the `{\tt m}' prefix before either the {\tt \#vanquished} command or the {\tt \#genocided} command. %.lp -\item[\ib{sounds}] +\item[sounds] Allow sounds to be emitted from an integrated sound library (default on). %.lp -\item[\ib{sparkle}] +\item[sparkle] Display a sparkly effect when a monster (including yourself) is hit by an attack to which it is resistant (default on). Persistent. %.lp -\item[\ib{spot\textunderscore monsters}] +\item[spot\textunderscore monsters] Show a message when hero notices a monster (default is off). %.lp -\item[\ib{standout}] +\item[standout] Boldface monsters and ``{\tt --More--}'' (default off). Persistent. %.lp -\item[\ib{statushilites}] +\item[statushilites] Controls how many turns status hilite behaviors highlight the field. If negated or set to zero, disables status hiliting. See ``{\it Configuring Status Hilites\/}'' for further information. %.lp -\item[\ib{status\textunderscore updates}] +\item[status\textunderscore updates] Allow updates to the status lines at the bottom of the screen (default true). %.lp -\item[\ib{suppress\textunderscore alert}] +\item[suppress\textunderscore alert] This option may be set to a {\it NetHack\/} version level to suppress alert notification messages about feature changes for that and prior versions (for example, ``{\tt suppress\textunderscore alert:3.3.1}'') %.lp -\item[\ib{symset}] +\item[symset] This option may be used to select one of the named symbol sets found within {\tt symbols} to alter the symbols displayed on the screen. Use ``{\tt symset:default}'' to explicitly select the default symbols. %.lp -\item[\ib{time}] +\item[time] Show the elapsed game time in turns on bottom line (default off). Persistent. %.lp -\item[\ib{timed\textunderscore delay}] +\item[timed\textunderscore delay] When pausing momentarily for display effect, such as with explosions and moving objects, use a timer rather than sending extra characters to the screen. (Applies to ``tty'' and ``curses'' interfaces only; ``X11'' interface always uses a timer-based delay. The default is on if configured into the program.) Persistent. %.lp -\item[\ib{tips}] +\item[tips] Show some helpful tips during gameplay (default on). Persistent. %.lp -\item[\ib{tombstone}] +\item[tombstone] Draw a tombstone graphic upon your death (default on). Persistent. %.lp -\item[\ib{toptenwin}] +\item[toptenwin] Put the ending display in a {\it NetHack\/} window instead of on stdout (default off). Setting this option makes the score list visible when a windowing version of {\it NetHack\/} is started without a parent window, but it no longer leaves the score list around after game end on a terminal or emulating window. %.lp -\item[\ib{travel}] +\item[travel] Allow the travel command via mouse click (default on). Turning this option off will prevent the game from attempting unintended moves if you make inadvertent mouse clicks on the map window. @@ -5258,14 +5253,14 @@ command. Persistent. % Display intended path during each step of travel (default off). % Debug mode only. %.lp -\item[\ib{tutorial}] +\item[tutorial] Play a tutorial level at the start of the game. Setting this option on or off in the config file will skip the query. %.lp -\item[\ib{verbose}] +\item[verbose] Provide more commentary during the game (default on). Persistent. %.lp -\item[\ib{whatis\textunderscore coord}] +\item[whatis\textunderscore coord] When using the `{\tt /}' or `{\tt ;}' commands to look around on the map with ``{\tt autodescribe}'' on, display coordinates after the description. @@ -5292,7 +5287,7 @@ the `{\tt /m}', `{\tt /M}', `{\tt /o}', and `{\tt /O}' sub-commands of `{\tt /}', where the `{\it none\/}' setting is overridden with `{\it map}'. %.lp -\item[\ib{whatis\textunderscore filter}] +\item[whatis\textunderscore filter] When getting a location on the map, and using the keys to cycle through next and previous targets, allows filtering the possible targets. (default none)\\ @@ -5315,18 +5310,18 @@ the door you were last moving towards.\\ Filtering can also be changed when getting a location with the ``getpos.filter'' key. %.lp -\item[\ib{whatis\textunderscore menu}] +\item[whatis\textunderscore menu] When getting a location on the map, and using a key to cycle through next and previous targets, use a menu instead to pick a target. (default off) %.lp -\item[\ib{whatis\textunderscore moveskip}] +\item[whatis\textunderscore moveskip] When getting a location on the map, and using shifted movement keys or meta-digit keys to fast-move, instead of moving 8 units at a time, move by skipping the same glyphs. (default off) %.lp -\item[\ib{windowtype}] +\item[windowtype] When the program has been built to support multiple interfaces, select whichone to use, such as ``{\tt tty}'' or ``{\tt X11}'' (default depends on build-time settings; use ``{\tt \#version}'' to check). @@ -5340,15 +5335,15 @@ non-comment line. For a comma-separated list in NETHACKOPTIONS or an OPTIONS line in a configuration file, that would be the {\it rightmost\/} option in the list. %.lp -\item[\ib{wizweight}] +\item[wizweight] Augment object descriptions with their objects' weight (default off). Debug mode only. %.lp -\item[\ib{zerocomp}] +\item[zerocomp] When writing out a save file, perform zero-comp compression of the contents. Not all ports support zero-comp compression. It has no effect on reading an existing save file. -\elist +\end{description} %.hn 2 \subsection*{Window Port Customization options} @@ -5368,15 +5363,15 @@ using by checking to see if it shows up in the Options list. Some options are dynamic and can be specified during the game with the `{\tt O}' command. -\blist{} +\begin{description}[font=\itshape] %.lp -\item[\ib{align\textunderscore message}] +\item[align\textunderscore message] Where to align or place the message window (top, bottom, left, or right) %.lp -\item[\ib{align\textunderscore status}] +\item[align\textunderscore status] Where to align or place the status window (top, bottom, left, or right). %.lp -\item[\ib{ascii\textunderscore map}] +\item[ascii\textunderscore map] %.hw DECgraphics IBMgraphics \% don't hyphenate these \hyphenation{DECgraphics IBMgraphics} If {\it NetHack\/} can, it should display the map using simple @@ -5388,94 +5383,94 @@ or {\it IBMgraphics\/} if your display supports them. Setting {\tt ascii\textunderscore map} to {\it True\/} forces {\tt tiled\textunderscore map} to be {\it False}. %.lp -\item[\ib{color}] +\item[color] If {\it NetHack\/} can, it should display color for different monsters, objects, and dungeon features (default on). %.lp -\item[\ib{eight\textunderscore bit\textunderscore tty}] +\item[eight\textunderscore bit\textunderscore tty] If {\it NetHack\/} can, it should pass eight-bit character values (for example, specified with the {\it traps \/} option) straight through to your terminal (default off). %.lp -\item[\ib{font\textunderscore map}] +\item[font\textunderscore map] If {\it NetHack\/} can, it should use a font by the chosen name for the map window. %.lp -\item[\ib{font\textunderscore menu}] +\item[font\textunderscore menu] If {\it NetHack\/} can, it should use a font by the chosen name for menu windows. %.lp -\item[\ib{font\textunderscore message}] +\item[font\textunderscore message] If {\it NetHack\/} can, it should use a font by the chosen name for the message window. %.lp -\item[\ib{font\textunderscore status}] +\item[font\textunderscore status] If {\it NetHack\/} can, it should use a font by the chosen name for the status window. %.lp -\item[\ib{font\textunderscore text}] +\item[font\textunderscore text] If {\it NetHack\/} can, it should use a font by the chosen name for text windows. %.lp -\item[\ib{font\textunderscore size\textunderscore map}] +\item[font\textunderscore size\textunderscore map] If {\it NetHack\/} can, it should use this size font for the map window. %.lp -\item[\ib{font\textunderscore size\textunderscore menu}] +\item[font\textunderscore size\textunderscore menu] If {\it NetHack\/} can, it should use this size font for menu windows. %.lp -\item[\ib{font\textunderscore size\textunderscore message}] +\item[font\textunderscore size\textunderscore message] If {\it NetHack\/} can, it should use this size font for the message window. %.lp -\item[\ib{font\textunderscore size\textunderscore status}] +\item[font\textunderscore size\textunderscore status] If {\it NetHack\/} can, it should use this size font for the status window. %.lp -\item[\ib{font\textunderscore size\textunderscore text}] +\item[font\textunderscore size\textunderscore text] If {\it NetHack\/} can, it should use this size font for text windows. %.lp -\item[\ib{fullscreen}] +\item[fullscreen] If {\it NetHack\/} can, it should try to display on the entire screen rather than in a window. %.lp -\item[\ib{guicolor}] +\item[guicolor] Use color text and/or highlighting attributes when displaying some non-map data (such as menu selector letters). Curses interface only; default is on. %.lp -\item[\ib{large\textunderscore font}] +\item[large\textunderscore font] If {\it NetHack\/} can, it should use a large font. %.lp -\item[\ib{map\textunderscore mode}] +\item[map\textunderscore mode] If {\it NetHack\/} can, it should display the map in the manner specified. %.lp -\item[\ib{player\textunderscore selection}] +\item[player\textunderscore selection] If {\it NetHack\/} can, it should pop up dialog boxes or use prompts for character selection. %.lp -\item[\ib{popup\textunderscore dialog}] +\item[popup\textunderscore dialog] If {\it NetHack\/} can, it should pop up dialog boxes for input. %.lp -\item[\ib{preload\textunderscore tiles}] +\item[preload\textunderscore tiles] If {\it NetHack\/} can, it should preload tiles into memory. For example, in the protected mode MS-DOS version, control whether tiles get pre-loaded into RAM at the start of the game. Doing so enhances performance of the tile graphics, but uses more memory. (default on). Cannot be set with the `{\tt O}' command. %.lp -\item[\ib{scroll\textunderscore amount}] +\item[scroll\textunderscore amount] If {\it NetHack\/} can, it should scroll the display by this number of cells when the hero reaches the scroll\textunderscore margin. %.lp -\item[\ib{scroll\textunderscore margin}] +\item[scroll\textunderscore margin] If {\it NetHack\/} can, it should scroll the display when the hero or cursor is this number of cells away from the edge of the window. %.lp -\item[\ib{selectsaved}] +\item[selectsaved] If {\it NetHack\/} can, it should display a menu of existing saved games for the player to choose from at game startup, if it can. Not all ports support this option. %.lp -\item[\ib{softkeyboard}] +\item[softkeyboard] If {\it NetHack\/} can, it should display an onscreen keyboard. Handhelds are most likely to support this option. %.lp -\item[\ib{splash\textunderscore screen}] +\item[splash\textunderscore screen] If {\it NetHack\/} can, it should display an opening splash screen when it starts up (default yes). %.lp -\item[\ib{statuslines}] +\item[statuslines] Number of lines for traditional below-the-map status display. Acceptable values are {\tt 2} and {\tt 3} (default is {\tt 2}). @@ -5509,16 +5504,16 @@ older than {\tt qt-5.9}, can only be set in the run-time configuration file or via NETHACKOPTIONS, not during play with the `{\tt O}' command.) %.lp -\item[\ib{term\textunderscore cols} {\normalfont and}] +\item[term\textunderscore cols \textrm{and}] %.lp -\item[\ib{term\textunderscore rows}] +\item[term\textunderscore rows] Curses interface only. Number of columns and rows to use for the display. Curses will attempt to resize to the values specified but will settle for smaller sizes if they are too big. Default is the current window size. %.lp -\item[\ib{tile\textunderscore file}] +\item[tile\textunderscore file] Specify the name of an alternative tile file to override the default. \\ %.lp "" @@ -5526,34 +5521,34 @@ Note: the X11 interface uses X resources rather than NetHack's options to select an alternate tile file. See {\tt NetHack.ad}, the sample X ``application defaults'' file. %.lp -\item[\ib{tile\textunderscore height}] +\item[tile\textunderscore height] Specify the preferred height of each tile in a tile capable port. %.lp -\item[\ib{tile\textunderscore width}] +\item[tile\textunderscore width] Specify the preferred width of each tile in a tile capable port %.lp -\item[\ib{tiled\textunderscore map}] +\item[tiled\textunderscore map] If {\it NetHack\/} can, it should display the map using {\it tiles} graphics rather than simple characters (letters and punctuation, possibly augmented by line-drawing symbols). Setting {\tt tiled\textunderscore map} to {\it True\/} forces {\tt ascii\textunderscore map} to be {\it False}. %.lp -\item[\ib{use\textunderscore darkgray}] +\item[use\textunderscore darkgray] Use bold black instead of blue for black glyphs (TTY only). %.lp -\item[\ib{use\textunderscore inverse}] +\item[use\textunderscore inverse] If {\it NetHack\/} can, it should display inverse when the game specifies it. %.lp -\item[\ib{use\textunderscore menu\textunderscore glyphs}] +\item[use\textunderscore menu\textunderscore glyphs] If {\it NetHack\/} can, it should display glyphs next to objects in the inventory. %.lp -\item[\ib{vary\textunderscore msgcount}] +\item[vary\textunderscore msgcount] If {\it NetHack\/} can, it should display this number of messages at a time in the message window. %.lp -\item[\ib{windowborders}] +\item[windowborders] Whether to draw boxes around the map, status area, message area, and persistent inventory window if enabled. Curses interface only. @@ -5585,7 +5580,7 @@ setting the value to 3 or 4 instead will keep borders for the map, message, and status windows but have room for two additional lines of inventory plus widen each inventory line by two columns. %.lp -\item[\ib{windowcolors}] +\item[windowcolors] If {\it NetHack\/} can, it should display all windows of a particular style with the specified foreground and background colors. Windows GUI and curses windowport only. @@ -5610,10 +5605,10 @@ or (for Windows only) one of Windows UI colors ({\it trueblack}, {\it windowtext}). %.lp -\item[\ib{wraptext}] +\item[wraptext] If {\it NetHack\/} can, it should wrap long lines of text if they don't fit in the visible area of the window. -\elist +\end{description} %.hn 2 \subsection*{Crash Report Options} @@ -5622,21 +5617,21 @@ in the visible area of the window. Please note that NetHack does not send {\textbf any} information off your computer unless you manually click submit on a form. %.si -\blist{} +\begin{description} %.lp \item[OPTION=crash\textunderscore email:{\it email\textunderscore address}] %.lp \item[OPTION=crash\textunderscore name:{\it your\textunderscore name}] %.ei -\elist +\end{description} These options are used only to save you some typing on the crash report and \#bugreport forms. %.si -\blist{} +\begin{description} %.lp \item[OPTION=crash\textunderscore urlmax:{\it bytes}] %.ei -\elist +\end{description} This option is used to limit the length of the URLs generated and is only needed if your browser cannot handle arbitrarily long URLs. @@ -5647,16 +5642,16 @@ needed if your browser cannot handle arbitrarily long URLs. Here are explanations of options that are used by specific platforms or ports to customize and change the port behavior. -\blist{} +\begin{description}[font=\itshape] %.lp -\item[\ib{altkeyhandling}] +\item[altkeyhandling] Select an alternate way to handle keystrokes ({\it Win32 tty\/ NetHack\/} only). The name of the handling type is one of {\it default}, {\it ray}, {\it 340} -%.\" \item[\ib{altmeta}] +%.\" \item[altmeta] %.\" On Amiga, this option controls whether typing ``Alt'' plus another key %.\" functions as a meta-shift for that key (default on). %.lp -\item[\ib{altmeta}] +\item[altmeta] %.\" On other (non-Amiga) systems where this option is available, it can be On systems where this option is available, it can be set to tell {\it NetHack\/} to convert a two character sequence beginning with @@ -5673,19 +5668,19 @@ character to complete the two character sequence. Type a second ESC to finish cancelling such a count. At other prompts a single ESC suffices. %.lp -\item[\ib{BIOS}] +\item[BIOS] Use BIOS calls to update the screen display quickly and to read the keyboard (allowing the use of arrow keys to move) on machines with an IBM PC compatible BIOS ROM (default off, {\it OS/2, PC\/ {\rm and} ST NetHack\/} only). %.lp -\item[\ib{rawio}] +\item[rawio] Force raw (non-cbreak) mode for faster output and more bulletproof input (MS-DOS sometimes treats `{\tt \^{}P}' as a printer toggle without it) (default off, {\it OS/2, PC\/ {\rm and} ST NetHack\/} only). Note: DEC Rainbows hang if this is turned on. Cannot be set with the `{\tt O}' command. %.lp -\item[\ib{subkeyvalue}] +\item[subkeyvalue] ({\it Win32 tty NetHack \/} only). May be used to alter the value of keystrokes that the operating system returns to {\it NetHack\/} to help compensate for international keyboard @@ -5696,7 +5691,7 @@ You can use multiple subkeyvalue assignments in the configuration file if needed. Cannot be set with the `{\tt O}' command. %.lp -\item[\ib{video}] +\item[video] Set the video mode used ({\it PC\/ NetHack\/} only). Values are {\it autodetect\/}, {\it default\/}, {\it vga\/}, or {\it vesa\/}. Setting {\it vesa\/} will cause the game to display tiles, using the full @@ -5708,13 +5703,13 @@ Setting {\it autodetect\/} attempts {\it vesa\/}, then {\it vga\/}, and finally sets {\it default\/} if neither of those modes works. Cannot be set with the `{\tt O}' command. %.lp -\item[\ib{video\textunderscore height}] +\item[video\textunderscore height] Set the VGA mode resolution height (MS-DOS only, with video:vesa) %.lp -\item[\ib{video\textunderscore width}] +\item[video\textunderscore width] Set the VGA mode resolution width (MS-DOS only, with video:vesa) %.lp -\item[\ib{videocolors}] +\item[videocolors] \begin{sloppypar} Set the color palette for PC systems using NO\textunderscore TERMS (default 4-2-6-1-5-3-15-12-10-14-9-13-11, {\it PC\/ NetHack\/} only). @@ -5724,13 +5719,13 @@ bright.magenta, and bright.cyan. Cannot be set with the `{\tt O}' command. \end{sloppypar} %.lp -\item[\ib{videoshades}] +\item[videoshades] Set the intensity level of the three gray scales available (default dark normal light, {\it PC\/ NetHack\/} only). If the game display is difficult to read, try adjusting these scales; if this does not correct the problem, try {\tt !color}. Cannot be set with the `{\tt O}' command. -\elist +\end{description} %.hn 2 \subsection*{Regular Expressions} @@ -5757,9 +5752,9 @@ By placing ``{\tt autopickup\textunderscore exception}'' lines in your configura file, you can define patterns to be checked when the game is about to autopickup something. -\blist{} +\begin{description} %.lp -\item[\ib{autopickup\textunderscore exception}] +\item[autopickup\textunderscore exception] Sets an exception to the ``{\it pickup\textunderscore types}'' option. The {\it autopickup\textunderscore exception\/} option should be followed by a regular expression to be used as a pattern to match against the singular form of the @@ -5784,7 +5779,7 @@ Exceptions can be set with the `{\tt O}' command, but because they are not included in your configuration file, they won't be in effect if you save and then restore your game. {\it autopickup\textunderscore exception\/} rules are not saved with the game. -\elist +\end{description} %.lp "Here are some examples:" Here are some examples: @@ -5822,35 +5817,35 @@ For example: BIND=v:loot \end{verbatim} -\blist{} +\begin{description}[font=\ttfamily] %.lp "Extended command keys" -\item[\tb{Extended command keys}] +\item[Extended command keys] You can bind multiple keys to the same extended command. Unbind a key by using ``{\tt nothing}'' as the extended command to bind to. You can also bind the ``{\tt }'', ``{\tt }'', and ``{\tt }'' keys. %.lp "Menu accelerator keys" -\item[\tb{Menu accelerator keys}] +\item[Menu accelerator keys] The menu control or accelerator keys can also be rebound via OPTIONS lines in the configuration file. You cannot bind object symbols or selection letters into menu accelerators. Some interfaces only support some of the menu accelerators. %.lp "Mouse buttons" -\item[\tb{Mouse buttons}] +\item[Mouse buttons] You can bind ``mouse1'' or ``mouse2'' to ``{\tt nothing}'', ``{\tt therecmdmenu}'', ``{\tt clicklook}'', or ``{\tt mouseaction}''. %.lp "Special command keys" -\item[\tb{Special command keys}] +\item[Special command keys] Below are the special commands you can rebind. Some of them can be bound to same keys with no problems, others are in the same ``context'', and if bound to same keys, only one of those commands will be available. Special command can only be bound to a single key. -\elist +\end{description} %.pg -\blist{\itemindent 10mm \labelwidth 15mm \rightmargin 15mm} +\begin{description}[itemindent=10mm, labelwidth=15mm, rightmargin=15mm] %.lp \item[{\bb{count}}] Prefix key to start a count, to repeat a command this many times. @@ -5975,7 +5970,7 @@ Default is~`{\tt z}'. \item[{\bb{getpos.valid.prev}}] When asked for a location, the key to go to previous closest valid location. Default is~`{\tt Z}'. -\elist +\end{description} %.hn 2 @@ -5991,9 +5986,9 @@ look like this: \begin{verbatim} MSGTYPE=type "pattern" \end{verbatim} -\blist{} +\begin{description}[font=\itshape] %.lp -\item[\ib{type}] +\item[type] how the message should be shown: %.sd %.si @@ -6006,9 +6001,9 @@ shown in between. %.ei %.ed %.lp -\item[\ib{pattern}] +\item[pattern] the pattern to match. The pattern should be a regular expression. -\elist +\end{description} %.lp "" Here's an example of message types using {\it NetHack's\/} internal @@ -6046,19 +6041,19 @@ look like this: MENUCOLOR="pattern"=color&attribute \end{verbatim} -\blist{} +\begin{description}[font=\itshape] %.lp -\item[\ib{pattern}] +\item[pattern] the pattern to match; %.lp -\item[\ib{color}] +\item[color] the color to use for lines matching the pattern; %.lp -\item[\ib{attribute}] +\item[attribute] the attribute to use for lines matching the pattern. The attribute is optional, and if left out, you must also leave out the preceding ampersand. If no attribute is defined, no attribute is used. -\elist +\end{description} %.lp "" The pattern should be a regular expression. @@ -6117,12 +6112,12 @@ use of user sounds. The following configuration file entries are relevant to mapping user sounds to messages: -\blist{} +\begin{description}[font=\itshape] %.lp -\item[\ib{SOUNDDIR}] +\item[SOUNDDIR] The directory that houses the sound files to be played. %.lp -\item[\ib{SOUND}] +\item[SOUND] An entry that maps a sound file to a user-specified message pattern. Each SOUND entry is broken down into the following parts: @@ -6136,7 +6131,7 @@ Each SOUND entry is broken down into the following parts: {\tt sound index} --- optional; the index corresponding to a sound file. %.ei %.ed -\elist +\end{description} %.lp "" The pattern should be a regular expression. @@ -6248,7 +6243,7 @@ It overrides other behavior rules if hit points are at or below the {\it major problem\/} threshold (which varies depending upon maximum hit points and experience level). -\blist{} +\begin{description} %.lp "*" \item[{\tt always}] will set the default attributes for that field. %.lp "*" @@ -6313,7 +6308,7 @@ and ``{\it title\/}''. For title, only the role's rank title is tested; the character's name is ignored. %.ei -\elist +\end{description} The in-game options menu can help you determine the correct syntax for a configuration file. @@ -6345,17 +6340,17 @@ Example hilites: The options that are used to select a particular symbol set from the symbol file are: -\blist{} +\begin{description}[font=\itshape] %.lp -\item[\ib{symset}] +\item[symset] Set the name of the symbol set that you want to load. {\it symbols\/}. %.lp -\item[\ib{roguesymset}] +\item[roguesymset] Set the name of the symbol set that you want to load for display on the rogue level. -\elist +\end{description} You can also override one or more symbols using the {\it SYMBOLS\/} and {\it ROGUESYMBOLS\/} configuration file options. @@ -6658,65 +6653,65 @@ only parameter. The most crucial settings to make the game more accessible are: %.pg -\blist{} +\begin{description}[font=\itshape] %.lp -\item[\ib{symset:plain}] +\item[symset:plain] Load a symbol set appropriate for use by blind players. %.lp -\item[\ib{menustyle:traditional}] +\item[menustyle:traditional] This will assist in the interface to speech synthesizers. %.lp -\item[\ib{nomenu\textunderscore overlay}] +\item[nomenu\textunderscore overlay] Show menus on a cleared screen and aligned to the left edge. %.lp -\item[\ib{number\textunderscore pad}] +\item[number\textunderscore pad] A lot of speech access programs use the number-pad to review the screen. If this is the case, disable the number\textunderscore pad option and use the traditional Rogue-like commands. %.lp -\item[\ib{paranoid\textunderscore confirmation:swim}] +\item[paranoid\textunderscore confirmation:swim] Prevent walking into water or lava. %.lp -\item[\ib{accessiblemsg}] +\item[accessiblemsg] Adds direction or location information to messages. %.lp -\item[\ib{spot\textunderscore monsters}] +\item[spot\textunderscore monsters] Shows a message when hero notices a monster; combine with accessiblemsg. %.lp -\item[\ib{mon\textunderscore movement}] +\item[mon\textunderscore movement] Shows a message when hero notices a monster movement; combine with spot\textunderscore monsters and accessiblemsg. %.lp -\item[\ib{autodescribe}] +\item[autodescribe] Automatically describe the terrain under the cursor when targeting. %.lp -\item[\ib{mention\textunderscore map}] +\item[mention\textunderscore map] Give feedback messages when interesting map locations change. %.lp -\item[\ib{mention\textunderscore walls}] +\item[mention\textunderscore walls] Give feedback messages when walking towards a wall or when travel command was interrupted. %.lp -\item[\ib{whatis\textunderscore coord:compass}] +\item[whatis\textunderscore coord:compass] When targeting with cursor, describe the cursor position with coordinates relative to your character. %.lp -\item[\ib{whatis\textunderscore filter:area}] +\item[whatis\textunderscore filter:area] When targeting with cursor, filter possible locations so only those in the same area (eg. same room, or same corridor) are considered. %.lp -\item[\ib{whatis\textunderscore moveskip}] +\item[whatis\textunderscore moveskip] When targeting with cursor and using fast-move, skip the same glyphs instead of moving 8 units at a time. %.lp -\item[\ib{nostatus\textunderscore updates}] +\item[nostatus\textunderscore updates] Prevent updates to the status lines at the bottom of the screen, if your screen-reader reads those lines. The same information can be seen via the {\tt \#attributes} command. %.lp -\item[\ib{showdamage}] +\item[showdamage] Give a message of damage taken and how many hit points are left. -\elist +\end{description} %.hn2 \subsection*{Global Configuration for System Administrators} @@ -6733,87 +6728,87 @@ set uses a compiled-in default (which may not be appropriate for your system). %.pg -\blist{} +\begin{description}[font=\itshape] %.lp -\item[\ib{WIZARDS}] +\item[WIZARDS] A space-separated list of user name who are allowed to play in debug mode (commonly referred to as wizard mode). A value of a single asterisk (*) allows anyone to start a game in debug mode. %.lp -\item[\ib{SHELLERS}] +\item[SHELLERS] A list of users who are allowed to use the shell escape command (`{\tt !}'). The syntax is the same as WIZARDS. %.lp -\item[\ib{EXPLORERS}] +\item[EXPLORERS] A list of users who are allowed to use the explore mode. The syntax is the same as WIZARDS. %.lp -\item[\ib{MSGHANDLER}] +\item[MSGHANDLER] A path and filename of executable. Whenever a message-window message is shown, NetHack runs this program. The program will get the message as the only parameter. %.lp -\item[\ib{MAXPLAYERS}] +\item[MAXPLAYERS] Limit the maximum number of games that can be running at the same time. %.lp -\item[\ib{SUPPORT}] +\item[SUPPORT] A string explaining how to get local support (no default value). %.lp -\item[\ib{RECOVER}] +\item[RECOVER] A string explaining how to recover a game on this system (no default value). %.lp -\item[\ib{SEDUCE}] +\item[SEDUCE] 0 or 1 to disable or enable, respectively, the SEDUCE option. When disabled, incubi and succubi behave like nymphs. %.lp -\item[\ib{CHECK\textunderscore PLNAME}] +\item[CHECK\textunderscore PLNAME] Setting this to 1 will make the EXPLORERS, WIZARDS, and SHELLERS check for the player name instead of the user's login name. %.lp -\item[\ib{CHECK\textunderscore SAVE\textunderscore UID}] +\item[CHECK\textunderscore SAVE\textunderscore UID] 0 or 1 to disable or enable, respectively, the UID (used identification number) checking for save files (to verify that the user who is restoring is the same one who saved). -\elist +\end{description} %.pg The following four options affect the score file: -\blist {} +\begin{description}[font=\itshape] %.pg %.lp -\item[\ib{PERSMAX}] +\item[PERSMAX] Maximum number of entries for one person. %.lp -\item[\ib{ENTRYMAX}] +\item[ENTRYMAX] Maximum number of entries in the score file. %.lp -\item[\ib{POINTSMIN}] +\item[POINTSMIN] Minimum number of points to get an entry in the score file. %.lp -\item[\ib{PERS\textunderscore IS\textunderscore UID}] +\item[PERS\textunderscore IS\textunderscore UID] 0 or 1 to use user names or numeric userids, respectively, to identify unique people for the score file. %.lp -\item[\ib{HIDEUSAGE}] +\item[HIDEUSAGE] 0 or 1 to control whether the help menu entry for command line usage is shown or suppressed. %.lp -\item[\ib{MAX\textunderscore STATUENAME\textunderscore RANK}] +\item[MAX\textunderscore STATUENAME\textunderscore RANK] Maximum number of score file entries to use for random statue names (default is 10). %.lp -\item[\ib{ACCESSIBILITY}] +\item[ACCESSIBILITY] 0 or 1 to disable or enable, respectively, the ability for players to set S\textunderscore pet\textunderscore override and S\textunderscore hero\textunderscore override symbols in their configuration file. %.lp -\item[\ib{PORTABLE\textunderscore DEVICE\textunderscore PATHS}] +\item[PORTABLE\textunderscore DEVICE\textunderscore PATHS] 0 or 1 Windows OS only, the game will look for all of its external files, and write to all of its output files in one place rather than at the standard locations. %.lp -\item[\ib{DUMPLOGFILE}] +\item[DUMPLOGFILE] A filename where the end-of-game dumplog is saved. Not defining this will prevent dumplog from being created. Only available if your game is compiled with DUMPLOG. @@ -6829,7 +6824,7 @@ Allows the following placeholders: {\tt \%n} --- player name\\ {\tt \%N} --- first character of player name %.lp -\item[\ib{LIVELOG}] +\item[LIVELOG] A bit-mask of types of events that should be written to the {\it livelog\/} file if one is present. The sample {\it sysconf\/} file accompanying the program contains a @@ -6842,14 +6837,14 @@ When available, it should be left commented out on single player installations because over time the file could grow to be extremely large unless it is actively maintained. %.lp -\item[\ib{CRASHREPORTURL}] +\item[CRASHREPORTURL] If set to {\tt https://www.nethack.org/links/cr-37BETA.html} and support is compiled in, brings up a browser window populated with the information needed to report a problem if the game panics or ends up in an internally inconsistent state, or if the \#bugreport command is invoked. -\elist +\end{description} %.hn 1 \section{Scoring} From 465a9b1fbca4c35488f9395d6d263bef7f72197a Mon Sep 17 00:00:00 2001 From: danielclow <106956386+danielclow@users.noreply.github.com> Date: Sat, 3 Jan 2026 23:52:28 +0800 Subject: [PATCH 169/442] Remove unneeded aliases After changing the list styles in the previous commit, it is no longer necessary to define aliases for these styles. --- doc/Guidebook.tex | 128 ++++++++++++++++++++++------------------------ 1 file changed, 61 insertions(+), 67 deletions(-) diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 89bfaed00..2b8111dac 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -7,12 +7,6 @@ \setlist[description]{leftmargin=30mm, topsep=2mm, partopsep=0mm, parsep=0mm, itemsep=1mm, labelwidth=28mm, labelsep=2mm} -\newcommand{\nd}{\noindent} - -\newcommand{\tb}[1]{\tt #1 \hfill} -\newcommand{\bb}[1]{\bf #1 \hfill} -\newcommand{\ib}[1]{\it #1 \hfill} - \hyphenation{CRASHREPORTURL} \begin{document} @@ -77,7 +71,7 @@ at the local inn, becoming more and more depressed as you watch the odds of your success being posted on the inn's walls getting lower and lower. %.pg -\nd In the morning you awake, collect your belongings, and +\noindent In the morning you awake, collect your belongings, and set off for the dungeon. After several days of uneventful travel, you see the ancient ruins that mark the entrance to the Mazes of Menace. It is late at night, so you make camp at the entrance @@ -2034,7 +2028,7 @@ Help menu: get the list of available extended commands. \end{description} %.pg -\nd If your keyboard has a meta key (which, when pressed in combination +\noindent If your keyboard has a meta key (which, when pressed in combination with another key, modifies it by setting the `meta' [8th, or `high'] bit), you can invoke many extended commands by meta-ing the first letter of the command. @@ -2146,7 +2140,7 @@ equivalent is used for another command, so the three key combination \end{description} %.pg -\nd If the {\it number\textunderscore pad\/} option is on, some additional letter commands +\noindent If the {\it number\textunderscore pad\/} option is on, some additional letter commands are available: \begin{description}[font=\ttfamily] %.lp @@ -3056,7 +3050,7 @@ leather jacket & 9 & & no armor & 10\\ \end{center} %.pg -\nd You can also wear other pieces of armor (cloak over suit, shirt under +\noindent You can also wear other pieces of armor (cloak over suit, shirt under suit, helmet, gloves, boots, shield) to lower your armor class even further. %--too obvious to mention unless we include polymorph into ettin or maralith @@ -4069,7 +4063,7 @@ you would enter the command \end{verbatim} %.ED -\nd in {\it csh} +\noindent in {\it csh} (note the need to escape the `!' since it's special to that shell), or the pair of commands %.SD i @@ -4079,7 +4073,7 @@ to that shell), or the pair of commands \end{verbatim} %.ED -\nd in {\it sh}, {\it ksh}, or {\it bash}. +\noindent in {\it sh}, {\it ksh}, or {\it bash}. %.pg The NETHACKOPTIONS value is effectively the same as a single OPTIONS @@ -5847,14 +5841,14 @@ can only be bound to a single key. %.pg \begin{description}[itemindent=10mm, labelwidth=15mm, rightmargin=15mm] %.lp -\item[{\bb{count}}] +\item[count] Prefix key to start a count, to repeat a command this many times. With {\it number\textunderscore pad\/} only. Default is~`{\tt n}'. %.lp -\item[{\bb{getdir.help}}] +\item[getdir.help] When asked for a direction, the key to show the help. Default is~`{\tt ?}'. %.lp -\item[{\bb{getdir.mouse}}] +\item[getdir.mouse] When asked for a direction, the key to initiate a simulated mouse click. You will be asked to pick a location. Use movement keystrokes to move the cursor around the map, then type @@ -5864,110 +5858,110 @@ to finish as if performing a left or right click. Only useful when using the {\tt \#therecmdmenu} command. Default is~`{\tt \textunderscore }'. %.lp -\item[{\bb{getdir.self}}] +\item[getdir.self] When asked for a direction, the key to target yourself. Default is~`{\tt .}'. %.lp -\item[{\bb{getdir.self2}}] +\item[getdir.self2] When asked for a direction, an alternate key to target yourself. Default is~`{\tt s}'. %.lp -\item[{\bb{getpos.autodescribe}}] +\item[getpos.autodescribe] When asked for a location, the key to toggle {\it autodescribe\/}. Default is~`{\tt \#}'. %.lp -\item[{\bb{getpos.all.next}}] +\item[getpos.all.next] When asked for a location, the key to go to next closest interesting thing. Default is~`{\tt a}'. %.lp -\item[{\bb{getpos.all.prev}}] +\item[getpos.all.prev] When asked for a location, the key to go to previous closest interesting thing. Default is~`{\tt A}'. %.lp -\item[{\bb{getpos.door.next}}] +\item[getpos.door.next] When asked for a location, the key to go to next closest door or doorway. Default is~`{\tt d}'. %.lp -\item[{\bb{getpos.door.prev}}] +\item[getpos.door.prev] When asked for a location, the key to go to previous closest door or doorway. Default is~`{\tt D}'. %.lp -\item[{\bb{getpos.help}}] +\item[getpos.help] When asked for a location, the key to show help. Default is~`{\tt ?}'. %.lp -\item[{\bb{getpos.mon.next}}] +\item[getpos.mon.next] When asked for a location, the key to go to next closest monster. Default is~`{\tt m}'. %.lp -\item[{\bb{getpos.mon.prev}}] +\item[getpos.mon.prev] When asked for a location, the key to go to previous closest monster. Default is~`{\tt M}'. %.lp -\item[{\bb{getpos.obj.next}}] +\item[getpos.obj.next] When asked for a location, the key to go to next closest object. Default is~`{\tt o}'. %.lp -\item[{\bb{getpos.obj.prev}}] +\item[getpos.obj.prev] When asked for a location, the key to go to previous closest object. Default is~`{\tt O}'. %.lp -\item[{\bb{getpos.menu}}] +\item[getpos.menu] When asked for a location, and using one of the next or previous keys to cycle through targets, toggle showing a menu instead. Default is~`{\tt !}'. %.lp -\item[{\bb{getpos.moveskip}}] +\item[getpos.moveskip] When asked for a location, and using the shifted movement keys or meta-digit keys to fast-move around, move by skipping the same glyphs instead of by 8 units. Default is~`{\tt *}'. %.lp -\item[{\bb{getpos.filter}}] +\item[getpos.filter] When asked for a location, change the filtering mode when using one of the next or previous keys to cycle through targets. Toggles between no filtering, in view only, and in the same area only. Default is~`{\tt "}'. %.lp -\item[{\bb{getpos.pick}}] +\item[getpos.pick] When asked for a location, the key to choose the location, and possibly ask for more info. When simulating a mouse click after being asked for a direction (see getdir.mouse above), the key to use to respond as right click. Default is~`{\tt .}'. %.lp -\item[{\bb{getpos.pick.once}}] +\item[getpos.pick.once] When asked for a location, the key to choose the location, and skip asking for more info. When simulating a mouse click after being asked for a direction, the key to respond as left click. Default is~`{\tt ,}'. %.lp -\item[{\bb{getpos.pick.quick}}] +\item[getpos.pick.quick] When asked for a location, the key to choose the location, skip asking for more info, and exit the location asking loop. Default is~`{\tt ;}'. %.lp -\item[{\bb{getpos.pick.verbose}}] +\item[getpos.pick.verbose] When asked for a location, the key to choose the location, and show more info without asking. Default is~`{\tt :}'. %.lp -\item[{\bb{getpos.self}}] +\item[getpos.self] When asked for a location, the key to go to your location. Default is~`{\tt @}'. %.lp -\item[{\bb{getpos.unexplored.next}}] +\item[getpos.unexplored.next] When asked for a location, the key to go to next closest unexplored location. Default is~`{\tt x}'. %.lp -\item[{\bb{getpos.unexplored.prev}}] +\item[getpos.unexplored.prev] When asked for a location, the key to go to previous closest unexplored location. Default is~`{\tt X}'. %.lp -\item[{\bb{getpos.valid}}] +\item[getpos.valid] When asked for a location, the key to go to show valid target locations. Default is~`{\tt \$}'. %.lp -\item[{\bb{getpos.valid.next}}] +\item[getpos.valid.next] When asked for a location, the key to go to next closest valid location. Default is~`{\tt z}'. %.lp -\item[{\bb{getpos.valid.prev}}] +\item[getpos.valid.prev] When asked for a location, the key to go to previous closest valid location. Default is~`{\tt Z}'. \end{description} @@ -6943,12 +6937,12 @@ Main events in the course of the game development are described below: %.pg \bigskip -\nd {\it Jay Fenlason\/} wrote the original {\it Hack}, with help from {\it +\noindent {\it Jay Fenlason\/} wrote the original {\it Hack}, with help from {\it Kenny Woodland}, {\it Mike Thome}, and {\it Jon Payne}. %.pg \medskip -\nd {\it Andries Brouwer\/} did a major re-write while at +\noindent {\it Andries Brouwer\/} did a major re-write while at Stichting Mathematisch Centrum (now Centrum Wiskunde \& Informatica), transforming Hack into a very different game. He published the Hack source code for use on UNIX @@ -6963,7 +6957,7 @@ was created for discussing it. %.pg \medskip -\nd {\it Don G. Kneller\/} ported {\it Hack\/} 1.0.3 to Microsoft C and MS-DOS, +\noindent {\it Don G. Kneller\/} ported {\it Hack\/} 1.0.3 to Microsoft C and MS-DOS, producing {\it PC Hack\/} 1.01e, added support for DEC Rainbow graphics in version 1.03g, and went on to produce at least four more versions (3.0, 3.2, 3.51, and 3.6; @@ -6972,12 +6966,12 @@ note that these are old {\it Hack\/} version numbers, not contemporary %.pg \medskip -\nd {\it R. Black\/} ported {\it PC Hack\/} 3.51 to Lattice C and the Atari +\noindent {\it R. Black\/} ported {\it PC Hack\/} 3.51 to Lattice C and the Atari 520/1040ST, producing {\it ST Hack\/} 1.03. %.pg \medskip -\nd {\it Mike Stephenson\/} merged these various versions back together, +\noindent {\it Mike Stephenson\/} merged these various versions back together, incorporating many of the added features, and produced {\it NetHack\/} version 1.4 in 1987. He then coordinated a cast of thousands in enhancing and debugging @@ -6988,7 +6982,7 @@ via {\it ftp\/} and {\it uucp\/} after expiring from the newsgroup. %.pg \medskip -\nd Later, Mike coordinated a major re-write of the game, heading a team which +\noindent Later, Mike coordinated a major re-write of the game, heading a team which included {\it Ken Arromdee}, {\it Jean-Christophe Collet}, {\it Steve Creps}, {\it Eric Hendrickson}, {\it Izchak Miller}, {\it Eric S. Raymond}, {\it John Rupley}, {\it Mike Threepoint}, and {\it Janet Walz}, to produce @@ -6996,14 +6990,14 @@ Rupley}, {\it Mike Threepoint}, and {\it Janet Walz}, to produce %.pg \medskip -\nd {\it NetHack\/} 3.0 was ported to the Atari by {\it Eric R. Smith}, to OS/2 by +\noindent {\it NetHack\/} 3.0 was ported to the Atari by {\it Eric R. Smith}, to OS/2 by {\it Timo Hakulinen}, and to VMS by {\it David Gentzel}. The three of them and {\it Kevin Darcy\/} later joined the main {\it NetHack Development Team} to produce subsequent revisions of 3.0. %.pg \medskip -\nd {\it Olaf Seibert\/} ported {\it NetHack\/} 2.3 and 3.0 to the Amiga. {\it +\noindent {\it Olaf Seibert\/} ported {\it NetHack\/} 2.3 and 3.0 to the Amiga. {\it Norm Meluch}, {\it Stephen Spackman\/} and {\it Pierre Martineau\/} designed overlay code for {\it PC NetHack\/} 3.0. {\it Johnny Lee\/} ported {\it NetHack\/} 3.0 to the Macintosh. Along with various other Dungeoneers, they @@ -7022,7 +7016,7 @@ the three component numbering scheme began to be used with 3.1.0. %.pg \medskip -\nd Headed by {\it Mike Stephenson\/} and coordinated by {\it Izchak Miller\/} +\noindent Headed by {\it Mike Stephenson\/} and coordinated by {\it Izchak Miller\/} and {\it Janet Walz}, the {\it NetHack Development Team} which now included {\it Ken Arromdee}, {\it David Cohrs}, {\it Jean-Christophe Collet}, {\it Kevin Darcy}, @@ -7038,19 +7032,19 @@ Version 3.1.0 was released in January of 1993. %.pg \medskip -\nd {\it Ken Lorber}, {\it Gregg Wonderly\/} and {\it Greg Olson}, with help +\noindent {\it Ken Lorber}, {\it Gregg Wonderly\/} and {\it Greg Olson}, with help from {\it Richard Addison}, {\it Mike Passaretti}, and {\it Olaf Seibert}, developed {\it NetHack\/} 3.1 for the Amiga. %.pg \medskip -\nd {\it Norm Meluch\/} and {\it Kevin Smolkowski}, with help from +\noindent {\it Norm Meluch\/} and {\it Kevin Smolkowski}, with help from {\it Carl Schelin}, {\it Stephen Spackman}, {\it Steve VanDevender}, and {\it Paul Winner}, ported {\it NetHack\/} 3.1 to the PC. %.pg \medskip -\nd {\it Jon W\{tte} and {\it Hao-yang Wang}, +\noindent {\it Jon W\{tte} and {\it Hao-yang Wang}, with help from {\it Ross Brown}, {\it Mike Engber}, {\it David Hairston}, {\it Michael Hamel}, {\it Jonathan Handler}, {\it Johnny Lee}, {\it Tim Lennan}, {\it Rob Menke}, and {\it Andy Swanson}, @@ -7059,7 +7053,7 @@ Building on their development, {\it Bart House} added a Think C port. %.pg \medskip -\nd {\it Timo Hakulinen\/} ported {\it NetHack\/} 3.1 to OS/2. +\noindent {\it Timo Hakulinen\/} ported {\it NetHack\/} 3.1 to OS/2. {\it Eric Smith\/} ported {\it NetHack\/} 3.1 to the Atari. {\it Pat Rankin}, with help from {\it Joshua Delahunty}, was responsible for the VMS version of {\it NetHack\/} 3.1. @@ -7067,7 +7061,7 @@ was responsible for the VMS version of {\it NetHack\/} 3.1. %.pg \medskip -\nd {\it Dean Luick}, with help from {\it David Cohrs}, developed +\noindent {\it Dean Luick}, with help from {\it David Cohrs}, developed {\it NetHack\/} 3.1 for X11. It drew the map as text rather than graphically but included {\tt nh10.bdf}, an optionally used custom X11 font which has @@ -7079,7 +7073,7 @@ forth, not separate images for beetles and ants or for cloaks and boots). %.pg \medskip -\nd {\it Warwick Allison\/} wrote a graphically displayed version +\noindent {\it Warwick Allison\/} wrote a graphically displayed version of {\it NetHack\/} for the Atari where the tiny pictures were described as ``icons'' and were distinct for specific types of monsters and objects rather than just @@ -7092,7 +7086,7 @@ picked up by various other games. %.pg \medskip -\nd The 3.2 {\it NetHack Development Team}, comprised of {\it Michael Allison}, {\it Ken +\noindent The 3.2 {\it NetHack Development Team}, comprised of {\it Michael Allison}, {\it Ken Arromdee}, {\it David Cohrs}, {\it Jessie Collet}, {\it Steve Creps}, {\it Kevin Darcy}, {\it Timo Hakulinen}, {\it Steve Linhart}, {\it Dean Luick}, {\it Pat Rankin}, {\it Eric Smith}, {\it Mike Stephenson}, {\it Janet Walz}, @@ -7100,7 +7094,7 @@ and {\it Paul Winner}, released version 3.2.0 in April of 1996. %.pg \medskip -\nd Version 3.2 marked the tenth anniversary of the formation of the +\noindent Version 3.2 marked the tenth anniversary of the formation of the development team. In a testament to their dedication to the game, all thirteen members of the original {\it NetHack Development Team} remained on the team at the @@ -7202,22 +7196,22 @@ runs on: %.pg \medskip -\nd{\it Pat Rankin} maintained 3.4 for VMS. +\noindent{\it Pat Rankin} maintained 3.4 for VMS. %.pg \medskip -\nd {\it Michael Allison} maintained {\it NetHack\/} 3.4 for the MS-DOS +\noindent {\it Michael Allison} maintained {\it NetHack\/} 3.4 for the MS-DOS platform. {\it Paul Winner} and {\it Yitzhak Sapir} provided encouragement. %.pg \medskip -\nd {\it Dean Luick}, {\it Mark Modrall}, and {\it Kevin Hugo} maintained and +\noindent {\it Dean Luick}, {\it Mark Modrall}, and {\it Kevin Hugo} maintained and enhanced the Macintosh port of 3.4. %.pg \medskip -\nd {\it Michael Allison}, {\it David Cohrs}, {\it Alex Kompel}, +\noindent {\it Michael Allison}, {\it David Cohrs}, {\it Alex Kompel}, {\it Dion Nicolaas}, and {\it Yitzhak Sapir} maintained and enhanced 3.4 for the Microsoft Windows platform. @@ -7226,7 +7220,7 @@ platform. %.pg \medskip -\nd {\it Ron Van Iwaarden} was the sole maintainer of {\it NetHack\/} for +\noindent {\it Ron Van Iwaarden} was the sole maintainer of {\it NetHack\/} for OS/2 the past several releases. Unfortunately Ron's last OS/2 machine stopped working in early 2006. A great many thanks to Ron for keeping {\it NetHack\/} alive on @@ -7234,13 +7228,13 @@ OS/2 all these years. %.pg \medskip -\nd {\it Janne Salmij\"{a}rvi} and {\it Teemu Suikki} maintained +\noindent {\it Janne Salmij\"{a}rvi} and {\it Teemu Suikki} maintained and enhanced the Amiga port of 3.4 after {\it Janne Salmij\"{a}rvi} resurrected it for 3.3.1. %.pg \medskip -\nd {\it Christian ``Marvin'' Bressler} maintained 3.4 for the Atari after he +\noindent {\it Christian ``Marvin'' Bressler} maintained 3.4 for the Atari after he resurrected it for 3.3.1. %.pg @@ -7379,7 +7373,7 @@ some bug fixes. %.pg \medskip -\nd The official {\it NetHack\/} web site is maintained by {\it Ken Lorber} at +\noindent The official {\it NetHack\/} web site is maintained by {\it Ken Lorber} at {\catcode`\#=11 \special{html:}} https:{\tt /}{\tt /}www.nethack.org{\tt /}. @@ -7390,7 +7384,7 @@ https:{\tt /}{\tt /}www.nethack.org{\tt /}. %.hn 2 \subsection*{Special Thanks} -\nd On behalf of the {\it NetHack\/} community, thank you very much once +\noindent On behalf of the {\it NetHack\/} community, thank you very much once again to {\it M. Drew Streib} and {\it Pasi Kallinen} for providing a public NetHack server at nethack.alt.org. Thanks to {\it Keith Simpson} and {\it Andy Thomson} for hardfought.org. Thanks to all those @@ -7403,7 +7397,7 @@ unnamed dungeoneers who invest their time and effort into annual %.hn 2 \subsection*{Dungeoneers} %.pg -\nd From time to time, some depraved individual out there in netland sends a +\noindent From time to time, some depraved individual out there in netland sends a particularly intriguing modification to help out with the game. The {\it NetHack Development Team} sometimes makes note of the names of the worst of these miscreants in this, the list of Dungeoneers: From d924ec0ae4d469f381e6eb37a938b5faa2e0f448 Mon Sep 17 00:00:00 2001 From: danielclow <106956386+danielclow@users.noreply.github.com> Date: Sun, 4 Jan 2026 01:29:27 +0800 Subject: [PATCH 170/442] Replace deprecated styles bf, it & remove verbatims This replaces deprecatd styles with the modern LaTeX equivalent (it - textit, etc.) and removes some verbatim content that was not needed. More special characters are cleaned up as well. --- doc/Guidebook.tex | 3059 ++++++++++++++++++++++----------------------- 1 file changed, 1526 insertions(+), 1533 deletions(-) diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 2b8111dac..765ae5818 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -1,6 +1,6 @@ % NetHack 3.7 Guidebook.tex $NHDT-Date: 1745139202 2025/04/20 00:53:22 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.579 $ */ \documentclass[titlepage]{article} -\usepackage{hyperref} +\usepackage[hidelinks]{hyperref} \usepackage{longtable} \usepackage{enumitem} \usepackage[a4paper, text={160mm, 220mm}, centering]{geometry} @@ -19,7 +19,7 @@ %.mt \title{\LARGE A Guide to the Mazes of Menace:\\ -\Large Guidebook for {\it NetHack\/}} +\Large Guidebook for \textit{NetHack}} %.au \author{Original version - Eric S. Raymond\\ @@ -83,7 +83,7 @@ dungeon\ldots \section{What is going on here?} %.pg -You have just begun a game of {\it NetHack}. Your goal is to grab as much +You have just begun a game of \textit{NetHack}. Your goal is to grab as much treasure as you can, retrieve the Amulet of Yendor, and escape the Mazes of Menace alive. @@ -106,7 +106,7 @@ They begin their quests with naught but uncommon strength, a trusty hauberk, and a great two-handed sword. %.pg % -\item[Cavemen \textrm{and} Cavewomen] +\item[Cavemen \textrm{\textmd{and}} Cavewomen] start with exceptional strength, but unfortunately, neolithic weapons. %.pg % @@ -131,7 +131,7 @@ disciplines have become capable of fighting as effectively without weapons as with. They wear no armor but make up for it with increased mobility. %.pg % -\item[Priests \textrm{and} Priestesses] +\item[Priests \textrm{\textmd{and}} Priestesses] are clerics militant, crusaders advancing the cause of righteousness with arms, armor, and arts thaumaturgic. Their ability to commune with deities via prayer @@ -153,7 +153,7 @@ to great advantage. \item[Samurai] are the elite warriors of feudal Nippon. They are lightly armored and quick, and wear the % -{\it dai-sho}, two swords of the deadliest +\textit{dai-sho}, two swords of the deadliest keenness. %.pg % @@ -223,28 +223,28 @@ seen on the current dungeon level; as you explore more of the level, it appears on the screen in front of you. %.pg -When {\it NetHack\/}'s ancestor {\it rogue\/} first appeared, its screen +When \textit{NetHack}'s ancestor \textit{rogue} first appeared, its screen orientation was almost unique among computer fantasy games. Since then, screen orientation has become the norm rather than the -exception; {\it NetHack\/} continues this fine tradition. Unlike text +exception; \textit{NetHack} continues this fine tradition. Unlike text adventure games that accept commands in pseudo-English sentences and -explain the results in words, {\it NetHack\/} commands are all one or two +explain the results in words, \textit{NetHack} commands are all one or two keystrokes and the results are displayed graphically on the screen. A minimum screen size of 24 lines by 80 columns is recommended; if the screen is larger, only a $21\times80$ section will be used for the map. %.pg -{\it NetHack\/} can even be played by blind players, with the assistance of +\textit{NetHack} can even be played by blind players, with the assistance of Braille readers or speech synthesisers. Instructions for configuring -{\it NetHack\/} for the blind are included later in this document. +\textit{NetHack} for the blind are included later in this document. %.pg -{\it NetHack\/} generates a new dungeon every time you play it; even the +\textit{NetHack} generates a new dungeon every time you play it; even the authors still find it an entertaining and exciting game despite having won several times. %.pg -{\it NetHack\/} offers a variety of display options. The options available to +\textit{NetHack} offers a variety of display options. The options available to you will vary from port to port, depending on the capabilities of your hardware and software, and whether various compile-time options were enabled when your executable was created. The three possible display @@ -258,10 +258,10 @@ colors in the Guidebook, and because it is common to all ports, we will use the default ASCII characters from the monochrome character display when referring to things you might see on the screen during your game. %.pg -In order to understand what is going on in {\it NetHack}, first you must -understand what {\it NetHack\/} is doing with the screen. The {\it NetHack\/} +In order to understand what is going on in \textit{NetHack}, first you must +understand what \textit{NetHack} is doing with the screen. The \textit{NetHack} screen replaces the ``You see \ldots'' descriptions of text adventure games. -Figure 1 is a sample of what a {\it NetHack\/} screen might look like. +Figure 1 is a sample of what a \textit{NetHack} screen might look like. The way the screen looks for you depends on your platform. %.BR 2 @@ -314,13 +314,13 @@ Figure 2 The bottom two (or three) lines of the screen contain several cryptic pieces of information describing your current status. Figure 1 shows the traditional two-line status area below the map. -Figure 2 shows just the status area, when the {\it statuslines:3\/} +Figure 2 shows just the status area, when the \textit{statuslines:3} option has been set (not all interfaces support this option). If any status line becomes wider than the screen, you might not see all of it due to truncation. -When the numbers grow bigger and multiple {\it conditions\/} are present, +When the numbers grow bigger and multiple \textit{conditions} are present, the two-line format will run out of room on the second line, but -{\it statuslines:2\/} +\textit{statuslines:2} is the default because a basic 24-line terminal isn't tall enough for the third line. @@ -331,7 +331,7 @@ Here are explanations of what the various status items mean: \begin{description} \item[Title] Your character's name and professional ranking (based on role and -{\it experience level\/}, see below). +\textit{experience level}, see below). %.lp \item[Strength] A measure of your character's strength; one of your six basic @@ -367,7 +367,7 @@ particular, it can affect the prices shopkeepers offer you. %.lp \item[Alignment] % -{\it Lawful}, {\it Neutral\/} or {\it Chaotic}. Often, Lawful is +\textit{Lawful}, \textit{Neutral} or \textit{Chaotic}. Often, Lawful is taken as good and Chaotic as evil, but legal and ethical do not always coincide. Your alignment influences how other monsters react toward you. Monsters of a like alignment are more likely @@ -392,7 +392,7 @@ certain magical items or spells. The number in parentheses is the maximum number your hit points can reach. %.lp \item[Power] -Spell points. This tells you how much mystic energy ({\it mana\/}) +Spell points. This tells you how much mystic energy (\textit{mana}) you have available for spell casting. Again, resting will regenerate the amount available. %.lp @@ -400,11 +400,11 @@ amount available. A measure of how effectively your armor stops blows from unfriendly creatures. The lower this number is, the more effective the armor; it is quite possible to have negative armor class. -See the {\it Armor\/} subsection of {\it Objects\/} for more information. +See the \textit{Armor} subsection of \textit{Objects} for more information. %.lp \item[Experience] Your current experience level. -If the {\it showexp\/} +If the \textit{showexp} option is set, it will be followed by a slash and experience points. As you adventure, you gain experience points. At certain experience point totals, you gain an experience level. @@ -412,51 +412,51 @@ The more experienced you are, the better you fight and withstand magical attacks. (By the time your level reaches double digits, the usefulness of showing the points with it has dropped significantly. -You can use the `{\tt O}' command to turn {\it showexp\/} +You can use the `\texttt{O}' command to turn \textit{showexp} off to avoid using up the limited status line space.) %.lp \item[Time] The number of turns elapsed so far, displayed if you have the -{\it time\/} option set. +\textit{time} option set. %.lp \item[Status] Hunger: your current hunger status. -Values are {\it Satiated}, {\it Not~Hungry\/} (or {\it Normal\/}), -{\it Hungry}, {\it Weak}, and {\it Fainting}. +Values are \textit{Satiated}, \textit{Not~Hungry} (or \textit{Normal}), +\textit{Hungry}, \textit{Weak}, and \textit{Fainting}. %.\" not mentioned: Fainted -Not shown when {\it Normal}. +Not shown when \textit{Normal}. %.lp "" Encumbrance: an indication of how what you are carrying affects your ability to move. -Values are {\it Unencumbered}, {\it Burdened}, {\it Stressed}, -{\it Strained}, {\it Overtaxed}, and {\it Overloaded}. -Not shown when {\it Unencumbered}. +Values are \textit{Unencumbered}, \textit{Burdened}, \textit{Stressed}, +\textit{Strained}, \textit{Overtaxed}, and \textit{Overloaded}. +Not shown when \textit{Unencumbered}. %.lp "" Fatal~conditions: -{\it Stone\/} (aka {\it Petrifying}, turning to stone), -{\it Slime\/} (turning into green slime), -{\it Strngl\/} (being strangled), -{\it FoodPois\/} (suffering from acute food poisoning), -{\it TermIll\/} (suffering from a terminal illness). +\textit{Stone} (aka \textit{Petrifying}, turning to stone), +\textit{Slime} (turning into green slime), +\textit{Strngl} (being strangled), +\textit{FoodPois} (suffering from acute food poisoning), +\textit{TermIll} (suffering from a terminal illness). %.lp "" Non-fatal~conditions: -{\it Blind\/} (can't see), {\it Deaf\/} (can't hear), -{\it Stun\/} (stunned), {\it Conf\/} (confused), {\it Hallu\/} (hallucinating). +\textit{Blind} (can't see), \textit{Deaf} (can't hear), +\textit{Stun} (stunned), \textit{Conf} (confused), \textit{Hallu} (hallucinating). %.lp "" Movement~modifiers: -{\it Lev\/} (levitating), {\it Fly\/} (flying), {\it Ride\/} (riding). +\textit{Lev} (levitating), \textit{Fly} (flying), \textit{Ride} (riding). %.lp "" Other conditions and modifiers exist, but there isn't enough room to display them with the other status fields. \\ % unindented paragraph -The {\tt \#attributes} command (default key {\tt \^{}X}) will show +The \texttt{\#attributes} command (default key \texttt{\textasciicircum X}) will show all current status information in unabbreviated format. It also shows other information which might be included on the status lines if those had more room. @@ -469,14 +469,14 @@ lines if those had more room. %.pg The top line of the screen is reserved for messages that describe things that are impossible to represent visually. If you see a -``{\tt --More--}'' on the top line, this means that {\it NetHack\/} has +``\texttt{--More--}'' on the top line, this means that \textit{NetHack} has another message to display on the screen, but it wants to make certain that you've read the one that is there first. To read the next message, just press the space bar. %.pg To change how and what messages are shown on the message line, -see ``{\it Configuring Message Types\/}`` and the {\it verbose\/} +see ``\textit{Configuring Message Types}`` and the \textit{verbose} option. %.hn 2 @@ -490,7 +490,7 @@ options to change some of the symbols the game uses; otherwise, the game will use default symbols. Here is a list of what the default symbols mean: -\begin{description}[font=\ttfamily] +\begin{description}[font=\mdseries\ttfamily] \item[-] The horizontal or corner walls of a room, or an open east/west door. \item[|] @@ -540,7 +540,7 @@ A gem or rock (possibly valuable, possibly worthless). A boulder or statue or an engraving on the floor of a room.\\ %.lp "" Note: statues are displayed as if they were the monsters they depict -so won't appear as a {\it grave accent\/} (aka {\it back-tick}). +so won't appear as a \textit{grave accent} (aka \textit{back-tick}). \item[0] An iron ball. \item[\textunderscore] @@ -554,7 +554,6 @@ or a pool of lava or a wall of lava. An opulent throne. \item[a-z \textrm{\textmd{and}}] \item[A-HJ-Z \textrm{\textmd{and}}] -%should probably change \item[@\&\textquotesingle:;] to \item[\verb+@&':;+] \item[@\&\textquotesingle :;] Letters and certain other symbols represent the various inhabitants of the Mazes of Menace. @@ -564,17 +563,17 @@ Sometimes, however, they can be helpful. Rather than a specific type of monster, this marks the last known location of an invisible or otherwise unseen monster. Note that the monster could have moved. -The `{\tt s}', `{\tt F}', and `{\tt m}' commands may be useful here. +The `\texttt{s}', `\texttt{F}', and `\texttt{m}' commands may be useful here. \item[1-5] The digits 1 through 5 may be displayed, marking unseen monsters sensed -via the {\it Warning\/} attribute. +via the \textit{Warning} attribute. Less dangerous monsters are indicated by lower values, more dangerous by higher values. \end{description} %.pg You need not memorize all these symbols; you can ask the game what any -symbol represents with the `{\tt /}' command (see the next section for +symbol represents with the `\texttt{/}' command (see the next section for more info). %.hn 1 @@ -584,23 +583,23 @@ more info). Commands can be initiated by typing one or two characters to which the command is bound to, or typing the command name in the extended commands entry. Some commands, -like ``{\tt search}'', do not require that any more information be collected -by {\it NetHack\/}. Other commands might require additional information, for +like ``\texttt{search}'', do not require that any more information be collected +by \textit{NetHack}. Other commands might require additional information, for example a direction, or an object to be used. For those commands that -require additional information, {\it NetHack\/} will present you with either +require additional information, \textit{NetHack} will present you with either a menu of choices, or with a command line prompt requesting information. Which you are presented with will depend chiefly on how you have set the -`{\it menustyle\/}' +`\textit{menustyle}' option. %.pg -For example, a common question in the form ``{\tt What do you want to +For example, a common question in the form ``\texttt{What do you want to use? [a-zA-Z\ ?*]}'', asks you to choose an object you are carrying. -Here, ``{\tt a-zA-Z}'' are the inventory letters of your possible choices. -Typing `{\tt ?}' gives you an inventory list of these items, so you can see -what each letter refers to. In this example, there is also a `{\tt *}' +Here, ``\texttt{a-zA-Z}'' are the inventory letters of your possible choices. +Typing `\texttt{?}' gives you an inventory list of these items, so you can see +what each letter refers to. In this example, there is also a `\texttt{*}' indicating that you may choose an object not on the list, if you -wanted to use something unexpected. Typing a `{\tt *}' lists your entire +wanted to use something unexpected. Typing a `\texttt{*}' lists your entire inventory, so you can see the inventory letters of every object you're carrying. Finally, if you change your mind and decide you don't want to do this command after all, you can press the `ESC' key to abort the @@ -608,50 +607,50 @@ command. %.pg You can put a number before some commands to repeat them that many -times; for example, ``{\tt 10s}'' will search ten times. If you have the -{\it number\textunderscore pad\/} -option set, you must type `{\tt n}' to prefix a count, so the example above -would be typed ``{\tt n10s}'' instead. Commands for which counts make no +times; for example, ``\texttt{10s}'' will search ten times. If you have the +\textit{number\textunderscore pad} +option set, you must type `\texttt{n}' to prefix a count, so the example above +would be typed ``\texttt{n10s}'' instead. Commands for which counts make no sense ignore them. In addition, movement commands can be prefixed for greater control (see below). To cancel a count or a prefix, press the `ESC' key. %.pg The list of commands is rather long, but it can be read at any time -during the game through the `{\tt ?}' command, which accesses a menu of +during the game through the `\texttt{?}' command, which accesses a menu of helpful texts. Here are the default key bindings for your reference: -\begin{description}[font=\ttfamily] +\begin{description}[font=\mdseries\ttfamily] %.lp \item[?] Help menu: display one of several help texts available. %.lp \item[/] -The {\tt whatis} command, to +The \texttt{whatis} command, to tell what a symbol represents. You may choose to specify a location or type a symbol (or even a whole word) to explain. Specifying a location is done by moving the cursor to a particular spot -on the map and then pressing one of `{\tt .}', `{\tt ,}', `{\tt ;}', -or `{\tt :}'. `{\tt .}' will explain the symbol at the chosen location, -conditionally check for ``{\tt More info?}'' depending upon whether the -`{\it help\/}' +on the map and then pressing one of `\texttt{.}', `\texttt{,}', `\texttt{;}', +or `\texttt{:}'. `\texttt{.}' will explain the symbol at the chosen location, +conditionally check for ``\texttt{More info?}'' depending upon whether the +`\textit{help}' option is on, and then you will be asked to pick another location; -`{\tt ,}' will explain the symbol but skip any additional +`\texttt{,}' will explain the symbol but skip any additional information, then let you pick another location; -`{\tt ;}' will skip additional info and also not bother asking -you to choose another location to examine; `{\tt :}' will show additional +`\texttt{;}' will skip additional info and also not bother asking +you to choose another location to examine; `\texttt{:}' will show additional info, if any, without asking for confirmation. When picking a location, -pressing the {\tt ESC} key will terminate this command, or pressing `{\tt ?}' +pressing the \texttt{ESC} key will terminate this command, or pressing `\texttt{?}' will give a brief reminder about how it works. %.lp "" If the -{\it autodescribe\/} +\textit{autodescribe} option is on, a short description of what you see at each location is -shown as you move the cursor. Typing `{\tt \#}' while picking a location will +shown as you move the cursor. Typing `\texttt{\#}' while picking a location will toggle that option on or off. The -{\it whatis\textunderscore coord\/} +\textit{whatis\textunderscore coord} option controls whether the short description includes map coordinates. %.lp "" @@ -662,7 +661,7 @@ always gives any additional information available about that name. You may also request a description of nearby monsters, all monsters currently displayed, nearby objects, or all objects. The -{\it whatis\textunderscore coord\/} +\textit{whatis\textunderscore coord} option controls which format of map coordinate is included with their descriptions. %.lp @@ -689,7 +688,7 @@ one-step movement commands cause you to fight monsters; the others \verb+ h- . -l + & \verb+ 4- . -6 +\\ \verb+ / | \ + & \verb+ / | \ +\\ \verb+ b j n + & \verb+ 1 2 3 +\\ - & (if {\it number\textunderscore pad\/} set) + & (if \textit{number\textunderscore pad} set) \end{tabular} \end{center} %.ed @@ -704,36 +703,36 @@ Go in that direction until you hit a wall or run into something. Prefix: move without picking up objects or fighting (even if you remember a monster there).\\ %.lp "" -A few non-movement commands use the `{\tt m}' prefix to request +A few non-movement commands use the `\texttt{m}' prefix to request operating via menu (to temporarily override the -{\it menustyle:Traditional\/} +\textit{menustyle:Traditional} option). -Primarily useful for `{\tt ,}' (pickup) when there is only one class of +Primarily useful for `\texttt{,}' (pickup) when there is only one class of objects present (where there won't be any ``what kinds of objects?'' prompt, -so no opportunity to answer `{\tt m}' at that prompt). +so no opportunity to answer `\texttt{m}' at that prompt). \\ %.lp "" The prefix will -make ``{\tt \#travel}'' command show a menu of interesting targets in sight. -It can also be used with the `{\tt $\backslash$}' (known, show a -list of all discovered objects) and the `{\tt \`{}}' (knownclass, +make ``\texttt{\#travel}'' command show a menu of interesting targets in sight. +It can also be used with the `\texttt{\textbackslash}' (known, show a +list of all discovered objects) and the `\texttt{\`{}}' (knownclass, show a list of discovered objects in a particular class) commands to offer a menu of several sorting alternatives (which sets a new value for the -{\it sortdiscoveries\/} -option); also for ``{\tt \#vanquished}'' and ``{\tt \#genocided}'' commands +\textit{sortdiscoveries} +option); also for ``\texttt{\#vanquished}'' and ``\texttt{\#genocided}'' commands to offer a sorting menu. \\ %.lp "" A few other commands (eat food, offer sacrifice, apply tinning-kit, drink/quaff, dip, tip container) use -the `{\tt m}' prefix to skip checking for applicable objects on +the `\texttt{m}' prefix to skip checking for applicable objects on the floor and go straight to checking inventory, -or (for ``{\tt \#loot}'' to remove a saddle), +or (for ``\texttt{\#loot}'' to remove a saddle), skip containers and go straight to adjacent monsters. \\ %.lp "" -In debug mode (aka ``wizard mode''), the `{\tt m}' prefix may also be -used with the ``{\tt \#teleport}'' and ``{\tt \#wizlevelport}'' commands. +In debug mode (aka ``wizard mode''), the `\texttt{m}' prefix may also be +used with the ``\texttt{\#teleport}'' and ``\texttt{\#wizlevelport}'' commands. %.lp \item[F{[yuhjklbn]}] Prefix: fight a monster (even if you only guess one is there). @@ -742,22 +741,22 @@ Prefix: fight a monster (even if you only guess one is there). Prefix: Move until something interesting is found. %.lp \item[G{[yuhjklbn]} \textrm{\textmd{or}} +{[yuhjklbn]}] -Prefix: Similar to `{\tt g}', but forking of corridors is not considered +Prefix: Similar to `\texttt{g}', but forking of corridors is not considered interesting. \\ -Note: {\tt +} means holding the {\tt } or -{\tt } key down like {\tt } while typing and releasing -{\tt }, then releasing {\tt }. {\tt \^{}} is used as +Note: \texttt{+} means holding the \texttt{} or +\texttt{} key down like \texttt{} while typing and releasing +\texttt{}, then releasing \texttt{}. \texttt{\textasciicircum } is used as shorthand elsewhere in the Guidebook to mean the same thing. Control -characters are case-insensitive so {\tt \^{}x} and {\tt \^{}X} are the same. +characters are case-insensitive so \texttt{\textasciicircum x} and \texttt{\textasciicircum X} are the same. %.lp \item[M{[yuhjklbn]}] -Old versions supported `{\tt M}' as a movement prefix which -combined the effect of `{\tt m}' with {\tt +}. +Old versions supported `\texttt{M}' as a movement prefix which +combined the effect of `\texttt{m}' with \texttt{+}. That is no longer supported as a prefix but similar effect can be achieved -by using {\tt m} and {\tt G} in combination. -{\tt m} can also be used in combination with {\tt g}, -{\tt +}, or {\tt +}. +by using \texttt{m} and \texttt{G} in combination. +\texttt{m} can also be used in combination with \texttt{g}, +\texttt{+}, or \texttt{+}. %.lp \item[\textunderscore] Travel to a map location via a shortest-path algorithm.\\ @@ -767,16 +766,16 @@ is computed over map locations the hero knows about (e.g. seen or previously traversed). If there is no known path, a guess is made instead. Stops on most of -the same conditions as the `{\tt G}' command, but without picking up -objects, so implicitly forces the `{\tt m}' prefix. +the same conditions as the `\texttt{G}' command, but without picking up +objects, so implicitly forces the `\texttt{m}' prefix. For ports with mouse support, the command is also invoked when a mouse-click takes place on a location other than the current position. %.lp \item[.] Wait or rest, do nothing for one turn. -Precede with the `{\tt m}' prefix -to wait for a turn even next to a hostile monster, if {\it safe\textunderscore wait\/} +Precede with the `\texttt{m}' prefix +to wait for a turn even next to a hostile monster, if \textit{safe\textunderscore wait} is on. %.lp \item[a] @@ -789,8 +788,8 @@ Confirmation is required. \item[A] Remove one or more worn items, such as armor.\\ %.lp "" -Use `{\tt T}' (take off) to take off only one piece of armor -or `{\tt R}' (remove) to take off only one accessory. +Use `\texttt{T}' (take off) to take off only one piece of armor +or `\texttt{R}' (remove) to take off only one accessory. %.lp \item[\textasciicircum A] Repeat the previous command. @@ -801,57 +800,57 @@ Close a door. \item[C] Call (name) a monster, an individual object, or a type of object.\\ %.lp "" -Same as extended command ``{\tt \#name}''. +Same as extended command ``\texttt{\#name}''. %.lp \item[\textasciicircum C] Panic button. Quit the game. %.lp \item[d] Drop something.\\ -For example {\tt d7a} --- drop seven items of object -{\it a}. +For example \texttt{d7a} --- drop seven items of object +\textit{a}. %.lp \item[D] Drop several things.\\ %.lp "" In answer to the question\\ -``{\tt What kinds of things do you want to drop? [!\%= BUCXPaium]}''\\ +``\texttt{What kinds of things do you want to drop? [!\%= BUCXPaium]}''\\ you should type zero or more object symbols possibly followed by -`{\tt a}' and/or `{\tt i}' and/or `{\tt u}' and/or `{\tt m}'. +`\texttt{a}' and/or `\texttt{i}' and/or `\texttt{u}' and/or `\texttt{m}'. In addition, one or more of the bless\-ed/\-un\-curs\-ed/\-curs\-ed groups may be typed.\\ %.sd %.si -{\tt DB} --- drop all objects known to be blessed.\\ -{\tt DU} --- drop all objects known to be uncursed.\\ -{\tt DC} --- drop all objects known to be cursed.\\ -{\tt DX} --- drop all objects of unknown B/U/C status.\\ -{\tt DP} --- drop objects picked up last.\\ -{\tt Da} --- drop all objects, without asking for confirmation.\\ -{\tt Di} --- examine your inventory before dropping anything.\\ -{\tt Du} --- drop only unpaid objects (when in a shop).\\ -{\tt Dm} --- use a menu to pick which object(s) to drop.\\ -{\tt D\%u} --- drop only unpaid food. +\texttt{DB} --- drop all objects known to be blessed.\\ +\texttt{DU} --- drop all objects known to be uncursed.\\ +\texttt{DC} --- drop all objects known to be cursed.\\ +\texttt{DX} --- drop all objects of unknown B/U/C status.\\ +\texttt{DP} --- drop objects picked up last.\\ +\texttt{Da} --- drop all objects, without asking for confirmation.\\ +\texttt{Di} --- examine your inventory before dropping anything.\\ +\texttt{Du} --- drop only unpaid objects (when in a shop).\\ +\texttt{Dm} --- use a menu to pick which object(s) to drop.\\ +\texttt{D\%u} --- drop only unpaid food. %.ei %.ed The last example shows a combination. -There are four categories of object filtering: class (`{\tt !}' for -potions, `{\tt ?}' for scrolls, and so on), shop status (`{\tt u}' for +There are four categories of object filtering: class (`\texttt{!}' for +potions, `\texttt{?}' for scrolls, and so on), shop status (`\texttt{u}' for unpaid, in other words, owned by the shop), bless/curse state -(`{\tt B}', `{\tt U}', `{\tt C}', and `{\tt X}' as shown above), -and novelty (`{\tt P}', recently picked up items; controlled by picking +(`\texttt{B}', `\texttt{U}', `\texttt{C}', and `\texttt{X}' as shown above), +and novelty (`\texttt{P}', recently picked up items; controlled by picking up or dropping things rather than by any time factor). %.lp "" \\ -If you specify more than one value in a category (such as ``{\tt !?}'' for -potions and scrolls or ``{\tt BU}'' for blessed and uncursed), an inventory +If you specify more than one value in a category (such as ``\texttt{!?}'' for +potions and scrolls or ``\texttt{BU}'' for blessed and uncursed), an inventory object will meet the criteria if it matches any of the specified -values (so ``{\tt !?}'' means `{\tt !}' or `{\tt ?}'). +values (so ``\texttt{!?}'' means `\texttt{!}' or `\texttt{?}'). If you specify more than one category, an inventory object must meet -each of the category criteria (so ``{\tt \%u}'' means class `{\tt \%}' and -unpaid `{\tt u}'). +each of the category criteria (so ``\texttt{\%u}'' means class `\texttt{\%}' and +unpaid `\texttt{u}'). Lastly, you may specify multiple values within multiple categories: -``{\tt !?BU}'' will select all potions and scrolls which are known to be +``\texttt{!?BU}'' will select all potions and scrolls which are known to be blessed or uncursed. (In versions prior to 3.6, filter combinations behaved differently.) %.lp @@ -863,15 +862,15 @@ Eat food.\\ %.lp "" Normally checks for edible item(s) on the floor, then if none are found or none are chosen, checks for edible item(s) in inventory. -Precede `{\tt e}' with the `{\tt m}' prefix to bypass attempting to eat +Precede `\texttt{e}' with the `\texttt{m}' prefix to bypass attempting to eat anything off the floor.\\ %.lp "" If you attempt to eat while already satiated, you might choke to death. If you risk it, you will be asked whether -to ``continue eating?'' {\it if you survive the first bite\/}. +to ``continue eating?'' \textit{if you survive the first bite}. You can set the -{\it paranoid\textunderscore confirmation:eating\/} -option to require a response of ``{\tt yes}'' instead of just `{\tt y}'. +\textit{paranoid\textunderscore confirmation:eating} +option to require a response of ``\texttt{yes}'' instead of just `\texttt{y}'. %.lp % Make sure Elbereth is not hyphenated below, the exact spelling matters. % (Only specified here to parallel Guidebook.mn; use of \tt font implicitly @@ -881,49 +880,49 @@ option to require a response of ``{\tt yes}'' instead of just `{\tt y}'. Engrave a message on the floor.\\ %.sd %.si -{\tt E-} --- write in the dust with your fingers.\\ +\texttt{E-} --- write in the dust with your fingers.\\ %.ei %.ed %.lp "" -Engraving the word ``{\tt Elbereth}'' will cause most monsters to not attack +Engraving the word ``\texttt{Elbereth}'' will cause most monsters to not attack you hand-to-hand (but if you attack, you will rub it out); this is often useful to give yourself a breather. %.lp \item[f] Fire (shoot or throw) one of the objects placed in your quiver (or quiver sack, or that you have at the ready). -You may select ammunition with a previous `{\tt Q}' command, or let the -computer pick something appropriate if {\it autoquiver\/} is true. +You may select ammunition with a previous `\texttt{Q}' command, or let the +computer pick something appropriate if \textit{autoquiver} is true. If your wielded weapon has the throw-and-return property, your quiver -is empty, and {\it autoquiver\/} +is empty, and \textit{autoquiver} is false, you will throw that wielded weapon instead of filling the quiver. This will also automatically use a polearm if wielded. -If {\it fireassist\/} is true, firing will automatically try to wield a launcher +If \textit{fireassist} is true, firing will automatically try to wield a launcher (for example, a bow or a sling) matching the ammo in the quiver; this might take multiple turns, and get interrupted by a monster. Remember to swap back to your main melee weapon afterwards. %.lp "" \\ -See also `{\tt t}' (throw) for more general throwing and shooting. +See also `\texttt{t}' (throw) for more general throwing and shooting. %.lp \item[i] List your inventory (everything you're carrying). %.lp \item[I] List selected parts of your inventory, usually be specifying the character -for a particular set of objects, like `{\tt [}' for armor or `{\tt !}' +for a particular set of objects, like `\texttt{[}' for armor or `\texttt{!}' for potions.\\ %.sd %.si -{\tt I*} --- list all gems in inventory;\\ -{\tt Iu} --- list all unpaid items;\\ -{\tt Ix} --- list all used up items that are on your shopping bill;\\ -{\tt IB} --- list all items known to be blessed;\\ -{\tt IU} --- list all items known to be uncursed;\\ -{\tt IC} --- list all items known to be cursed;\\ -{\tt IX} --- list all items whose bless/curse status is unknown;\\ -{\tt IP} --- list items picked up last;\\ -{\tt I\$} --- count your money. +\texttt{I*} --- list all gems in inventory;\\ +\texttt{Iu} --- list all unpaid items;\\ +\texttt{Ix} --- list all used up items that are on your shopping bill;\\ +\texttt{IB} --- list all items known to be blessed;\\ +\texttt{IU} --- list all items known to be uncursed;\\ +\texttt{IC} --- list all items known to be cursed;\\ +\texttt{IX} --- list all items whose bless/curse status is unknown;\\ +\texttt{IP} --- list items picked up last;\\ +\texttt{I\$} --- count your money. %.ei %.ed %.lp @@ -940,18 +939,18 @@ it, depending on your user interface). For the non-boolean choices, a further menu or prompt will appear once you've closed this menu. The available options are listed later in this Guidebook. Options are usually set before the -game rather than with the `{\tt O}' command; see the section on options below. -Precede {\tt O} with the {\tt m} prefix to show advanced options. +game rather than with the `\texttt{O}' command; see the section on options below. +Precede \texttt{O} with the \texttt{m} prefix to show advanced options. %.lp \item[\textasciicircum O] Show overview.\\ %.lp "" -Shortcut for ``{\tt \#overview}'': +Shortcut for ``\texttt{\#overview}'': list interesting dungeon levels visited.\\ %.lp "" -(Prior to 3.6.0, `{\tt \^{}O}' was a debug mode command which listed +(Prior to 3.6.0, `\texttt{\textasciicircum O}' was a debug mode command which listed the placement of all special levels. -Use ``{\tt \#wizwhere}'' to run that command.) +Use ``\texttt{\#wizwhere}'' to run that command.) %.lp \item[p] Pay your shopping bill. @@ -962,15 +961,15 @@ Put on an accessory (ring, amulet, or blindfold).\\ This command may also be used to wear armor. The prompt for which inventory item to use will only list accessories, but choosing an unlisted item of armor will attempt to wear it. -(See the `{\tt W}' command below. It lists armor as the inventory +(See the `\texttt{W}' command below. It lists armor as the inventory choices but will accept an accessory and attempt to put that on.) %.lp \item[\textasciicircum P] Repeat previous message.\\ %.lp "" -Subsequent {\tt \^{}P}'s repeat earlier messages. +Subsequent \texttt{\textasciicircum P}'s repeat earlier messages. For some interfaces, the behavior can be varied via the -{\it msg\textunderscore window\/} option. +\textit{msg\textunderscore window} option. %.lp \item[q] Quaff (drink) something (potion, water, etc).\\ @@ -979,13 +978,13 @@ When there is a fountain or sink present, it asks whether to drink from that. If that is declined, then it offers a chance to choose a potion from inventory. -Precede {\tt q} with the {\tt m} prefix to skip asking about +Precede \texttt{q} with the \texttt{m} prefix to skip asking about drinking from a fountain or sink. %.lp \item[Q] Select an object for your quiver, quiver sack, or just generally at the ready (only one of these is available at a time). You can then throw -this (or one of these) using the `{\tt f}' command. +this (or one of these) using the `\texttt{f}' command. %.lp \item[r] Read a scroll or spellbook. @@ -996,13 +995,13 @@ Remove a worn accessory (ring, amulet, or blindfold).\\ If you're wearing more than one, you'll be prompted for which one to remove. When you're only wearing one, then by default it will be removed without asking, but you can set the -{\it paranoid\textunderscore confirmation:Remove\/} +\textit{paranoid\textunderscore confirmation:Remove} option to require a prompt.\\ %.lp "" This command may also be used to take off armor. The prompt for which inventory item to remove only lists worn accessories, but an item of worn armor can be chosen. -(See the `{\tt T}' command below. It lists armor as the inventory +(See the `\texttt{T}' command below. It lists armor as the inventory choices but will accept an accessory and attempt to remove it.) %.lp \item[\textasciicircum R] @@ -1011,8 +1010,8 @@ Redraw the screen. \item[s] Search for secret doors and traps around you. It usually takes several tries to find something. -Precede with the `{\tt m}' prefix to wait for a turn -even next to a hostile monster, if {\it safe\textunderscore wait\/} +Precede with the `\texttt{m}' prefix to wait for a turn +even next to a hostile monster, if \textit{safe\textunderscore wait} is on.\\ %.lp "" Can also be used to figure out whether there is still a monster at @@ -1042,8 +1041,8 @@ that arrow and any weapon skill bonus or penalty for bow applies. If you ``throw'' an arrow while not wielding a bow, you are throwing it by hand and it will generally be less effective than when shot.\\ %.lp "" -See also `{\tt f}' (fire) for throwing or shooting an item pre-selected -via the `{\tt Q}' (quiver) command, with some extra assistance. +See also `\texttt{f}' (fire) for throwing or shooting an item pre-selected +via the `\texttt{Q}' (quiver) command, with some extra assistance. %.lp \item[T] Take off armor.\\ @@ -1054,13 +1053,13 @@ and/or a shirt, or a suit covering a shirt, as if the underlying items weren't there.) When you're only wearing one, then by default it will be taken off without asking, but you can set the -{\it paranoid\textunderscore confirmation:Remove\/} +\textit{paranoid\textunderscore confirmation:Remove} option to require a prompt.\\ %.lp "" This command may also be used to remove accessories. The prompt for which inventory item to take off only lists worn armor, but a worn accessory can be chosen. -(See the `{\tt R}' command above. It lists accessories as the inventory +(See the `\texttt{R}' command above. It lists accessories as the inventory choices but will accept an item of armor and attempt to take it off.) %.lp \item[\textasciicircum T] @@ -1076,11 +1075,11 @@ Display the game history. Wield weapon.\\ %.sd %.si -{\tt w-} --- wield nothing, use your bare (or gloved) hands.\\ +\texttt{w-} --- wield nothing, use your bare (or gloved) hands.\\ %.ei %.ed -Some characters can wield two weapons at once; use the `{\tt X}' command -(or the ``{\tt \#twoweapon}'' extended command) to do so. +Some characters can wield two weapons at once; use the `\texttt{X}' command +(or the ``\texttt{\#twoweapon}'' extended command) to do so. %.lp \item[W] Wear armor.\\ @@ -1088,7 +1087,7 @@ Wear armor.\\ This command may also be used to put on an accessory (ring, amulet, or blindfold). The prompt for which inventory item to use will only list armor, but choosing an unlisted accessory will attempt to put it on. -(See the `{\tt P}' command above. It lists accessories as the inventory +(See the `\texttt{P}' command above. It lists accessories as the inventory choices but will accept an item of armor and attempt to wear it.) %.lp \item[x] @@ -1100,31 +1099,31 @@ the exchange still takes place. %.lp \item[X] Toggle two-weapon combat, if your character can do it. Also available -via the ``{\tt \#twoweapon}'' extended command.\\ +via the ``\texttt{\#twoweapon}'' extended command.\\ %.lp "" (In versions prior to 3.6 this keystroke ran the command to switch from normal play to ``explore mode'', also known as ``discovery mode'', which has now -been moved to ``{\tt \#exploremode}'' and {\tt M-X}.) +been moved to ``\texttt{\#exploremode}'' and \texttt{M-X}.) %.lp \item[\textasciicircum X] Display basic information about your character.\\ %.lp "" Displays name, role, race, gender (unless role name makes that -redundant, such as {\tt Caveman} or {\tt Priestess}), and alignment, +redundant, such as \texttt{Caveman} or \texttt{Priestess}), and alignment, along with your patron deity and his or her opposition. It also shows most of the various items of information from the status line(s) in a less terse form, including several additional things which don't appear in the normal status display due to space considerations.\\ %.lp "" -In normal play, that's all that `{\tt \^{}X}' displays. +In normal play, that's all that `\texttt{\textasciicircum X}' displays. In explore mode, the role and status feedback is augmented by the -information provided by {\it enlightenment\/} magic. +information provided by \textit{enlightenment} magic. %.lp \item[z] Zap a wand.\\ %.sd %.si -{\tt z.} --- to aim at yourself, use `{\tt .}' for the direction. +\texttt{z.} --- to aim at yourself, use `\texttt{.}' for the direction. %.ei %.ed %.lp @@ -1132,7 +1131,7 @@ Zap a wand.\\ Zap (cast) a spell.\\ %.sd %.si -{\tt Z.} --- to cast at yourself, use `{\tt .}' for the direction. +\texttt{Z.} --- to cast at yourself, use `\texttt{.}' for the direction. %.ei %.ed %.lp @@ -1149,10 +1148,10 @@ Show what type of thing a visible symbol corresponds to. \item[,] Pick up some things from the floor beneath you.\\ %.lp "" -May be preceded by `{\tt m}' to force a selection menu. +May be preceded by `\texttt{m}' to force a selection menu. %.lp \item[@] -Toggle the {\it autopickup\/} option on and off. +Toggle the \textit{autopickup} option on and off. %.lp \item[\textasciicircum] Ask for the type of an adjacent trap you found earlier. @@ -1188,9 +1187,9 @@ Using this command, you can also rearrange the order in which your spells are listed, either by sorting the entire list or by picking one spell from the menu then picking another to swap places with it. Swapping pairs of spells changes their casting letters, -so the change lasts after the current `{\tt +}' command finishes. Sorting +so the change lasts after the current `\texttt{+}' command finishes. Sorting the whole list is temporary. To make the most recent sort order persist -beyond the current `{\tt +}' command, choose the sort option again and then +beyond the current `\texttt{+}' command, choose the sort option again and then pick ``reassign casting letters''. (Any spells learned after that will be added to the end of the list rather than be inserted into the sorted ordering.) @@ -1199,28 +1198,28 @@ ordering.) Show what types of objects have been discovered. \\ %.lp "" -May be preceded by `{\tt m}' to select preferred display order. +May be preceded by `\texttt{m}' to select preferred display order. %.lp \item[\textasciigrave] Show discovered types for one class of objects. \\ %.lp "" -May be preceded by `{\tt m}' to select preferred display order. +May be preceded by `\texttt{m}' to select preferred display order. %.lp \item[|] If persistent inventory display is supported and enabled (with the -{\it perm\textunderscore invent\/} +\textit{perm\textunderscore invent} option), interact with it instead of with the map. \\ %.lp "" Allows scrolling with the menu\textunderscore first\textunderscore page, menu\textunderscore previous\textunderscore page, menu\textunderscore next\textunderscore page, and menu\textunderscore last\textunderscore page -keys (`{\tt \^{}}', `{\tt <}', `{\tt >}', `{\tt \verb+|+}' by default). +keys (`\texttt{\textasciicircum}', `\texttt{<}', `\texttt{>}', `\texttt{|}' by default). Some interfaces also support menu\textunderscore shift\textunderscore left and menu\textunderscore shift\textunderscore right -keys (`{\tt \verb+{+}' and `{\tt \verb+}+}' by default). -Use the {\it Return\/} (aka {\it Enter\/}) or {\it Escape\/} key to +keys (`\texttt{\textbraceleft}' and `\texttt{\textbraceright}' by default). +Use the \textit{Return} (aka \textit{Enter}) or \textit{Escape} key to resume play. %.lp @@ -1234,19 +1233,19 @@ You can view the explored portion of the current level's map without monsters; without monsters and objects; or without monsters, objects, and traps.\\ %.lp "" -The {\tt } key is also shown as {\tt } on some keyboards or -{\tt } on others. -It is sometimes displayed as {\tt \^{}?} even though that is not an actual +The \texttt{} key is also shown as \texttt{} on some keyboards or +\texttt{} on others. +It is sometimes displayed as \texttt{\textasciicircum ?} even though that is not an actual control character.\\ %.lp "" -Many terminals have an option to swap the {\tt } and {\tt } -keys, so typing the {\tt } key might not execute this command. -If that happens, you can use the extended command ``{\tt \#terrain}'' instead. +Many terminals have an option to swap the \texttt{} and \texttt{} +keys, so typing the \texttt{} key might not execute this command. +If that happens, you can use the extended command ``\texttt{\#terrain}'' instead. %.lp \item[\#] Perform an extended command.\\ %.lp "" -As you can see, the authors of {\it NetHack\/} +As you can see, the authors of \textit{NetHack} used up all the letters, so this is a way to introduce the less frequently used commands. What extended commands are available depends on what features @@ -1254,8 +1253,8 @@ the game was compiled with. %.lp \item[\#adjust] Adjust inventory letters (most useful when the -{\it fixinv\/} -option is ``on''). Autocompletes. Default key is `{\tt M-a}'.\\ +\textit{fixinv} +option is ``on''). Autocompletes. Default key is `\texttt{M-a}'.\\ %.lp "" This command allows you to move an item from one particular inventory slot to another so that it has a letter which is more meaningful for you @@ -1264,17 +1263,17 @@ are displayed. You can move to a currently empty slot, or if the destination is occupied---and won't merge---the item there will swap slots with the one being moved. -``{\tt \#adjust}'' can also be used to split a stack of objects; when +``\texttt{\#adjust}'' can also be used to split a stack of objects; when choosing the item to adjust, enter a count prior to its letter.\\ %.lp "" Adjusting without a count used to collect all compatible stacks when moving to the destination. That behavior has been changed; to gather -compatible stacks, ``{\tt \#adjust}'' a stack into its own inventory slot. +compatible stacks, ``\texttt{\#adjust}'' a stack into its own inventory slot. If it has a name assigned, other stacks with the same name or with no name will merge provided that all their other attributes match. If it does not have a name, only other stacks with no name are eligible. In either case, otherwise compatible stacks with a different name -will not be merged. This contrasts with using ``{\tt \#adjust}'' to move +will not be merged. This contrasts with using ``\texttt{\#adjust}'' to move from one slot to a different slot. In that situation, moving (no count given) a compatible stack will merge if either stack has a name when the other doesn't and give that name to the result, while @@ -1284,28 +1283,28 @@ deciding whether to merge with the destination stack. \item[\#annotate] Allows you to specify one line of text to associate with the current dungeon level. All levels with annotations are displayed by the -``{\tt \#overview}'' command. Autocompletes. -Default key is `{\tt M-A}', -and also `{\tt \^{}N}' if {\it number\textunderscore pad\/} is on. +``\texttt{\#overview}'' command. Autocompletes. +Default key is `\texttt{M-A}', +and also `\texttt{\textasciicircum N}' if \textit{number\textunderscore pad} is on. %.lp \item[\#apply] Apply (use) a tool such as a pick-axe, a key, or a lamp. -Default key is `{\tt a}'.\\ +Default key is `\texttt{a}'.\\ %.lp "" -If the tool used acts on items on the floor, using the `{\tt m}' prefix +If the tool used acts on items on the floor, using the `\texttt{m}' prefix skips those items.\\ %.lp "" If used on a wand, that wand will be broken, releasing its magic in the process. Confirmation is required. %.lp \item[\#attributes] -Show your attributes. Default key is `{\tt \^{}X}'. +Show your attributes. Default key is `\texttt{\textasciicircum X}'. %.lp \item[\#autopickup] -Toggle the {\it autopickup\/} option. Default key is `{\tt @}'. +Toggle the \textit{autopickup} option. Default key is `\texttt{@}'. %.lp \item[\#bugreport] -Bring up a browser window to submit a report to the {\it NetHack Development +Bring up a browser window to submit a report to the \textit{NetHack Development Team}. Can be disabled at the time the program is built; when enabled, CRASHREPORTURL must be set in the system configuration file. @@ -1313,23 +1312,23 @@ CRASHREPORTURL must be set in the system configuration file. \item[\#call] Call (name) a monster, or an object in inventory, on the floor, or in the discoveries list, or add an annotation for the -current level (same as ``{\tt \#annotate}''). Default key is `{\tt C}'. +current level (same as ``\texttt{\#annotate}''). Default key is `\texttt{C}'. %.lp \item[\#cast] -Cast a spell. Default key is `{\tt Z}'. +Cast a spell. Default key is `\texttt{Z}'. %.lp \item[\#chat] -Talk to someone. Default key is `{\tt M-c}'. +Talk to someone. Default key is `\texttt{M-c}'. %.lp \item[\#chronicle] Show a list of important game events. %.lp \item[\#close] -Close a door. Default key is `{\tt c}'. +Close a door. Default key is `\texttt{c}'. %.lp \item[\#conduct] List voluntary challenges you have maintained. Autocompletes. -Default key is `{\tt M-C}'.\\ +Default key is `\texttt{M-C}'.\\ %.lp "" See the section below entitled ``Conduct'' for details. %.lp @@ -1338,54 +1337,54 @@ Start the fuzz tester. Debug mode only. %.lp \item[\#dip] -Dip an object into something. Autocompletes. Default key is `{\tt M-d}'.\\ +Dip an object into something. Autocompletes. Default key is `\texttt{M-d}'.\\ %.lp "" -The {\tt m} prefix skips dipping into a fountain or pool if there +The \texttt{m} prefix skips dipping into a fountain or pool if there is one at your location. %.lp \item[\#down] -Go down a staircase. Default key is `{\tt >}'. +Go down a staircase. Default key is `\texttt{>}'. %.lp \item[\#drop] -Drop an item. Default key is `{\tt d}'. +Drop an item. Default key is `\texttt{d}'. %.lp \item[\#droptype] -Drop specific item types. Default key is `{\tt D}'. +Drop specific item types. Default key is `\texttt{D}'. %.lp \item[\#eat] -Eat something. Default key is `{\tt e}'. -The `{\tt m}' prefix skips eating items on the floor. +Eat something. Default key is `\texttt{e}'. +The `\texttt{m}' prefix skips eating items on the floor. %.lp \item[\#engrave] -Engrave writing on the floor. Default key is `{\tt E}'. +Engrave writing on the floor. Default key is `\texttt{E}'. %.lp \item[\#enhance] Advance or check weapon and spell skills. Autocompletes. -Default key is `{\tt M-e}'. +Default key is `\texttt{M-e}'. %.lp \item[\#exploremode] Switch from normal play to non-scoring explore mode. -Default key is `{\tt M-X}'.\\ +Default key is `\texttt{M-X}'.\\ %.lp "" -Requires confirmation; default response is `{\tt n}' (no). -To really switch to explore mode, respond with `{\tt y}'. +Requires confirmation; default response is `\texttt{n}' (no). +To really switch to explore mode, respond with `\texttt{y}'. You can set the -{\it paranoid\textunderscore confirmation:quit\/} -option to require a response of ``{\tt yes}'' instead. +\textit{paranoid\textunderscore confirmation:quit} +option to require a response of ``\texttt{yes}'' instead. %.lp \item[\#fight] Prefix key to force fight a direction, even if you see nothing to fight there. -Default key is `{\tt F}', or `{\tt -}' with -{\it number\textunderscore pad\/} +Default key is `\texttt{F}', or `\texttt{-}' with +\textit{number\textunderscore pad} %.lp \item[\#fire] Fire ammunition from quiver, possibly autowielding a launcher, or hit with a wielded polearm. -Default key is `{\tt f}'. +Default key is `\texttt{f}'. %.lp \item[\#force] -Force a lock. Autocompletes. Default key is `{\tt M-f}'. +Force a lock. Autocompletes. Default key is `\texttt{M-f}'. %.lp \item[\#genocided] List any monster types which have been genocided. @@ -1393,27 +1392,27 @@ In explore mode and debug mode it also shows types which have become extinct. \\ %.lp "" -The display order is the same as is used by {\\tt \#vanquished}. -The `{\tt m}' prefix brings up a menu of available sorting orders, and -doing that for either {\\tt \#genocided} or {\\tt \#vanquished} changes the order for both. +The display order is the same as is used by \texttt{\#vanquished}. +The `\texttt{m}' prefix brings up a menu of available sorting orders, and +doing that for either \texttt{\#genocided} or \texttt{\#vanquished} changes the order for both. \\ %.lp "" If the sorting order is ``count high to low'' or ``count low to high'' -(which are applicable for {\tt \#vanquished}), that will be ignored -for {\tt \#genocided} and alphabetical will be used instead. -The menu omits those two choices when used for {\tt \#genocide}. +(which are applicable for \texttt{\#vanquished}), that will be ignored +for \texttt{\#genocided} and alphabetical will be used instead. +The menu omits those two choices when used for \texttt{\#genocide}. \\ %.lp "" Autocompletes. -Default key is `{\tt M-g}'. +Default key is `\texttt{M-g}'. %.lp \item[\#glance] -Show what type of thing a map symbol corresponds to. Default key is `{\tt ;}'. +Show what type of thing a map symbol corresponds to. Default key is `\texttt{;}'. %.lp \item[\#help] Show the help menu. -Default key is `{\tt ?}', -and also `{\tt h}' if {\it number\textunderscore pad\/} is on. +Default key is `\texttt{?}', +and also `\texttt{h}' if \textit{number\textunderscore pad} is on. %.lp \item[\#herecmdmenu] Show a menu of possible actions directed at your current location. @@ -1421,47 +1420,47 @@ The menu is limited to a subset of the likeliest actions, not an exhaustive set of all possibilities. Autocompletes.\\ %.lp "" -If mouse support is enabled and the {\it herecmd\textunderscore menu\/} +If mouse support is enabled and the \textit{herecmd\textunderscore menu} option is On, clicking on the hero (or steed when mounted) will execute this command. %.lp \item[\#history] -Show long version and game history. Default key is `{\tt V}'. +Show long version and game history. Default key is `\texttt{V}'. %.lp \item[\#inventory] -Show your inventory. Default key is `{\tt i}'. +Show your inventory. Default key is `\texttt{i}'. %.lp \item[\#inventtype] -Inventory specific item types. Default key is `{\tt I}'. +Inventory specific item types. Default key is `\texttt{I}'. %.lp \item[\#invoke] -Invoke an object's special powers. Autocompletes. Default key is `{\tt M-i}'. +Invoke an object's special powers. Autocompletes. Default key is `\texttt{M-i}'. %.lp \item[\#jump] Jump to another location. Autocompletes. -Default key is `{\tt M-j}', -and also `{\tt j}' if {\it number\textunderscore pad\/} is on. +Default key is `\texttt{M-j}', +and also `\texttt{j}' if \textit{number\textunderscore pad} is on. %.lp \item[\#kick] Kick something. -Default key is `{\tt \^{}D}', -and also `{\tt k}' if {\it number\textunderscore pad\/} is on. +Default key is `\texttt{\textasciicircum D}', +and also `\texttt{k}' if \textit{number\textunderscore pad} is on. %.lp \item[\#known] Show what object types have been discovered. -Default key is `{\tt $\backslash$}'. +Default key is `\texttt{\textbackslash}'. \\ %.lp "" -The `{\tt m}' prefix allows assigning a new value to the -{\it sortdiscoveries\/} +The `\texttt{m}' prefix allows assigning a new value to the +\textit{sortdiscoveries} option to control the order in which the discoveries are displayed. %.lp \item[\#knownclass] Show discovered types for one class of objects. -Default key is `{\tt `}'. +Default key is `\texttt{`}'. \\ %.lp "" -The `{\tt m}' prefix operates the same as for {\tt \#known}. +The `\texttt{m}' prefix operates the same as for \texttt{\#known}. %.lp \item[\#levelchange] Change your experience level. @@ -1474,7 +1473,7 @@ Autocompletes. Debug mode only. %.lp \item[\#look] -Look at what is here, under you. Default key is `{\tt :}'. +Look at what is here, under you. Default key is `\texttt{:}'. %.lp \item[\#lookaround] Describe what you can see, or remember, of your surroundings. @@ -1482,42 +1481,42 @@ Describe what you can see, or remember, of your surroundings. \item[\#loot] Loot a box or bag on the floor beneath you, or the saddle from a steed standing next to you. Autocompletes. -Precede with the `{\tt m}' prefix to skip containers at your location +Precede with the `\texttt{m}' prefix to skip containers at your location and go directly to removing a saddle. -Default key is `{\tt M-l}', -and also `{\tt l}' if {\it number\textunderscore pad\/} is on. +Default key is `\texttt{M-l}', +and also `\texttt{l}' if \textit{number\textunderscore pad} is on. %.lp \item[\#monster] Use a monster's special ability (when polymorphed into monster form). -Autocompletes. Default key is `{\tt M-m}'. +Autocompletes. Default key is `\texttt{M-m}'. %.lp \item[\#name] Name a monster, an individual object, or a type of object. -Same as ``{\tt \#call}''. +Same as ``\texttt{\#call}''. Autocompletes. -Default keys are `{\tt N}', `{\tt M-n}', and `{\tt M-N}'. +Default keys are `\texttt{N}', `\texttt{M-n}', and `\texttt{M-N}'. %.lp \item[\#offer] -Offer a sacrifice to the gods. Autocompletes. Default key is `{\tt M-o}'.\\ +Offer a sacrifice to the gods. Autocompletes. Default key is `\texttt{M-o}'.\\ %.lp "" You'll need to find an altar to have any chance at success. Corpses of recently killed monsters are the fodder of choice. %.lp "" -The `{\tt m}' prefix skips offering any items which are on the altar.\\ +The `\texttt{m}' prefix skips offering any items which are on the altar.\\ %.lp \item[\#open] -Open a door. Default key is `{\tt o}'. +Open a door. Default key is `\texttt{o}'. %.lp \item[\#options] -Show and change option settings. Default key is `{\tt O}'. -Precede with the {\tt m} prefix to show advanced options. +Show and change option settings. Default key is `\texttt{O}'. +Precede with the \texttt{m} prefix to show advanced options. %.lp \item[\#optionsfull] Show advanced game option settings. No default key. -Precede with the `{\tt m}' prefix to execute the simpler options command. -(Mainly useful if you use {\tt BINDING=O:optionsfull} to switch -`{\tt O}' from simple options back to traditional advanced options.) +Precede with the `\texttt{m}' prefix to execute the simpler options command. +(Mainly useful if you use \texttt{BINDING=O:optionsfull} to switch +`\texttt{O}' from simple options back to traditional advanced options.) %.lp \item[\#overview] Display information you've discovered about the dungeon. @@ -1531,7 +1530,7 @@ If dungeon overview is chosen during end-of-game disclosure, every visited level will be included regardless of annotations. \\ %.lp "" -Precede \#overview with the `{\tt m}' prefix to display the dungeon +Precede \#overview with the `\texttt{m}' prefix to display the dungeon overview as a menu where you can select any visited level to add or remove an annotation without needing to return to that level. This will also force all visited levels to be displayed rather than just @@ -1539,7 +1538,7 @@ the ``interesting'' subset. \\ %.lp "" Autocompletes. -Default keys are `{\tt \^{}O}', and `{\tt M-O}'. +Default keys are `\texttt{\textasciicircum O}', and `\texttt{M-O}'. % DON'T PANIC! %.lp \item[\#panic] @@ -1548,26 +1547,26 @@ Terminates the current game. Autocompletes. Debug mode only.\\ %.lp "" -Asks for confirmation; default is `{\tt n}' (no); continue playing. -To really panic, respond with `{\tt y}'. +Asks for confirmation; default is `\texttt{n}' (no); continue playing. +To really panic, respond with `\texttt{y}'. You can set the -{\it paranoid\textunderscore confirmation:quit\/} -option to require a response of ``{\tt yes}'' instead. +\textit{paranoid\textunderscore confirmation:quit} +option to require a response of ``\texttt{yes}'' instead. %.lp \item[\#pay] -Pay your shopping bill. Default key is `{\tt p}'. +Pay your shopping bill. Default key is `\texttt{p}'. %.lp \item[\#perminv] If persistent inventory display is supported and enabled (with the -{\it perm\textunderscore invent\/} option), interact with it instead of with the map. +\textit{perm\textunderscore invent} option), interact with it instead of with the map. You'll be prompted for menu scrolling keystrokes such -as `{\tt \verb+>+}' and `{\tt \verb+<+}'. -Press {\tt Return} or {\tt Escape} to resume normal play. -Default key is {\tt \verb+|+}. +as `\texttt{>}' and `\texttt{<}'. +Press \texttt{Return} or \texttt{Escape} to resume normal play. +Default key is \texttt{|}. %.lp \item[\#pickup] -Pick up things at the current location. Default key is `{\tt ,}'. -The `{\tt m}' prefix forces use of a menu. +Pick up things at the current location. Default key is `\texttt{,}'. +The `\texttt{m}' prefix forces use of a menu. %.lp \item[\#polyself] Polymorph self. @@ -1575,7 +1574,7 @@ Autocompletes. Debug mode only. %.lp \item[\#pray] -Pray to the gods for help. Autocompletes. Default key is `{\tt M-p}'.\\ +Pray to the gods for help. Autocompletes. Default key is `\texttt{M-p}'.\\ %.lp "" Praying too soon after receiving prior help is a bad idea. (Hint: entering the dungeon alive is treated as having received help. @@ -1583,19 +1582,19 @@ You probably shouldn't start off a new game by praying right away.) Since using this command by accident can cause trouble, there is an option to make you confirm your intent before praying. It is enabled by default, and you can reset the -{\it paranoid\textunderscore confirmation\/} +\textit{paranoid\textunderscore confirmation} option to disable it. %.lp \item[\#prevmsg] -Show previously displayed game messages. Default key is `{\tt \^{}P}'. +Show previously displayed game messages. Default key is `\texttt{\textasciicircum P}'. %.lp \item[\#puton] -Put on an accessory (ring, amulet, etc). Default key is `{\tt P}'. +Put on an accessory (ring, amulet, etc). Default key is `\texttt{P}'. %.lp \item[\#quaff] -Quaff (drink) something. Default key is `{\tt q}'.\\ +Quaff (drink) something. Default key is `\texttt{q}'.\\ %.lp "" -The {\tt m} prefix skips drinking from a fountain or sink if there +The \texttt{m} prefix skips drinking from a fountain or sink if there is one at your location. %.lp \item[\#quit] @@ -1603,70 +1602,70 @@ Quit the program without saving your game. Autocompletes.\\ %.lp "" Since using this command by accident would throw away the current game, you are asked to confirm your intent before quitting. -Default response is `{\tt n}' (no); continue playing. -To really quit, respond with `{\tt y}'. +Default response is `\texttt{n}' (no); continue playing. +To really quit, respond with `\texttt{y}'. You can set the -{\it paranoid\textunderscore confirmation:quit\/} -option to require a response of ``{\tt yes}'' instead. +\textit{paranoid\textunderscore confirmation:quit} +option to require a response of ``\texttt{yes}'' instead. %.lp \item[\#quiver] -Select ammunition for quiver. Default key is `{\tt Q}'. +Select ammunition for quiver. Default key is `\texttt{Q}'. %.lp \item[\#read] -Read a scroll, a spellbook, or something else. Default key is `{\tt r}'. +Read a scroll, a spellbook, or something else. Default key is `\texttt{r}'. %.lp \item[\#redraw] Redraw the screen. -Default key is `{\tt \^{}R}', -and also `{\tt \^{}L}' if {\it number\textunderscore pad\/} is on. +Default key is `\texttt{\textasciicircum R}', +and also `\texttt{\textasciicircum L}' if \textit{number\textunderscore pad} is on. %.lp \item[\#remove] -Remove an accessory (ring, amulet, etc). Default key is `{\tt R}'. +Remove an accessory (ring, amulet, etc). Default key is `\texttt{R}'. %.lp \item[\#repeat] Repeat the previous command. -Default key is~`{\tt \^{}A}'. +Default key is~`\texttt{\textasciicircum A}'. %.lp \item[\#reqmenu] Prefix key to modify the behavior or request menu from some commands. Prevents autopickup when used with movement commands. -Default key is `{\tt m}'. +Default key is `\texttt{m}'. %.lp \item[\#retravel] Travel to a previously selected travel destination. -Default key is `{\tt C-\textunderscore }'. -See also {\tt \#travel}. +Default key is `\texttt{C-\textunderscore }'. +See also \texttt{\#travel}. %.lp \item[\#ride] Ride (or stop riding) a saddled creature. Autocompletes. -Default key is `{\tt M-R}'. +Default key is `\texttt{M-R}'. %.lp \item[\#rub] -Rub a lamp or a stone. Autocompletes. Default key is `{\tt M-r}'. +Rub a lamp or a stone. Autocompletes. Default key is `\texttt{M-r}'. %.lp \item[\#run] Prefix key to run towards a direction. -Default key is `{\tt G}' when -{\it number\textunderscore pad\/} +Default key is `\texttt{G}' when +\textit{number\textunderscore pad} is off, -`{\tt 5}' when -{\it number\textunderscore pad\/} +`\texttt{5}' when +\textit{number\textunderscore pad} is set to 1~or~3, -otherwise `{\tt M-5}' when it is set to 2~or~4. +otherwise `\texttt{M-5}' when it is set to 2~or~4. %.lp \item[\#rush] Prefix key to rush towards a direction. -Default key is `{\tt g}' when -{\it number\textunderscore pad\/} +Default key is `\texttt{g}' when +\textit{number\textunderscore pad} is off, -`{\tt M-5}' when -{\it number\textunderscore pad\/} +`\texttt{M-5}' when +\textit{number\textunderscore pad} is set to 1~or~3, -otherwise `{\tt 5}' when it is set to 2~or~4. +otherwise `\texttt{5}' when it is set to 2~or~4. %.lp \item[\#save] Save the game and exit the program. -Default key is `{\tt S}'. +Default key is `\texttt{S}'. %.lp \item[\#saveoptions] Save configuration options to the config file. @@ -1674,49 +1673,49 @@ This will overwrite the file, removing all comments, so if you have manually edited the config file, don't use this. %.lp \item[\#search] -Search for traps and secret doors around you. Default key is `{\tt s}'. +Search for traps and secret doors around you. Default key is `\texttt{s}'. %.lp \item[\#seeall] -Show all equipment in use. Default key is `{\tt *}'. +Show all equipment in use. Default key is `\texttt{*}'. %.lp "" \\ Will display in-use items in a menu even when there is only one. %.lp \item[\#seeamulet] -Show the amulet currently worn. Default key is `{\tt "}'. +Show the amulet currently worn. Default key is `\texttt{"}'. %.lp "" \\ -Using the `{\tt m}' prefix will force the display of a worn +Using the `\texttt{m}' prefix will force the display of a worn amulet in a menu rather than with just a message. %.lp \item[\#seearmor] -Show the armor currently worn. Default key is `{\tt [}'. +Show the armor currently worn. Default key is `\texttt{[}'. %.lp "" \\ Will display worn armor in a menu even when there is only thing worn. %.lp \item[\#seerings] -Show the ring(s) currently worn. Default key is `{\tt =}'. +Show the ring(s) currently worn. Default key is `\texttt{=}'. %.lp "" Will display worn rings in a menu if there are two (or there is just one and is a meat ring rather than a ``real'' ring). -Use the `{\tt m}' prefix to force a menu for one ring. +Use the `\texttt{m}' prefix to force a menu for one ring. %.lp \item[\#seetools] -Show the tools currently in use. Default key is `{\tt (}'. +Show the tools currently in use. Default key is `\texttt{(}'. %.lp "" Will display the result in a message if there is one tool in use (worn blindfold or towel or lenses, lit lamp(s) and/or candle(s), leashes attached to pets). Will display a menu if there are more than one or if the command is -preceded by the `{\tt m}' prefix. +preceded by the `\texttt{m}' prefix. %.lp \item[\#seeweapon] -Show the weapon currently wielded. Default key is `{\tt )}'. +Show the weapon currently wielded. Default key is `\texttt{)}'. %.lp "" If dual-wielding, a separate message about the secondary weapon will be given. -Using the `{\tt m}' prefix will force a menu and it will include +Using the `\texttt{m}' prefix will force a menu and it will include primary weapon, alternate weapon even when not dual-wielding, and also whatever is currently assigned to the quiver slot. %.lp @@ -1725,29 +1724,29 @@ Do a shell escape, switching from NetHack to a subprocess. Can be disabled at the time the program is built. When enabled, access for specific users can be controlled by the system configuration file. -Use the shell command `{\tt exit}' to return to the game. -Default key is `{\tt !}'. +Use the shell command `\texttt{exit}' to return to the game. +Default key is `\texttt{!}'. %.lp \item[\#showgold] Report the gold in your inventory, including gold you know about in containers you're carrying. If you are inside a shop, report any credit or debt you have in that shop. -Default key is `{\tt \$}'. +Default key is `\texttt{\$}'. %.lp \item[\#showspells] List and reorder known spells. -Default key is `{\tt +}'. +Default key is `\texttt{+}'. %.lp \item[\#showtrap] Describe an adjacent trap, possibly covered by objects or a monster. To be eligible, the trap must already be discovered. -(The ``{\tt \#terrain}'' command can display your map with all objects and +(The ``\texttt{\#terrain}'' command can display your map with all objects and monsters temporarily removed, making it possible to see all discovered traps.) -Default key is `{\tt \^{}}'. +Default key is `\texttt{\textasciicircum }'. %.lp \item[\#sit] -Sit down. Autocompletes. Default key is `{\tt M-s}'. +Sit down. Autocompletes. Default key is `\texttt{M-s}'. %.lp \item[\#stats] Show memory usage statistics. @@ -1758,23 +1757,23 @@ Debug mode only. Suspend the game, switching from NetHack to the terminal it was started from without performing save-and-exit. Can be disabled at the time the program is built. -When enabled, mainly useful for {\it tty\/} and {\it curses\/} interfaces on +When enabled, mainly useful for \textit{tty} and \textit{curses} interfaces on %.UX \. \" yields "UNIX." UNIX. -Use the shell command `{\tt fg}' to return to the game. -Default key is `{\tt \^{}Z}'. +Use the shell command `\texttt{fg}' to return to the game. +Default key is `\texttt{\textasciicircum Z}'. %.lp \item[\#swap] -Swap wielded and secondary weapons. Default key is `{\tt x}'. +Swap wielded and secondary weapons. Default key is `\texttt{x}'. %.lp \item[\#takeoff] -Take off one piece of armor. Default key is `{\tt T}'. +Take off one piece of armor. Default key is `\texttt{T}'. %.lp \item[\#takeoffall] -Remove all armor. Default key is `{\tt A}'. +Remove all armor. Default key is `\texttt{A}'. %.lp \item[\#teleport] -Teleport around the level. Default key is `{\tt \^{}T}'. +Teleport around the level. Default key is `\texttt{\textasciicircum T}'. %.lp \item[\#terrain] Show map without obstructions. @@ -1790,7 +1789,7 @@ its explored portion. In debug mode there are additional choices.\\ %.lp "" Autocompletes. -Default key is `{\tt }' or `{\tt }' (see {\it Del\/} above). +Default key is `\texttt{}' or `\texttt{}' (see \textit{Del} above). %.lp \item[\#therecmdmenu] Show a menu of possible actions directed at a location next to you. @@ -1800,11 +1799,11 @@ Autocompletes. %%--invoking it by mouse seems to be broken %% \\ %% .lp "" -%% If mouse support is enabled and the {\it herecmd\textunderscore menu\/} +%% If mouse support is enabled and the \textit{herecmd\textunderscore menu} %% option is On, clicking on an adjacent location will execute this command. %.lp \item[\#throw] -Throw something. Default key is `{\tt t}'. +Throw something. Default key is `\texttt{t}'. %.lp \item[\#timeout] Look at the timeout queue. @@ -1819,32 +1818,32 @@ of them or ``tip something being carried''. %.lp "" If the latter is chosen, there will be another prompt for which item from inventory to tip. -The `{\tt m}' prefix makes the command skip containers on the +The `\texttt{m}' prefix makes the command skip containers on the floor and pick one from inventory, except for the special case of -{\it menustyle:Traditional\/} +\textit{menustyle:Traditional} with two or more containers present; that situation will start with the floor container menu. \\ %.lp "" -Autocompletes. Default key is `{\tt M-T}'. +Autocompletes. Default key is `\texttt{M-T}'. %.lp \item[\#travel] Travel to a specific location on the map. -Default key is `{\tt \textunderscore }'. +Default key is `\texttt{\textunderscore }'. Using the ``request menu'' prefix shows a menu of interesting targets in sight without asking to move the cursor. -When picking a target with cursor and the {\it autodescribe\/} +When picking a target with cursor and the \textit{autodescribe} option is on, the top line will show ``(no travel path)'' if your character does not know of a path to that location. -See also {\tt \#retravel}. +See also \texttt{\#retravel}. %.lp \item[\#turn] -Turn undead away. Autocompletes. Default key is `{\tt M-t}'. +Turn undead away. Autocompletes. Default key is `\texttt{M-t}'. %.lp \item[\#twoweapon] Toggle two-weapon combat on or off. Autocompletes. -Default key is `{\tt X}', -and also `{\tt M-2}' if {\it number\textunderscore pad\/} is off.\\ +Default key is `\texttt{X}', +and also `\texttt{M-2}' if \textit{number\textunderscore pad} is off.\\ %.lp "" Note that you must use suitable weapons for this type of combat, or it will @@ -1852,12 +1851,12 @@ be automatically turned off. %.lp \item[\#untrap] Untrap something (trap, door, or chest). -Default key is `{\tt M-u}', and `{\tt u}' if {\it number\textunderscore pad\/} is on.\\ +Default key is `\texttt{M-u}', and `\texttt{u}' if \textit{number\textunderscore pad} is on.\\ %.lp "" In some circumstances it can also be used to rescue trapped monsters. %.lp \item[\#up] -Go up a staircase. Default key is `{\tt <}'. +Go up a staircase. Default key is `\texttt{<}'. %.lp \item[\#vanquished] List vanquished monsters by type and count. @@ -1873,33 +1872,33 @@ on the map. Using the ``request menu'' prefix prior to \#vanquished brings up a menu of sorting orders available (provided that the vanquished monsters list contains at least two types of monsters). -Whichever ordering is picked gets assigned to the {\it sortvanquished} +Whichever ordering is picked gets assigned to the \textit{sortvanquished} option so is remembered for subsequent \#vanquished requests. -The {\tt \#genocided} command shares this sorting order. +The \texttt{\#genocided} command shares this sorting order. \\ %.lp "" During end-of-game disclosure, when asked whether to show vanquished -monsters answering `{\tt a}' will let you choose from the sort menu. +monsters answering `\texttt{a}' will let you choose from the sort menu. \\ %.lp "" Autocompletes. -Default key is `{\tt M-V}'. +Default key is `\texttt{M-V}'. %.lp \item[\#version] -Print compile time options for this version of {\it NetHack\/}. +Print compile time options for this version of \textit{NetHack}. %.lp The second paragraph lists the user interface(s) that are included. -If there are more than one, you can use the {\it windowtype\/} +If there are more than one, you can use the \textit{windowtype} option in your run-time configuration file to select the one you want. %.lp -Autocompletes. Default key is `{\tt M-v}'. +Autocompletes. Default key is `\texttt{M-v}'. %.lp \item[\#versionshort] Show the program's version number, plus the date and time that the running copy was built from sources (not the version's release date). -Default key is `{\tt v}'. +Default key is `\texttt{v}'. %.lp \item[\#vision] Show vision array. @@ -1908,23 +1907,23 @@ Debug mode only. %.lp \item[\#wait] Rest one move while doing nothing. -Default key is `{\tt .}', and also `{\tt{ }}' if -{\it rest\textunderscore on\textunderscore space\/} is on. +Default key is `\texttt{.}', and also `{\tt{ }}' if +\textit{rest\textunderscore on\textunderscore space} is on. %.lp \item[\#wear] -Wear a piece of armor. Default key is `{\tt W}'. +Wear a piece of armor. Default key is `\texttt{W}'. %.lp \item[\#whatdoes] -Tell what a key does. Default key is `{\tt \&}'. +Tell what a key does. Default key is `\texttt{\&}'. %.lp \item[\#whatis] -Show what type of thing a symbol corresponds to. Default key is `{\tt /}'. +Show what type of thing a symbol corresponds to. Default key is `\texttt{/}'. %.lp \item[\#wield] -Wield a weapon. Default key is `{\tt w}'. +Wield a weapon. Default key is `\texttt{w}'. %.lp \item[\#wipe] -Wipe off your face. Autocompletes. Default key is `{\tt M-w}'. +Wipe off your face. Autocompletes. Default key is `\texttt{M-w}'. %.lp \item[\#wizborn] Show monster birth, death, genocide, and extinct statistics. @@ -1945,20 +1944,20 @@ within a modest radius. No time elapses. Autocompletes. Debug mode only. -Default key is `{\tt \^{}E}'. +Default key is `\texttt{\textasciicircum E}'. %.lp \item[\#wizgenesis] Create a monster. May be prefixed by a count to create more than one. Autocompletes. Debug mode only. -Default key is `{\tt \^{}G}'. +Default key is `\texttt{\textasciicircum G}'. %.lp \item[\#wizidentify] Identify all items in inventory. Autocompletes. Debug mode only. -Default key is `{\tt \^{}I}'. +Default key is `\texttt{\textasciicircum I}'. %.lp \item[\#wizintrinsic] Set one or more intrinsic attributes. @@ -1968,7 +1967,7 @@ Debug mode only. \item[\#wizkill] Remove monsters from play by just pointing at them. By default the hero gets credit or blame for killing the targets. -Precede this command with the `{\tt m}' prefix to override that. +Precede this command with the `\texttt{m}' prefix to override that. Autocompletes. Debug mode only. %.lp @@ -1976,13 +1975,13 @@ Debug mode only. Teleport to another level. Autocompletes. Debug mode only. -Default key is `{\tt \^{}V}'. +Default key is `\texttt{\textasciicircum V}'. %.lp \item[\#wizmap] Map the level. Autocompletes. Debug mode only. -Default key is `{\tt \^{}F}'. +Default key is `\texttt{\textasciicircum F}'. %.lp \item[\#wizrumorcheck] Verify rumor boundaries by displaying first and last true rumors and @@ -2013,7 +2012,7 @@ Debug mode only. Wish for something. Autocompletes. Debug mode only. -Default key is `{\tt \^{}W}'. +Default key is `\texttt{\textasciicircum W}'. %.lp \item[\#wmode] Show wall modes. @@ -2021,7 +2020,7 @@ Autocompletes. Debug mode only. %.lp \item[\#zap] -Zap a wand. Default key is `{\tt z}'. +Zap a wand. Default key is `\texttt{z}'. %.lp \item[\#?] Help menu: get the list of available extended commands. @@ -2033,32 +2032,32 @@ with another key, modifies it by setting the `meta' [8th, or `high'] bit), you can invoke many extended commands by meta-ing the first letter of the command. -On {\it Windows\/} and {\it MS-DOS\/}, +On \textit{Windows} and \textit{MS-DOS}, the `Alt' key can be used in this fashion. On other systems, if typing `Alt' plus another key transmits a -two character sequence consisting of an {\tt Escape} -followed by the other key, you may set the {\it altmeta\/} -option to have {\it NetHack\/} combine them into {\tt meta+}. +two character sequence consisting of an \texttt{Escape} +followed by the other key, you may set the \textit{altmeta} +option to have \textit{NetHack} combine them into \texttt{meta+}. (This combining action only takes place when NetHack is expecting a command to execute, not when accepting input to name something or to make a wish.) %.pg -Unlike control characters, where {\tt \^{}x} and {\tt \^{}X} denote the same -thing, meta characters are case-sensitive: {\tt M-x} and {\tt M-X} +Unlike control characters, where \texttt{\textasciicircum x} and \texttt{\textasciicircum X} denote the same +thing, meta characters are case-sensitive: \texttt{M-x} and \texttt{M-X} represent different things. Some commands which can be run via a meta character require that the letter be capitalized because the lower-case equivalent is used for another command, so the three key combination -{\tt meta+Shift+letter} is needed. +\texttt{meta+Shift+letter} is needed. %.BR 1 -\begin{description}[font=\ttfamily] +\begin{description}[font=\mdseries\ttfamily] %.lp \item[M-?] {\tt\#?} (not supported by all platforms) %.lp \item[M-2] -{\tt\#twoweapon} (unless the {\it number\textunderscore pad\/} option is enabled) +{\tt\#twoweapon} (unless the \textit{number\textunderscore pad} option is enabled) %.lp \item[M-a] {\tt\#adjust} @@ -2140,9 +2139,9 @@ equivalent is used for another command, so the three key combination \end{description} %.pg -\noindent If the {\it number\textunderscore pad\/} option is on, some additional letter commands +\noindent If the \textit{number\textunderscore pad} option is on, some additional letter commands are available: -\begin{description}[font=\ttfamily] +\begin{description}[font=\mdseries\ttfamily] %.lp \item[h] {\tt\#help} @@ -2176,7 +2175,7 @@ Walls and corridors remain on the map as you explore them. %.pg Secret corridors are hidden and appear to be solid rock. -You can find them with the `{\tt s}' (search) command when adjacent +You can find them with the `\texttt{s}' (search) command when adjacent to them. Multiple search attempts may be needed. When searching is successful, secret corridors become ordinary open @@ -2191,16 +2190,16 @@ corridors and shows them as such. Doorways connect rooms and corridors. Some doorways have no doors; you can walk right through. Others have doors in them, which may be open, closed, or locked. -To open a closed door, use the `{\tt o}' (open) -command; to close it again, use the `{\tt c}' (close) command. +To open a closed door, use the `\texttt{o}' (open) +command; to close it again, use the `\texttt{c}' (close) command. By default the -{\it autoopen} +\textit{autoopen} option is enabled, so simply attempting to walk onto a closed door's -location will attempt to open it without needing `{\tt o}'. +location will attempt to open it without needing `\texttt{o}'. Opening via -{\it autoopen} -will not work if you are {\it confused\/} or {\it stunned\/} or suffer from -the {\it fumbling\/} attribute. +\textit{autoopen} +will not work if you are \textit{confused} or \textit{stunned} or suffer from +the \textit{fumbling} attribute. %.pg Open doors cannot be entered diagonally; you must approach them @@ -2208,20 +2207,20 @@ straight on, horizontally or vertically. Doorways without doors are not restricted in this fashion except on one particular level %.\" the rogue level -(described by ``{\tt \#overview}'' as ``a primitive area''). +(described by ``\texttt{\#overview}'' as ``a primitive area''). %.pg Unlocking magic exists but usually won't be available early on. You can get through a locked door without magic by first using an -unlocking tool with the `{\tt a}' (apply) command, and then opening it. +unlocking tool with the `\texttt{a}' (apply) command, and then opening it. By default the -{\it autounlock} -option is also enabled, so if you attempt to open (via `{\tt o}' or -{\it autoopen}) +\textit{autounlock} +option is also enabled, so if you attempt to open (via `\texttt{o}' or +\textit{autoopen}) a locked door while carrying an unlocking tool, you'll be asked whether to use it on the door's lock. Alternatively, you can break a closed door (whether locked or not) down -by kicking it via the ``{\tt \^{}D}'' (kick) command. +by kicking it via the ``\texttt{\textasciicircum D}'' (kick) command. Kicking down a door destroys it and makes a lot of noise which might wake sleeping monsters. @@ -2229,7 +2228,7 @@ wake sleeping monsters. Some closed doors are booby-trapped and will explode if an attempt is made to open (when unlocked) or unlock (when locked) or kick down. Like kicking, an explosion destroys the door and makes a lot of noise. -The ``{\tt \#untrap}'' command can be used to search a door for traps but +The ``\texttt{\#untrap}'' command can be used to search a door for traps but might take multiple attempts to find one. When one is found, you'll be asked whether to try to disarm it. If you accede, success will eliminate the trap but @@ -2247,13 +2246,13 @@ And some (giants) can smash doors. %.pg Secret doors are hidden and appear to be ordinary wall (from inside a room) or solid rock (from outside). -You can find them with the `{\tt s}' (search) command but it might +You can find them with the `\texttt{s}' (search) command but it might take multiple tries (possibly many tries if your luck is poor). Once found they are in all ways equivalent to normal doors. Mapping magic does not reveal secret doors. %.hn 2 -\subsection*{Traps (`{\tt \^{}}')} +\subsection*{Traps (`\texttt{\textasciicircum }')} %.pg There are traps throughout the dungeon to snare the unwary intruder. @@ -2261,9 +2260,9 @@ For example, you may suddenly fall into a pit and be stuck for a few turns trying to climb out (see below). A trap usually won't appear on your map until you trigger it by moving onto it, you see someone else trigger it, or you discover it with -the `{\tt s}' (search) command (multiple attempts are often needed; +the `\texttt{s}' (search) command (multiple attempts are often needed; if your luck is poor, many attempts might be needed). -{\it Wands of secret door detection\/} and the spell of {\it detect unseen} +\textit{Wands of secret door detection} and the spell of \textit{detect unseen} also reveal traps within a modest radius but only if the trap is also within line-of-sight (whether you can see at the time or not). There is also other magic which can reveal traps. @@ -2300,7 +2299,7 @@ necessarily to a specific location there. %.pg There is a special multi-level branch of the dungeon with pre-mapped levels -based on the classic computer game ``{\it Sokoban}.'' +based on the classic computer game ``\textit{Sokoban}.'' In that game, you operate as a warehouse worker who pushes crates around obstacles to position them at designated locations. In NetHack, the goal is to push boulders into pits or holes until those @@ -2317,24 +2316,24 @@ With careful foresight, it is possible to complete all of the levels according to the traditional rules of Sokoban. (Hint: to solve Sokoban puzzles, you often need to move things away from their eventual destinations in order to open up more room to maneuver.) -Since NetHack does not support an {\it undo\/} capability, some allowances +Since NetHack does not support an \textit{undo} capability, some allowances are permitted in case you get stuck. For example, each level has at least one extra boulder. Also, it is possible to drop everything in order to be able to squeeze into the same location as a boulder (and then presumably move past it), or to destroy a boulder with magic or tools, or to create new boulders -with a {\it scroll of earth}. +with a \textit{scroll of earth}. However, doing such things will lower your luck without any specific message given about that. -See the {\it Conduct\/} section for information about getting feedback for +See the \textit{Conduct} section for information about getting feedback for your actions in Sokoban. %.hn 2 -\subsection*{Stairs and ladders (`{\tt <}', `{\tt >}')} +\subsection*{Stairs and ladders (`\texttt{<}', `\texttt{>}')} %.pg In general, each level in the dungeon will have a staircase going up -(`{\tt <}') to the previous level and another going down (`{\tt >}') +(`\texttt{<}') to the previous level and another going down (`\texttt{>}') to the next level. There are some exceptions though. For instance, fairly early in the dungeon you will find a level with two down staircases, one @@ -2373,8 +2372,8 @@ inter-level connections are nearly indistinguishable during game play. %.pg Occasionally you will run across a room with a shopkeeper near the door and many items lying on the floor. You can buy items by picking them -up and then using the `{\tt p}' command. You can inquire about the price -of an item prior to picking it up by using the ``{\tt \#chat}'' command +up and then using the `\texttt{p}' command. You can inquire about the price +of an item prior to picking it up by using the ``\texttt{\#chat}'' command while standing on it. Using an item prior to paying for it will incur a charge, and the shopkeeper won't allow you to leave the shop until you have paid any debt you owe. @@ -2402,11 +2401,11 @@ find a ``credit card'' in the dungeon, don't bother trying to use it in shops; shopkeepers will not accept it.) %.pg -The {\tt \$} command, which reports the amount of gold you are carrying, +The \texttt{\$} command, which reports the amount of gold you are carrying, will also show current shop debt or credit, if any. -The {\tt Iu} command lists unpaid items (those which still belong to the +The \texttt{Iu} command lists unpaid items (those which still belong to the shop) if you are carrying any. -The {\tt Ix} command shows an inventory-like display of any unpaid items +The \texttt{Ix} command shows an inventory-like display of any unpaid items which have been used up, along with other shop fees, if any. %.hn 3 @@ -2448,29 +2447,29 @@ There are several options which can be used to augment the normal feedback. %.pg The -{\it pile\textunderscore limit\/} +\textit{pile\textunderscore limit} option controls how many objects can be in a pile---sharing the same map location---for the game to state ``there are objects here'' instead of listing them. -The default is {\tt 5}. -Setting it to {\tt 1} would always give that message instead of listing +The default is \texttt{5}. +Setting it to \texttt{1} would always give that message instead of listing any objects. -Setting it to {\tt 0} is a special case which will always list all +Setting it to \texttt{0} is a special case which will always list all objects no matter how big a pile is. Note that the number refers to the count of separate stacks of objects present rather than the sum of the quantities of those stacks (so -{\tt 7 arrows} or {\tt 25 gold pieces} will each count as 1 rather +\texttt{7 arrows} or \texttt{25 gold pieces} will each count as 1 rather than as 7 and 25, respectively, and total to 2 when both are at the same location). %.pg -The {\tt nopickup} command prefix (default `{\tt m}') can be +The \texttt{nopickup} command prefix (default `\texttt{m}') can be used before a movement direction to step on objects without attempting auto-pickup and without giving feedback about them. %.pg The -{\it mention\textunderscore walls\/} +\textit{mention\textunderscore walls} option controls whether you get feedback if you try to walk into a wall or solid stone or off the edge of the map. Normally nothing happens (unless the hero is blind and no wall is shown, @@ -2480,7 +2479,7 @@ some non-obvious reason. %.pg The -{\it mention\textunderscore decor\/} +\textit{mention\textunderscore decor} option controls whether you get feedback when walking on ``furniture.'' Normally stepping onto stairs or a fountain or an altar or various other things doesn't elicit anything unless it is covered by one or more objects @@ -2499,27 +2498,27 @@ case the back on land circumstance is implied. %.pg The -{\it confirm\/} +\textit{confirm} and -{\it safe\textunderscore pet\/} +\textit{safe\textunderscore pet} options control what happens when you try to move onto a peaceful monster's spot or a tame one's spot. %.\" getting away from "Movement feedback" here; oh well... %.pg -The {\tt nopickup} command prefix (default `{\tt m}') is +The \texttt{nopickup} command prefix (default `\texttt{m}') is also the move-without-attacking prefix and can be used to try to step onto a visible monster's spot without the move being considered an attack -(see the {\it Fighting\/} subsection of {\it Monsters\/} below). -The `{\tt fight}' command prefix (default `{\tt F}'; -also `{\tt -}' if -{\it number\textunderscore pad\/} +(see the \textit{Fighting} subsection of \textit{Monsters} below). +The `\texttt{fight}' command prefix (default `\texttt{F}'; +also `\texttt{-}' if +\textit{number\textunderscore pad} is on) can be used to force an attack, when guessing where an unseen monster is or when deliberately attacking a peaceful or tame creature. %.pg The -{\it run\textunderscore mode} +\textit{run\textunderscore mode} option controls how frequently the map gets redrawn when moving more than one step in a single command (so when rushing, running, or traveling). @@ -2528,13 +2527,13 @@ than one step in a single command (so when rushing, running, or traveling). %.pg One dungeon level (occurring in mid to late teens of the main dungeon) -is a tribute to the ancestor game {\it hack}'s inspiration {\it rogue}. +is a tribute to the ancestor game \textit{hack}'s inspiration \textit{rogue}. %.pg It is usually displayed differently from other levels: possibly in characters instead of tiles, or without line-drawing symbols if already -in characters; also, gold is shown as {\tt *} rather than {\tt \verb+$+} -and stairs are shown as {\tt \verb+%+} rather than {\tt <} and {\tt >}. +in characters; also, gold is shown as \texttt{*} rather than \texttt{\textdollar} +and stairs are shown as \texttt{\%} rather than \texttt{<} and \texttt{>}. There are some minor differences in actual game play: doorways lack doors; a scroll, wand, or spell of light used in a room lights up the whole room rather than within a radius around your character. @@ -2554,16 +2553,16 @@ help you locate them before they locate you (which some monsters can do very well). %.pg -The commands `{\tt /}' and `{\tt ;}' may be used to obtain information +The commands `\texttt{/}' and `\texttt{;}' may be used to obtain information about those -monsters who are displayed on the screen. The command ``{\tt \#name}'' -(by default bound to `{\tt C}'), allows you +monsters who are displayed on the screen. The command ``\texttt{\#name}'' +(by default bound to `\texttt{C}'), allows you to assign a name to a monster, which may be useful to help distinguish one from another when multiple monsters are present. Assigning a name which is just a space will remove any prior name. %.pg -The extended command ``{\tt \#chat}'' can be used to interact with an adjacent +The extended command ``\texttt{\#chat}'' can be used to interact with an adjacent monster. There is no actual dialog (in other words, you don't get to choose what you'll say), but chatting with some monsters such as a shopkeeper or the Oracle of Delphi can produce useful results. @@ -2580,27 +2579,27 @@ Remember: discretion is the better part of valor. %.pg In most circumstances, if you attempt to attack a peaceful monster by moving into its location, you'll be asked to confirm your intent. By -default an answer of `{\tt y}' acknowledges that intent, -which can be error prone if you're using `{\tt y}' to move. You can set the -{\it paranoid\textunderscore confirmation:attack\/} -option to require a response of ``{\tt yes}'' instead. +default an answer of `\texttt{y}' acknowledges that intent, +which can be error prone if you're using `\texttt{y}' to move. You can set the +\textit{paranoid\textunderscore confirmation:attack} +option to require a response of ``\texttt{yes}'' instead. %.pg If you can't see a monster (if it is invisible, or if you are blinded), -the symbol `{\tt I}' will be shown when you learn of its presence. +the symbol `\texttt{I}' will be shown when you learn of its presence. If you attempt to walk into it, you will try to fight it just like a monster that you can see; of course, if the monster has moved, you will attack empty air. If you guess that the monster has moved and you don't wish to fight, you can use the -`{\tt m}' command to move without fighting; likewise, if you don't remember -a monster but want to try fighting anyway, you can use the `{\tt F}' command. +`\texttt{m}' command to move without fighting; likewise, if you don't remember +a monster but want to try fighting anyway, you can use the `\texttt{F}' command. %.hn 2 \subsection*{Your pet} %.pg -You start the game with a little dog (`{\tt d}'), kitten (`{\tt f}'), -or pony (`{\tt u}'), which follows +You start the game with a little dog (`\texttt{d}'), kitten (`\texttt{f}'), +or pony (`\texttt{u}'), which follows you about the dungeon and fights monsters with you. Like you, your pet needs food to survive. Dogs and cats usually feed themselves on fresh carrion and other meats; @@ -2633,22 +2632,22 @@ have the right equipment and skill. Convincing a wild beast to let you saddle it up is difficult to say the least. Many a dungeoneer has had to resort to magic and wizardry in order to forge the alliance. Once you do have the beast under your control however, you can -easily climb in and out of the saddle with the ``{\tt \#ride}'' command. Lead +easily climb in and out of the saddle with the ``\texttt{\#ride}'' command. Lead the beast around the dungeon when riding, in the same manner as you would move yourself. It is the beast that you will see displayed on the map. %.pg -Riding skill is managed by the ``{\tt \#enhance}'' command. See the section +Riding skill is managed by the ``\texttt{\#enhance}'' command. See the section on Weapon proficiency for more information about that. %.pg -Use the `{\tt a}' (apply) command and pick a saddle in your inventory to +Use the `\texttt{a}' (apply) command and pick a saddle in your inventory to attempt to put that saddle on an adjacent creature. If successful, it will be transferred to that creature's inventory. %.pg -Use the ``{\tt \#loot}'' command while adjacent to a saddled creature to +Use the ``\texttt{\#loot}'' command while adjacent to a saddled creature to try to remove the saddle from that creature. If successful, it will be transferred to your inventory. @@ -2689,11 +2688,11 @@ location ordinarily wouldn't be seen any more. %.pg When you find something in the dungeon, it is common to want to pick -it up. In {\it NetHack}, this is accomplished by using the `{\tt ,}' command. -If {\it autopickup\/} option is on, you will automatically pick up the object -by walking over it, unless you move with the `{\tt m}' prefix. +it up. In \textit{NetHack}, this is accomplished by using the `\texttt{,}' command. +If \textit{autopickup} option is on, you will automatically pick up the object +by walking over it, unless you move with the `\texttt{m}' prefix. %.pg -If you're carrying too many items, {\it NetHack\/} will tell you so and you +If you're carrying too many items, \textit{NetHack} will tell you so and you won't be able to pick up anything more. Otherwise, it will add the object(s) to your pack and tell you what you just picked up. %.pg @@ -2708,16 +2707,16 @@ will get slower and you'll burn calories faster, requiring food more frequently to cope with it. Eventually, you'll be so overloaded that you'll either have to discard some of what you're carrying or collapse under its weight. %.pg -{\it NetHack\/} will tell you how badly you have loaded yourself. +\textit{NetHack} will tell you how badly you have loaded yourself. If you are encumbered, one of the conditions -{\tt Burdened}, {\tt Stressed}, {\tt Strained}, -{\tt Overtaxed}, or {\tt Overloaded} will be +\texttt{Burdened}, \texttt{Stressed}, \texttt{Strained}, +\texttt{Overtaxed}, or \texttt{Overloaded} will be shown on the bottom line status display. %.pg When you pick up an object, it is assigned an inventory letter. Many commands that operate on objects must ask you to find out which object -you want to use. When {\it NetHack\/} asks you to choose a particular object +you want to use. When \textit{NetHack} asks you to choose a particular object you are carrying, you are usually presented with a list of inventory letters to choose from (see Commands, above). @@ -2728,13 +2727,13 @@ type. During a game, any two objects with the same description are the same type. However, the descriptions will vary from game to game. %.pg -When you use one of these objects, if its effect is obvious, {\it NetHack\/} +When you use one of these objects, if its effect is obvious, \textit{NetHack} will remember what it is for you. If its effect isn't extremely obvious, you will be asked what you want to call this type of object -so you will recognize it later. You can also use the ``{\tt \#name}'' +so you will recognize it later. You can also use the ``\texttt{\#name}'' command, for the same purpose at any time, to name all objects of a particular type or just an individual object. -When you use ``{\tt \#name}'' on an object which has already been named, +When you use ``\texttt{\#name}'' on an object which has already been named, specifying a space as the value will remove the prior name instead of assigning a new one. @@ -2776,23 +2775,23 @@ provided that you can see them land. %.pg An item with unknown status will be reported in your inventory with no prefix. An item which you know the state of will be distinguished in your inventory -by the presence of the word {\tt cursed}, {\tt uncursed} or -{\tt blessed} in the description of the item. -In some cases {\tt uncursed} will be omitted as being redundant when +by the presence of the word \texttt{cursed}, \texttt{uncursed} or +\texttt{blessed} in the description of the item. +In some cases \texttt{uncursed} will be omitted as being redundant when enough other information is displayed. The -{\it implicit\textunderscore uncursed\/} -option can be used to control this; toggle it off to have {\tt uncursed} +\textit{implicit\textunderscore uncursed} +option can be used to control this; toggle it off to have \texttt{uncursed} be displayed even when that can be deduced from other attributes. %.pg Sometimes the bless or curse state of objects is referred to as their -``{\tt BUC}'' attribute, for Blessed, Uncursed, or Cursed state, -or ``{\tt BUCX}'' for Blessed, Uncursed, Cursed, or unknown. -(The term {\it beatitude\/} is occasionally used as well.) +``\texttt{BUC}'' attribute, for Blessed, Uncursed, or Cursed state, +or ``\texttt{BUCX}'' for Blessed, Uncursed, Cursed, or unknown. +(The term \textit{beatitude} is occasionally used as well.) %.hn 2 -\subsection*{Weapons (`{\tt )}')} +\subsection*{Weapons (`\texttt{)}')} %.pg Given a chance, most monsters in the Mazes of Menace will gratuitously try to @@ -2833,11 +2832,11 @@ vulnerable to certain types of weapons. Many weapons can be wielded in one hand; some require both hands. When wielding a two-handed weapon, you can not wear a shield, and vice versa. When wielding a one-handed weapon, you can have another -weapon ready to use by setting things up with the `{\tt x}' command, which +weapon ready to use by setting things up with the `\texttt{x}' command, which exchanges your primary (the one being wielded) and alternate weapons. And if you have proficiency in the ``two weapon combat'' skill, you may wield both weapons simultaneously as primary and secondary; use the -`{\tt X}' command to engage or disengage that. +`\texttt{X}' command to engage or disengage that. Only some types of characters (barbarians, for instance) have the necessary skill available. Even with that skill, using two weapons at once incurs a penalty in the chance to hit your target compared to using just one @@ -2845,30 +2844,30 @@ weapon at a time. %.pg There might be times when you'd rather not wield any weapon at all. -To accomplish that, wield `{\tt -}', or else use the `{\tt A}' command which +To accomplish that, wield `\texttt{-}', or else use the `\texttt{A}' command which allows you to unwield the current weapon in addition to taking off other worn items. %.pg Those of you in the audience who are AD\&D players, be aware that each weapon which existed in AD\&D does roughly the same damage to monsters in -{\it NetHack}. Some of the more obscure weapons (such as the -{\it aklys}, {\it lucern hammer}, and {\it bec-de-corbin\/}) are defined -in an appendix to {\it Unearthed Arcana}, an AD\&D supplement. +\textit{NetHack}. Some of the more obscure weapons (such as the +\textit{aklys}, \textit{lucern hammer}, and \textit{bec-de-corbin}) are defined +in an appendix to \textit{Unearthed Arcana}, an AD\&D supplement. %.pg -The commands to use weapons are `{\tt w}' (wield), `{\tt t}' (throw), -`{\tt f}' (fire), `{\tt Q}' (quiver), -`{\tt x}' (exchange), `{\tt X}' (twoweapon), and ``{\tt \#enhance}'' +The commands to use weapons are `\texttt{w}' (wield), `\texttt{t}' (throw), +`\texttt{f}' (fire), `\texttt{Q}' (quiver), +`\texttt{x}' (exchange), `\texttt{X}' (twoweapon), and ``\texttt{\#enhance}'' (see below). %.hn 3 \subsection*{Throwing and shooting} %.pg -You can throw just about anything via the `{\tt t}' command. It will prompt -for the item to throw; picking `{\tt ?}' will list things in your inventory -which are considered likely to be thrown, or picking `{\tt *}' will list +You can throw just about anything via the `\texttt{t}' command. It will prompt +for the item to throw; picking `\texttt{?}' will list things in your inventory +which are considered likely to be thrown, or picking `\texttt{*}' will list your entire inventory. After you've chosen what to throw, you will be prompted for a direction rather than for a specific target. The distance something can be thrown depends mainly on the type of object @@ -2881,26 +2880,26 @@ Some weapons will return when thrown. A boomerang---provided it fails to hit anything---is an obvious example. If an aklys (thonged club) is thrown while it is wielded, it will return even when it hits something. -A sufficiently strong hero can throw the warhammer {\it Mjollnir\/}; -when thrown by a {\it Valkyrie\/} it will return too. -However, aklyses and {\it Mjollnir\/} occasionally fail to return. +A sufficiently strong hero can throw the warhammer \textit{Mjollnir}; +when thrown by a \textit{Valkyrie} it will return too. +However, aklyses and \textit{Mjollnir} occasionally fail to return. Returning thrown objects occasionally fail to be caught, sometimes even hitting the thrower, but when caught they become re-wielded. %.pg -You can simplify the throwing operation by using the `{\tt Q}' command to -select your preferred ``missile'', then using the `{\tt f}' command to +You can simplify the throwing operation by using the `\texttt{Q}' command to +select your preferred ``missile'', then using the `\texttt{f}' command to throw it. You'll be prompted for a direction as above, but you don't -have to specify which item to throw each time you use `{\tt f}'. There is +have to specify which item to throw each time you use `\texttt{f}'. There is also an option, -{\it autoquiver}, -which has {\it NetHack\/} choose another item to automatically fill your +\textit{autoquiver}, +which has \textit{NetHack} choose another item to automatically fill your quiver (or quiver sack, or have at the ready) when the inventory slot used -for `{\tt Q}' runs out. -If your quiver is empty, {\it autoquiver\/} +for `\texttt{Q}' runs out. +If your quiver is empty, \textit{autoquiver} is false, and you are wielding a weapon which returns when thrown, you will throw that weapon instead of filling the quiver. -The fire command also has extra assistance, if {\it fireassist\/} +The fire command also has extra assistance, if \textit{fireassist} is on it will try to wield a launcher matching the ammo in the quiver. %.pg @@ -2916,12 +2915,12 @@ shoot arrows, in crossbow skill if you're wielding one to shoot bolts, or in sling skill if you're wielding one to shoot stones). The number of items that the character has a chance to fire varies from turn to turn. You can explicitly limit the number of shots by using a -numeric prefix before the `{\tt t}' or `{\tt f}' command. -For example, ``{\tt 2f}'' (or ``{\tt n2f}'' if using -{\it number\textunderscore pad\/} +numeric prefix before the `\texttt{t}' or `\texttt{f}' command. +For example, ``\texttt{2f}'' (or ``\texttt{n2f}'' if using +\textit{number\textunderscore pad} mode) would ensure that at most 2 arrows are shot even if you could have fired 3. If you specify -a larger number than would have been shot (``{\tt 4f}'' in this example), +a larger number than would have been shot (``\texttt{4f}'' in this example), you'll just end up shooting the same number (3, here) as if no limit had been specified. Once the volley is in motion, all of the items will travel in the same direction; if the first ones kill a monster, @@ -2945,14 +2944,14 @@ can achieve for each group. For instance, wizards can become highly skilled in daggers or staves but not in swords or bows. %.pg -The ``{\tt \#enhance}'' extended command is used to review current weapons +The ``\texttt{\#enhance}'' extended command is used to review current weapons proficiency (also spell proficiency) and to choose which skill(s) to improve when you've used one or more skills enough to become eligible to do so. The skill rankings are ``none'' (sometimes also referred to as ``restricted'', because you won't be able to advance), ``unskilled'', ``basic'', ``skilled'', and ``expert''. Restricted skills simply will not appear in the list -shown by ``{\tt \#enhance}''. +shown by ``\texttt{\#enhance}''. (Divine intervention might unrestrict a particular skill, in which case it will start at unskilled and be limited to basic.) Some characters can enhance their barehanded combat or martial arts skill @@ -2968,7 +2967,7 @@ higher. A successful hit has a chance to boost your training towards the next skill level (unless you've already reached the limit for this skill). Once such training reaches the threshold for that next level, you'll be told that you feel more confident in your skills. At that -point you can use ``{\tt \#enhance}'' to increase one or more skills. +point you can use ``\texttt{\#enhance}'' to increase one or more skills. Such skills are not increased automatically because there is a limit to your total overall skills, so you need to actively choose which skills to enhance @@ -2980,7 +2979,7 @@ and which to ignore. %.pg Some characters can use two weapons at once. Setting things up to do so can seem cumbersome but becomes second nature with use. -To wield two weapons, you need to use the ``{\tt \#twoweapon}'' command. +To wield two weapons, you need to use the ``\texttt{\#twoweapon}'' command. But first you need to have a weapon in each hand. (Note that your two weapons are not fully equal; the one in the hand you normally wield with is considered primary and the other @@ -2994,35 +2993,35 @@ as alternate weapon.) %.pg If your primary weapon is wielded but your off hand is empty or has -the wrong weapon, use the sequence `{\tt x}', `{\tt w}', `{\tt x}' to +the wrong weapon, use the sequence `\texttt{x}', `\texttt{w}', `\texttt{x}' to first swap your primary into your off hand, wield whatever you want as secondary weapon, then swap them both back into the intended hands. If your secondary or alternate weapon is correct but your primary -one is not, simply use `{\tt w}' to wield the primary. +one is not, simply use `\texttt{w}' to wield the primary. Lastly, if neither hand holds the correct weapon, -use `{\tt w}', `{\tt x}', `{\tt w}' +use `\texttt{w}', `\texttt{x}', `\texttt{w}' to first wield the intended secondary, swap it to off hand, and then wield the primary. %.pg The whole process can be simplified via use of the -{\it pushweapon\/} -option. When it is enabled, then using `{\tt w}' to wield something +\textit{pushweapon} +option. When it is enabled, then using `\texttt{w}' to wield something causes the currently wielded weapon to become your alternate weapon. -So the sequence `{\tt w}', `{\tt w}' can be used to first wield the weapon you +So the sequence `\texttt{w}', `\texttt{w}' can be used to first wield the weapon you intend to be secondary, and then wield the one you want as primary which will push the first into secondary position. %.pg -When in two-weapon combat mode, using the `{\tt X}' command +When in two-weapon combat mode, using the `\texttt{X}' command toggles back to single-weapon mode. Throwing or dropping either of the weapons or having one of them be stolen or destroyed will also make you revert to single-weapon combat. %.hn 2 -\subsection*{Armor (`{\tt [}')} +\subsection*{Armor (`\texttt{[}')} %.pg Lots of unfriendly things lurk about; you need armor to protect @@ -3031,7 +3030,7 @@ protection than others. Your armor class is a measure of this protection. Armor class (AC) is measured as in AD\&D, with 10 being the equivalent of no armor, and lower numbers meaning better armor. Each suit of armor which exists in AD\&D gives the same protection in -{\it NetHack}. +\textit{NetHack}. Here is a list of the armor class values provided by suits of armor: @@ -3062,7 +3061,7 @@ enchanted. Shirts are an exception; they don't provide any protection unless enchanted. Some cloaks also don't improve AC when unenchanted but all cloaks offer some protection against rust or corrosion to suits worn under them and -against some monster {\it touch\/} attacks. +against some monster \textit{touch} attacks. %.pg If a piece of armor is enchanted, its armor protection will be better @@ -3079,21 +3078,21 @@ Many types of armor are subject to some kind of damage like rust. Such damage can be repaired. Some types of armor may inhibit spell casting. %.pg -The {\it nudist\/} +The \textit{nudist} 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 extremely difficult to accomplish). %.pg -The commands to use armor are `{\tt W}' (wear) and `{\tt T}' (take off). -The `{\tt A}' command can be used to take off armor as well as other +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 worn items. -Also, `{\tt P}' (put on) and `{\tt R}' (remove) which are normally for +Also, `\texttt{P}' (put on) and `\texttt{R}' (remove) which are normally for accessories can be used for armor, but pieces of armor won't be shown as likely candidates in a prompt for choosing what to put on or remove. %.hn 2 -\subsection*{Food (`{\tt \%}')} +\subsection*{Food (`\texttt{\%}')} %.pg Food is necessary to survive. If you go too long without eating you @@ -3117,13 +3116,13 @@ but with some rather unpleasant side-effects. %.pg You can name one food item after something you like to eat with the -{\it fruit\/} option. +\textit{fruit} option. %.pg -The command to eat food is `{\tt e}'. +The command to eat food is `\texttt{e}'. %.hn 2 -\subsection*{Scrolls (`{\tt ?}')} +\subsection*{Scrolls (`\texttt{?}')} %.pg Scrolls are labeled with various titles, probably chosen by ancient wizards @@ -3133,7 +3132,7 @@ magic spells on them). %.pg One of the most useful of these is the % -{\it scroll of identify}, which +\textit{scroll of identify}, which can be used to determine what another object is, whether it is cursed or blessed, and how many uses it has left. Some objects of subtle enchantment are difficult to identify without these. @@ -3150,24 +3149,24 @@ cursed, or read while the hero is confused. %.pg A mail daemon may run up and deliver mail to you as a % -{\it scroll of mail} (on versions compiled with this feature). -To use this feature on versions where {\it NetHack\/} +\textit{scroll of mail} (on versions compiled with this feature). +To use this feature on versions where \textit{NetHack} mail delivery is triggered by electronic mail appearing in your system mailbox, -you must let {\it NetHack\/} know where to look for new mail by setting the -{\tt MAIL} environment variable to the file name of your mailbox. -You may also want to set the {\tt MAILREADER} environment variable to the -file name of your favorite reader, so {\it NetHack\/} can shell to it when you +you must let \textit{NetHack} know where to look for new mail by setting the +\texttt{MAIL} environment variable to the file name of your mailbox. +You may also want to set the \texttt{MAILREADER} environment variable to the +file name of your favorite reader, so \textit{NetHack} can shell to it when you read the scroll. -On versions of {\it NetHack\/} where mail is randomly +On versions of \textit{NetHack} where mail is randomly generated internal to the game, these environment variables are ignored. You can disable the mail daemon by turning off the -{\it mail\/} option. +\textit{mail} option. %.pg -The command to read a scroll is `{\tt r}'. +The command to read a scroll is `\texttt{r}'. %.hn 2 -\subsection*{Potions (`{\tt !}')} +\subsection*{Potions (`\texttt{!}')} %.pg Potions are distinguished by the color of the liquid inside the flask. @@ -3177,20 +3176,20 @@ They disappear after you quaff them. Clear potions are potions of water. Sometimes these are blessed or cursed, resulting in holy or unholy water. Holy water is the bane of the undead, so potions of holy water are good things to -throw (`{\tt t}') at them. It is also sometimes very useful to dip -(``{\tt \#dip}'') an object into a potion. +throw (`\texttt{t}') at them. It is also sometimes very useful to dip +(``\texttt{\#dip}'') an object into a potion. %.pg -The command to drink a potion is `{\tt q}' (quaff). +The command to drink a potion is `\texttt{q}' (quaff). %.hn 2 -\subsection*{Wands (`{\tt /}')} +\subsection*{Wands (`\texttt{/}')} %.pg Wands usually have multiple magical charges. Some types of wands require a direction in which to zap them. You can also -zap them at yourself (just give a `{\tt .}' or `{\tt s}' for the direction). +zap them at yourself (just give a `\texttt{.}' or `\texttt{s}' for the direction). Be warned, however, for this is often unwise. Other types of wands don't require a direction. The number of charges in a @@ -3215,15 +3214,15 @@ magical energies. When you have fully identified a particular wand, inventory display will include additional information in parentheses: the number of times it has been recharged followed by a colon and then by its current number of charges. -A current charge count of {\tt -1} is a special case indicating that the wand +A current charge count of \texttt{-1} is a special case indicating that the wand has been cancelled. %.pg -The command to use a wand is `{\tt z}' (zap). To break one, use the `{\tt a}' +The command to use a wand is `\texttt{z}' (zap). To break one, use the `\texttt{a}' (apply) command. %.hn 2 -\subsection*{Rings (`{\tt =}')} +\subsection*{Rings (`\texttt{=}')} %.pg Rings are very useful items, since they are relatively permanent @@ -3248,14 +3247,14 @@ off before putting on or removing a ring and then re-wear them after. That's done implicitly to avoid unnecessary tedium. %.pg -The commands to use rings are `{\tt P}' (put on) and `{\tt R}' (remove). -`{\tt A}', `{\tt W}', and `{\tt T}' can also be used; see {\it Amulets\/}. +The commands to use rings are `\texttt{P}' (put on) and `\texttt{R}' (remove). +`\texttt{A}', `\texttt{W}', and `\texttt{T}' can also be used; see \textit{Amulets}. %.hn 2 -\subsection*{Spellbooks (`{\tt +}')} +\subsection*{Spellbooks (`\texttt{+}')} %.pg -Spellbooks are tomes of mighty magic. When studied with the `{\tt r}' (read) +Spellbooks are tomes of mighty magic. When studied with the `\texttt{r}' (read) command, they transfer to the reader the knowledge of a spell (and therefore eventually become unreadable)---unless the attempt backfires. @@ -3281,7 +3280,7 @@ your memory of each spell will dim, and you will need to relearn it. %.pg Some spells require a direction in which to cast them, similar to wands. -To cast one at yourself, just give a `{\tt .}' or `{\tt s}' for the direction. +To cast one at yourself, just give a `\texttt{.}' or `\texttt{s}' for the direction. A few spells require you to pick a target location rather than just specify a particular direction. Other spells don't require any direction or target. @@ -3290,7 +3289,7 @@ Other spells don't require any direction or target. Just as weapons are divided into groups in which a character can become proficient (to varying degrees), spells are similarly grouped. Successfully casting a spell exercises its skill group; using the -``{\tt \#enhance}'' command to advance a sufficiently exercised skill +``\texttt{\#enhance}'' command to advance a sufficiently exercised skill will affect all spells within the group. Advanced skill may increase the potency of spells, reduce their risk of failure during casting attempts, and improve the accuracy of the estimate for how much longer they will @@ -3303,14 +3302,14 @@ Casting a spell also requires flexible movement, and wearing various types of armor may interfere with that. %.pg -The command to read a spellbook is the same as for scrolls, `{\tt r}' (read). -The `{\tt +}' command lists each spell you know along with its level, skill +The command to read a spellbook is the same as for scrolls, `\texttt{r}' (read). +The `\texttt{+}' command lists each spell you know along with its level, skill category, chance of failure when casting, and an estimate of how strongly it is remembered. -The `{\tt Z}' (cast) command casts a spell. +The `\texttt{Z}' (cast) command casts a spell. %.hn 2 -\subsection*{Tools (`{\tt (}')} +\subsection*{Tools (`\texttt{(}')} %.pg Tools are miscellaneous objects with various purposes. Some tools @@ -3319,8 +3318,8 @@ out after a while. Other tools are containers, which objects can be placed into or taken out of. %.pg -Some tools (such as a blindfold) can be {\it worn\/} and can be put on and -removed like other accessories (rings, amulets); see {\it Amulets\/}. +Some tools (such as a blindfold) can be \textit{worn} and can be put on and +removed like other accessories (rings, amulets); see \textit{Amulets}. Other tools (such as pick-axe) can be wielded as weapons in addition to being applied for their usual purpose, and in some cases (again, pick-axe) become wielded as a weapon even when applied. @@ -3329,38 +3328,38 @@ become wielded as a weapon even when applied. % Mentioned here because of the old method of attempting "Zen" conduct: % restart until there's a blindfold in starting inventory and put it on % first thing. -The {\it blind\/} +The \textit{blind} option can be set (prior to game start) to attempt to play the entire game without being able to see (a self-imposed challenge which is very difficult to accomplish). %.pg -The command to use a tool is `{\tt a}' (apply). +The command to use a tool is `\texttt{a}' (apply). %.hn 3 \subsection*{Containers} %.pg You may encounter bags, boxes, and chests in your travels. A tool of -this sort can be opened with the ``{\tt \#loot}'' extended command when +this sort can be opened with the ``\texttt{\#loot}'' extended command when you are standing on top of it (that is, on the same floor spot), -or with the `{\tt a}' (apply) command when you are carrying it. However, +or with the `\texttt{a}' (apply) command when you are carrying it. However, chests are often locked, and are in any case unwieldy objects. You must set one down before unlocking it by -using a key or lock-picking tool with the `{\tt a}' (apply) command, -by kicking it with the `{\tt \^{}D}' command, -or by using a weapon to force the lock with the ``{\tt \#force}'' +using a key or lock-picking tool with the `\texttt{a}' (apply) command, +by kicking it with the `\texttt{\textasciicircum D}' command, +or by using a weapon to force the lock with the ``\texttt{\#force}'' extended command. %.pg Some chests are trapped, causing nasty things to happen when you unlock or open them. You can check for and try to deactivate traps -with the ``{\tt \#untrap}'' extended command. +with the ``\texttt{\#untrap}'' extended command. %.pg When the contents of a container are known, that container will be described as something like ``a sack containing 3 items''. -In this example, the 3 refers to number of {\it stacks\/} of compatible +In this example, the 3 refers to number of \textit{stacks} of compatible items, not to the total number of individual items. So a sack holding 2 sky blue potions, 7 arrows, and 350 gold pieces would be described as having 3 items rather than 10 or 359. @@ -3373,10 +3372,10 @@ If a chest or large box is described as ``broken'', that means that it can't be locked rather than that it no longer functions as a container. %.pg -The {\it apply\/} and {\it loot\/} commands allow you to take out and/or +The \textit{apply} and \textit{loot} commands allow you to take out and/or put in an arbitrary number of items in a single operation. If you want to take everything out of a container, you can use the -``{\tt \#tip}'' command to pour the contents onto the floor. +``\texttt{\#tip}'' command to pour the contents onto the floor. This may be your only way to get things out if your hands are stuck to a cursed two-handed weapon. When your hands aren't stuck, you have the potential to pour the @@ -3385,7 +3384,7 @@ contents into another container. the floor.) %.hn 2 -\subsection*{Amulets (`{\tt "}')} +\subsection*{Amulets (`\texttt{"}')} %.pg Amulets are very similar to rings, and often more powerful. Like @@ -3398,16 +3397,16 @@ Like wearing rings, wearing an amulet affects your metabolism, causing you to grow hungry more rapidly. %.pg -The commands to use amulets are the same as for rings, `{\tt P}' (put on) -and `{\tt R}' (remove). -`{\tt A}' can be used to remove various worn items including amulets. -Also, '{\tt W}' (wear) and `{\tt T}' (take off) which are normally for +The commands to use amulets are the same as for rings, `\texttt{P}' (put on) +and `\texttt{R}' (remove). +`\texttt{A}' can be used to remove various worn items including amulets. +Also, '\texttt{W}' (wear) and `\texttt{T}' (take off) which are normally for armor can be used for amulets and other accessories (rings and eyewear), but accessories won't be shown as likely candidates in a prompt for choosing what to wear or take off. %.hn 2 -\subsection*{Gems (`{\tt *}')} +\subsection*{Gems (`\texttt{*}')} %.pg Some gems are valuable, and can be sold for a lot of gold. @@ -3421,7 +3420,7 @@ All rocks, however, can be used as projectile weapons (if you have a sling). In the most desperate of cases, you can still throw them by hand. %.hn 2 -\subsection*{Large rocks (`{\tt `}')} +\subsection*{Large rocks (`\texttt{`}')} %.pg Statues and boulders are not particularly useful, and are generally heavy. It is rumored that some statues are not what they seem. @@ -3429,12 +3428,12 @@ It is rumored that some statues are not what they seem. %.pg Boulders occasionally block your path. You can push one forward (by attempting to walk onto its spot) -when nothing blocks {\it its\/} path, or you can +when nothing blocks \textit{its} path, or you can smash it into a pile of small rocks with breaking magic or a pick-axe. It is possible to move onto a boulder's location if certain conditions are met; ordinarily one of those conditions is that pushing it any further be blocked. -Using the move-without-picking-up prefix (default key `{\tt m}') +Using the move-without-picking-up prefix (default key `\texttt{m}') prior to the direction of movement will attempt to move to a boulder's location without pushing it in addition to the prefix's usual action of suppressing auto-pickup at the destination. @@ -3451,11 +3450,11 @@ They can be smashed into rocks though. %.pg For some configurations of the program, statues are no longer shown -as `{\tt `}' +as `\texttt{`}' but by the letter representing the monster they depict instead. %.hn 2 -\subsection*{Gold (`{\tt \$}')} +\subsection*{Gold (`\texttt{\$}')} %.pg Gold adds to your score, and you can buy things in shops with it. @@ -3467,12 +3466,12 @@ you are carrying (shopkeepers aside). Gold pieces are the only type of object where bless/curse state does not apply. They're always uncursed but never described as uncursed even if you turn -off the ``{\it implicit\textunderscore uncursed\/}'' option. -You can set the ``{\it goldX\/}'' +off the ``\textit{implicit\textunderscore uncursed}'' option. +You can set the ``\textit{goldX}'' option if you prefer to have gold pieces be treated as bless/curse state -{\it unknown\/} rather than as known to be uncursed. +\textit{unknown} rather than as known to be uncursed. Only matters when you're using an object selection prompt that can filter -by ``{\tt BUCX}'' state. +by ``\texttt{BUCX}'' state. %.hn 2 \subsection*{Persistence of Objects} @@ -3497,7 +3496,7 @@ again you will re-discover the object and resume remembering it. The situation is the same for a pile of objects, except that only the top item of the pile is displayed. The -{\it hilite\textunderscore pile\/} +\textit{hilite\textunderscore pile} option can be enabled in order to show an item differently when it is the top one of a pile. @@ -3505,10 +3504,10 @@ the top one of a pile. \section{Conduct} %.pg -As if winning {\it NetHack\/} were not difficult enough, certain players +As if winning \textit{NetHack} were not difficult enough, certain players seek to challenge themselves by imposing restrictions on the way they play the game. The game automatically tracks some of -these challenges, which can be checked at any time with the {\tt \#conduct} +these challenges, which can be checked at any time with the \texttt{\#conduct} command or at the end of the game. When you perform an action which breaks a challenge, it will no longer be listed. This gives players extra ``bragging rights'' for winning the game with these @@ -3528,8 +3527,8 @@ not violate any food challenges either. %.pg A strict vegan diet is one which avoids any food derived from animals. The primary source of nutrition is fruits and vegetables. The -corpses and tins of blobs (`{\tt b}'), jellies (`{\tt j}'), and fungi -(`{\tt F}') are also considered to be vegetable matter. Certain human +corpses and tins of blobs (`\texttt{b}'), jellies (`\texttt{j}'), and fungi +(`\texttt{F}') are also considered to be vegetable matter. Certain human food is prepared without animal products; namely, lembas wafers, cram rations, food rations (gunyoki), K-rations, and C-rations. Metal or another normally indigestible material eaten while polymorphed @@ -3540,7 +3539,7 @@ Note however that eating such items still counts against foodless conduct. Vegetarians do not eat animals; however, they are less selective about eating animal byproducts than vegans. In addition to the vegan items listed above, they may eat any kind -of pudding (`{\tt P}') other than the black puddings, +of pudding (`\texttt{P}') other than the black puddings, eggs and food made from eggs (fortune cookies and pancakes), food made with milk (cream pies and candy bars), and lumps of royal jelly. Monks are expected to observe a vegetarian diet. @@ -3571,8 +3570,8 @@ from ``cherries'' to ``pork chops'', are also assumed to be vegan. %.pg An atheist is one who rejects religion. This means that you cannot -{\tt \#pray}, {\tt \#offer} sacrifices to any god, -{\tt \#turn} undead, or {\tt \#chat} with a priest. +\texttt{\#pray}, \texttt{\#offer} sacrifices to any god, +\texttt{\#turn} undead, or \texttt{\#chat} with a priest. Particularly selective readers may argue that playing Monk or Priest characters should violate this conduct; that is a choice left to the player. Offering the Amulet of Yendor to your god is necessary to @@ -3589,7 +3588,7 @@ fire, and kick weapons; use a wand, spell, or other type of item; or fight with your hands and feet. %.pg -In {\it NetHack}, a pacifist refuses to cause the death of any other monster +In \textit{NetHack}, a pacifist refuses to cause the death of any other monster (i.e. if you would get experience for the death). This is a particularly difficult challenge, although it is still possible to gain experience by other means. @@ -3607,7 +3606,7 @@ counted. %.pg There is a side-branch to the main dungeon called ``Sokoban,'' briefly -described in the earlier section about {\it Traps}. +described in the earlier section about \textit{Traps}. As mentioned there, the goal is to push boulders into pits and/or holes to plug those in order to both get the boulders out of your way and be able to go past the traps. @@ -3618,7 +3617,7 @@ diagonally. Other rules can, such as not smashing boulders with magic or tools, but doing so causes you to receive a luck penalty. No message about that is given at the time, but it is tracked as a conduct. -The {\tt \#conduct} command and end of game disclosure will report whether +The \texttt{\#conduct} command and end of game disclosure will report whether you have abided by the special rules of Sokoban, and if not, how many times you violated them, providing you with a way to discover which actions incur bad luck so that you can be better informed about whether @@ -3630,7 +3629,7 @@ you haven't done anything interesting there. Ending the game with ``never broke the Sokoban rules'' conduct is most meaningful if you also manage to perform the ``obtained the Sokoban prize'' achievement -(see {\it Achievements\/} below).) +(see \textit{Achievements} below).) %.pg There are several other challenges tracked by the game. It is possible @@ -3656,7 +3655,7 @@ choose ``nothing'' if you want to decline. End of game disclosure will also display various achievements representing progress toward ultimate ascension, if any have been attained. -They aren't directly related to {\it conduct\/} but are grouped with +They aren't directly related to \textit{conduct} but are grouped with it because they fall into the same category of ``bragging rights'' and to limit the number of questions during disclosure. Listed here roughly in order of difficulty and not necessarily in the order @@ -3665,12 +3664,12 @@ in which you might accomplish them. % [length stuff copied from paranoid_confirmation] \newlength{\achwidth} %.PS "Mines'\~End\~" -\settowidth{\achwidth}{\tt Mines'~End~} +\settowidth{\achwidth}{\texttt{Mines'~End~}} \addtolength{\achwidth}{\labelsep} \begin{description}[leftmargin=\achwidth, topsep=1mm, itemsep=0mm, labelwidth=*, font=\ttfamily, align=right] %.PL Shop \item[] -Attained rank title {\it Rank}. +Attained rank title \textit{Rank}. \item[Shop] Entered a shop. \item[Temple] @@ -3735,20 +3734,20 @@ Achievements are recorded and subsequently reported in the order in which they happen during your current game rather than the order listed here. %.pg -There are nine {\it Rank} titles for each role, bestowed at experience +There are nine \textit{Rank} titles for each role, bestowed at experience levels 1, 3, 6, 10, 14, 18, 22, 26, and 30. The one for experience level 1 is not recorded as an achievement. Losing enough levels to revert to lower rank(s) does not discard the corresponding achievement(s). %.pg -There's no guaranteed {\it Novel} so the achievement to read one might +There's no guaranteed \textit{Novel} so the achievement to read one might not always be attainable (except perhaps by wishing). -Similarly, the {\it Big Room} level is not always present. +Similarly, the \textit{Big Room} level is not always present. Unlike with the Novel, there's no way to wish for this opportunity. %.pg -The ``special items'' hidden in {\it Mines'~End\/} and {\it Sokoban\/} +The ``special items'' hidden in \textit{Mines'~End} and \textit{Sokoban} are not unique but are considered to be prizes or rewards for exploring those levels since doing so is not necessary to complete the game. @@ -3756,20 +3755,20 @@ Finding other instances of the same objects doesn't record the corresponding achievement. %.pg -The {\it Medusa\/} achievement is recorded if she dies for any reason, +The \textit{Medusa} achievement is recorded if she dies for any reason, even if you are not directly responsible, and only if she dies. %.pg -The 5-note {\it tune\/} can be learned via trial and error with a musical +The 5-note \textit{tune} can be learned via trial and error with a musical instrument played closely enough---but not too close!---to the Castle level's drawbridge or can be given to you via prayer boon. %.pg -{\it Blind\/}, {\it Deaf\/}, {\it Nudist\/}, and {\it Pauper\/} are also conducts, and they can only be -enabled by setting the correspondingly named option in {\tt NETHACKOPTIONS} +\textit{Blind}, \textit{Deaf}, \textit{Nudist}, and \textit{Pauper} are also conducts, and they can only be +enabled by setting the correspondingly named option in \texttt{NETHACKOPTIONS} or run-time configuration file prior to game start. -In the case of {\it Blind\/} and {\it Deaf\/}, the option also enforces the conduct. +In the case of \textit{Blind} and \textit{Deaf}, the option also enforces the conduct. They aren't really significant accomplishments unless/until you make substantial progress into the dungeon. @@ -3777,19 +3776,19 @@ substantial progress into the dungeon. \section{Options} %.pg -Due to variations in personal tastes and conceptions of how {\it NetHack\/} -should do things, there are options you can set to change how {\it NetHack\/} +Due to variations in personal tastes and conceptions of how \textit{NetHack} +should do things, there are options you can set to change how \textit{NetHack} behaves. %.hn 2 \subsection*{Setting the options} %.pg -Options may be set in a number of ways. Within the game, the `{\tt O}' +Options may be set in a number of ways. Within the game, the `\texttt{O}' command allows you to view all options and change most of them. You can also set options automatically by placing them in a configuration -file, or in the ``{\tt NETHACKOPTIONS}'' environment variable. -Some versions of {\it NetHack\/} also have front-end programs that allow +file, or in the ``\texttt{NETHACKOPTIONS}'' environment variable. +Some versions of \textit{NetHack} also have front-end programs that allow you to set options before starting the game or a global configuration for system administrators. @@ -3812,24 +3811,24 @@ On Windows, the name is \mbox{``.nethackrc''} location in the folder The file may not exist, but it is a normal ASCII text file and can be created with any text editor. -After running {\it NetHack\/} for the first time, you should find a default +After running \textit{NetHack} for the first time, you should find a default template for ths configuration file named \mbox{``.nethackrc.template''} in \mbox{{``\%USERPROFILE\%\textbackslash NetHack\textbackslash''}}. -If you have not created the configuration file, {\it NetHack\/} will create +If you have not created the configuration file, \textit{NetHack} will create the configuration file for you using the default template file.\\ %.lp "" On MS-DOS it is \mbox{``defaults.nh''} in the same folder as -\mbox{{\it nethack.exe\/}}.\\ +\mbox{\textit{nethack.exe}}.\\ %.lp "" -Any line in the configuration file starting with `{\tt \#}' is treated +Any line in the configuration file starting with `\texttt{\#}' is treated as a comment and ignored. Empty lines are ignored. -Any line beginning with `{\tt \verb+[+}' and ending in `{\tt \verb+]+}' -is a section marker (the closing `{\tt \verb+]+}' can be followed -by whitespace and then an arbitrary comment beginning with `{\tt \#}'). +Any line beginning with `\texttt{[}' and ending in `\texttt{]}' +is a section marker (the closing `\texttt{]}' can be followed +by whitespace and then an arbitrary comment beginning with `\texttt{\#}'). The text between the square brackets is the section name. Section markers are only valid after a CHOOSE directive and their names are case insensitive. @@ -3872,8 +3871,8 @@ Example: %.lp \item[HACKDIR] -Default location of files {\it NetHack\/} needs. On Windows HACKDIR -defaults to the location of the {\it NetHack.exe\/} or {\it NetHackw.exe\/} file +Default location of files \textit{NetHack} needs. On Windows HACKDIR +defaults to the location of the \textit{NetHack.exe} or \textit{NetHackw.exe} file so setting HACKDIR to override that is not usually necessary or recommended. %.lp \item[LEVELDIR] @@ -3904,7 +3903,7 @@ Enable or disable an extended command autocompletion. Autocompletion has no effect for the X11 windowport. You can specify multiple autocompletions. To enable autocompletion, list the extended command. Prefix the -command with ``{{\tt !}}'' to disable the autocompletion +command with ``\texttt{!}'' to disable the autocompletion for that command. %.lp "" @@ -3917,7 +3916,7 @@ Example: %.lp \item[AUTOPICKUP\textunderscore EXCEPTION] -Set exceptions to the {{\it pickup\textunderscore types\/}} +Set exceptions to the {\textit{pickup\textunderscore types}} option. See the ``Configuring Autopickup Exceptions'' section. %.lp \item[BINDINGS] @@ -3956,7 +3955,7 @@ Example: %.ed %.lp "" -If {\tt []} is present, the preceding section is closed and no new +If \texttt{[]} is present, the preceding section is closed and no new section begins; whatever follows will be common to all sections. Otherwise the last section extends to the end of the options file. @@ -3971,7 +3970,7 @@ See the ``Configuring Message Types`` section. %.lp \item[ROGUESYMBOLS] Custom symbols for the rogue level's symbol set. -See {\it SYMBOLS} below. +See \textit{SYMBOLS} below. %.lp \item[SOUND] Define a sound mapping. @@ -3984,7 +3983,7 @@ See the ``Configuring User Sounds'' section. \item[SYMBOLS] Override one or more symbols in the symbol set used for all dungeon levels except for the special rogue level. -See the ``Modifying {\it NetHack\/} Symbols'' section. +See the ``Modifying \textit{NetHack} Symbols'' section. %.pg %.lp "" @@ -4038,13 +4037,13 @@ Here is an example of configuration file contents: %.BR 2 %.hn 2 -\subsection*{Using the {\tt NETHACKOPTIONS} environment variable} +\subsection*{Using the \texttt{NETHACKOPTIONS} environment variable} %.pg The NETHACKOPTIONS variable is a comma-separated list of initial values for the various options. Some can only be turned on or off. You turn one of these on by adding the name of the option to the list, -and turn it off by typing a `{\tt !}' or ``{\tt no}'' before the name. +and turn it off by typing a `\texttt{!}' or ``\texttt{no}'' before the name. Others take a character string as a value. You can set string options by typing the option name, a colon or equals sign, and then the value of the string. @@ -4052,10 +4051,10 @@ The value is terminated by the next comma or the end of string. %.pg For example, to set up an environment variable so that -{\it color\/} is {\tt on}, -{\it legacy\/} is {\tt off}, -character {\it name\/} is set to ``{\tt Blue Meanie}'', -and named {\it fruit\/} is set to ``{\tt lime}'', +\textit{color} is \texttt{on}, +\textit{legacy} is \texttt{off}, +character \textit{name} is set to ``\texttt{Blue Meanie}'', +and named \textit{fruit} is set to ``\texttt{lime}'', you would enter the command %.SD i \begin{verbatim} @@ -4063,7 +4062,7 @@ you would enter the command \end{verbatim} %.ED -\noindent in {\it csh} +\noindent in \textit{csh} (note the need to escape the `!' since it's special to that shell), or the pair of commands %.SD i @@ -4073,7 +4072,7 @@ to that shell), or the pair of commands \end{verbatim} %.ED -\noindent in {\it sh}, {\it ksh}, or {\it bash}. +\noindent in \textit{sh}, \textit{ksh}, or \textit{bash}. %.pg The NETHACKOPTIONS value is effectively the same as a single OPTIONS @@ -4087,9 +4086,9 @@ not allowed. Instead of a comma-separated list of options, NETHACKOPTIONS can be set to the full name of a configuration file you want to use. -If that full name doesn't start with a slash, precede it with `{\tt @}' +If that full name doesn't start with a slash, precede it with `\texttt{@}' (at-sign) to let NetHack know that the rest is intended as a file name. -If it does start with `{\tt /}', the at-sign is optional. +If it does start with `\texttt{/}', the at-sign is optional. %.hn 2 \subsection*{Customization options} @@ -4104,7 +4103,7 @@ Some options are persistent, and are saved and reloaded along with the game. Changing a persistent option in the configuration file applies only to new games. -\begin{description}[font=\itshape] +\begin{description}[font=\mdseries\itshape] %.lp \item[accessiblemsg] Add location or direction information to messages (default is off). @@ -4115,22 +4114,22 @@ Note that this has nothing to do with your computer's audio capabilities. Persistent. %.lp \item[alignment] -Your starting alignment ({\tt align:lawful}, {\tt align:neutral}, -or {\tt align:chaotic}). +Your starting alignment (\texttt{align:lawful}, \texttt{align:neutral}, +or \texttt{align:chaotic}). You may specify just the first letter. Many roles and the non-human races restrict which alignments are allowed. -See {\it role\/} +See \textit{role} for a description of how to use negation to exclude choices. %.lp "" \\ -If {\tt align} is not specified, there is no default value; +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. -Cannot be set with the `{\tt O}' command. Persistent. +Cannot be set with the `\texttt{O}' command. Persistent. %.lp \item[autodescribe] Automatically describe the terrain under cursor when asked to get a location on the map (default true). -The {\it whatis\textunderscore coord\/} +The \textit{whatis\textunderscore coord} option controls whether the description includes map coordinates. %.lp \item[autodig] @@ -4146,14 +4145,14 @@ Automatically pick up things onto which you move (default off). Persistent. \\ %.lp "" -See ``{\it pickup\textunderscore types\/}'' and also -``{\it autopickup\textunderscore exception\/}'' for ways to refine the behavior. +See ``\textit{pickup\textunderscore types}'' and also +``\textit{autopickup\textunderscore exception}'' for ways to refine the behavior. \\ %.lp "" -Note: prior to version 3.7.0, the default for {\it autopickup\/} was {\it on}. +Note: prior to version 3.7.0, the default for \textit{autopickup} was \textit{on}. %.lp \item[autoquiver] -This option controls what happens when you attempt the `{\tt f}' (fire) +This option controls what happens when you attempt the `\texttt{f}' (fire) command when nothing is quivered or readied (default false). When true, the computer will fill your quiver or quiver sack or make ready some suitable weapon. @@ -4161,9 +4160,9 @@ Note that it will not take into account the blessed/cursed status, enchantment, damage, or quality of the weapon; you are free to manually fill your quiver or quiver sack or make ready -with the `{\tt Q}' command instead. +with the `\texttt{Q}' command instead. If no weapon is found or the option is -false, the `{\tt t}' (throw) command is executed instead. Persistent. +false, the `\texttt{t}' (throw) command is executed instead. Persistent. %.lp \item[autounlock] %\hyphenation{apply\-key}%this needs to be tested... @@ -4174,7 +4173,7 @@ Takes a plus-sign separated list of values: % au => autounlock \newlength{\auwidth} %.PS Apply-Key -\settowidth{\auwidth}{\tt Apply-Key} +\settowidth{\auwidth}{\texttt{Apply-Key}} \addtolength{\auwidth}{\labelsep} \begin{description}[leftmargin=\auwidth, topsep=1mm, itemsep=0mm, labelwidth=*, font=\ttfamily, align=right] %.PL Untrap @@ -4202,9 +4201,9 @@ has no effect on doors); none of the above; can't be combined with the other choices. %.PE \end{description} -Omitting the value is treated as if {\tt autounlock:apply-key}. -Preceding {\tt autounlock} with `{\tt !}' or ``{\tt no}'' is treated as -{\tt autounlock:none}. +Omitting the value is treated as if \texttt{autounlock:apply-key}. +Preceding \texttt{autounlock} with `\texttt{!}' or ``\texttt{no}'' is treated as +\texttt{autounlock:none}. \\ %.lp "" Applying a key might set off a trap if the door or container is trapped. @@ -4224,15 +4223,15 @@ Allow saving and loading bones files (default true). Persistent. %.lp \item[boulder] Set the character used to display boulders (default is the ``large rock'' -class symbol, `{\tt `}'). +class symbol, `\texttt{`}'). %.lp \item[catname] -Name your starting cat (for example, ``{\tt catname:Morris}''). -Cannot be set with the `{\tt O}' command. +Name your starting cat (for example, ``\texttt{catname:Morris}''). +Cannot be set with the `\texttt{O}' command. %.lp character \item[character] -Synonym for ``{\tt role}'' to pick the type of your character -(for example ``{\tt character:Monk}''). See {\it role\/} for more details. +Synonym for ``\texttt{role}'' to pick the type of your character +(for example ``\texttt{character:Monk}''). See \textit{role} for more details. %.lp \item[checkpoint] Save game state after each level change, for possible recovery after @@ -4254,26 +4253,26 @@ Start the character permanently deaf (default false). Persistent. %.lp \item[dropped\textunderscore nopick] If this option is on, items you dropped will not be automatically picked up, -even if ``{\it autopickup\/}'' is also on and they are in -``{\it pickup\textunderscore types\/}'' or match a positive autopickup exception +even if ``\textit{autopickup}'' is also on and they are in +``\textit{pickup\textunderscore types}'' or match a positive autopickup exception (default on). Persistent. %.lp \item[disclose] Controls what information the program reveals when the game ends. Value is a space separated list of prompting/category pairs -(default is `{\tt ni na nv ng nc no}', -prompt with default response of `{\tt n}' for each candidate). +(default is `\texttt{ni na nv ng nc no}', +prompt with default response of `\texttt{n}' for each candidate). Persistent. The possibilities are: %.sd %.si -{\tt i} --- disclose your inventory;\\ -{\tt a} --- disclose your attributes;\\ -{\tt v} --- summarize monsters that have been vanquished;\\ -{\tt g} --- list monster species that have been genocided;\\ -{\tt c} --- display your conduct; also achievements, if any;\\ -{\tt o} --- display dungeon overview. +\texttt{i} --- disclose your inventory;\\ +\texttt{a} --- disclose your attributes;\\ +\texttt{v} --- summarize monsters that have been vanquished;\\ +\texttt{g} --- list monster species that have been genocided;\\ +\texttt{c} --- display your conduct; also achievements, if any;\\ +\texttt{o} --- display dungeon overview. %.ei %.ed @@ -4282,45 +4281,45 @@ lets you refine how it behaves. Here are the valid prefixes: %.sd %.si -{\tt y} --- prompt you and default to yes on the prompt;\\ -{\tt n} --- prompt you and default to no on the prompt;\\ -{\tt +} --- disclose it without prompting;\\ -{\tt -} --- do not disclose it and do not prompt. +\texttt{y} --- prompt you and default to yes on the prompt;\\ +\texttt{n} --- prompt you and default to no on the prompt;\\ +\texttt{+} --- disclose it without prompting;\\ +\texttt{-} --- do not disclose it and do not prompt. %.ei %.ed The listing of vanquished monsters can be sorted, -so there are two additional choices for `{\tt v}': +so there are two additional choices for `\texttt{v}': The listings of vanquished monsters and of genocided types can be sorted, so there are two additional choices for `q' and `g': %.sd %.si -{\tt ?} --- prompt you and default to ask on the prompt;\\ +\texttt{?} --- prompt you and default to ask on the prompt;\\ {\tt\#} --- disclose it without prompting, ask for sort order. %.ei %.ed Asking refers to picking one of the orderings from a menu. -The `{\tt +}' disclose without prompting choice, -or being prompted and answering `{\tt y}' rather than `{\tt a}', +The `\texttt{+}' disclose without prompting choice, +or being prompted and answering `\texttt{y}' rather than `\texttt{a}', will default to showing monsters in the order specified by the -{\it sortvanquished\/} option. +\textit{sortvanquished} option. \\ %.lp "" -Omitted categories are implicitly added with `{\tt n}' prefix. -Specified categories with omitted prefix implicitly use `{\tt +}' prefix. +Omitted categories are implicitly added with `\texttt{n}' prefix. +Specified categories with omitted prefix implicitly use `\texttt{+}' prefix. Order of the disclosure categories does not matter, program display for end-of-game disclosure follows a set sequence. %.lp "" -(for example, ``{\tt disclose:yi na +v -g o}'') +(for example, ``\texttt{disclose:yi na +v -g o}'') The example sets -{\tt inventory} to {\it prompt\/} and default to {\it yes\/}, -{\tt attributes} to {\it prompt\/} and default to {\it no\/}, -{\tt vanquished} to {\it disclose without prompting\/}, -{\tt genocided} to {\it not disclose\/} and {\it not prompt\/}, -{\tt conduct} to implicitly {\it prompt\/} and default to {\it no\/}, -{\tt overview} to {\it disclose without prompting\/}. +\texttt{inventory} to \textit{prompt} and default to \textit{yes}, +\texttt{attributes} to \textit{prompt} and default to \textit{no}, +\texttt{vanquished} to \textit{disclose without prompting}, +\texttt{genocided} to \textit{not disclose} and \textit{not prompt}, +\texttt{conduct} to implicitly \textit{prompt} and default to \textit{no}, +\texttt{overview} to \textit{disclose without prompting}. %.lp "" Note that the vanquished monsters list includes all monsters killed by @@ -4329,8 +4328,8 @@ And the dungeon overview shows all levels you had visited but does not reveal things about them that you hadn't discovered. %.lp \item[dogname] -Name your starting dog (for example, ``{\tt dogname:Fang}''). -Cannot be set with the `{\tt O}' command. +Name your starting dog (for example, ``\texttt{dogname:Fang}''). +Cannot be set with the `\texttt{O}' command. %.lp \item[extmenu] Changes the extended commands interface to pop-up a menu of available commands. @@ -4344,11 +4343,11 @@ or just the subset of commands which have traditionally been considered extended ones (off). %.lp \item[female] -An obsolete synonym for ``{\tt gender:female}''. Cannot be set with the -`{\tt O}' command. +An obsolete synonym for ``\texttt{gender:female}''. Cannot be set with the +`\texttt{O}' command. %.lp \item[fireassist] -This option controls what happens when you attempt the `{\tt f}' (fire) +This option controls what happens when you attempt the `\texttt{f}' (fire) and don't have an appropriate launcher, such as a bow or a sling, wielded. If on, you will automatically wield the launcher. Default is on. %.lp @@ -4362,45 +4361,45 @@ Commands asking for an inventory item show a menu instead of a text query with possible menu letters. Default is off. %.lp \item[fruit] -Name a fruit after something you enjoy eating (for example, ``{\tt fruit:mango}'') -(default ``{\tt slime mold}''). Basically a nostalgic whimsy that -{\it NetHack\/} uses from time to time. You should set this to something you +Name a fruit after something you enjoy eating (for example, ``\texttt{fruit:mango}'') +(default ``\texttt{slime mold}''). Basically a nostalgic whimsy that +\textit{NetHack} uses from time to time. You should set this to something you find more appetizing than slime mold. Apples, oranges, pears, bananas, and -melons already exist in {\it NetHack\/}, so don't use those. +melons already exist in \textit{NetHack}, so don't use those. %.lp \item[gender] -Your starting gender ({\tt gender:male} or {\tt gender:female}). +Your starting gender (\texttt{gender:male} or \texttt{gender:female}). You may specify just the first letter. Although you can still denote your gender using either of the deprecated -``{\it male\/}'' and ``{\it female\/}'' -options, the ``{\it gender\/}'' option will take precedence. -See {\it role\/} +``\textit{male}'' and ``\textit{female}'' +options, the ``\textit{gender}'' option will take precedence. +See \textit{role} for a description of how to use negation to exclude choices. %.lp "" \\ -If {\tt gender} is not specified, there is no default value; +If \texttt{gender} is not specified, there is no default value; player will be prompted unless role and/or race forces a choice for gender. -Cannot be set with the `{\tt O}' command. Persistent. +Cannot be set with the `\texttt{O}' command. Persistent. %.lp \item[goldX] When filtering objects based on bless/curse state (BUCX), whether to -treat gold pieces as {\tt X} (unknown bless/curse state, when `on') -or {\tt U} (known to be uncursed, when `off', the default). +treat gold pieces as \texttt{X} (unknown bless/curse state, when `on') +or \texttt{U} (known to be uncursed, when `off', the default). Gold is never blessed or cursed, but it is not described as ``uncursed'' -even when the {\it implicit\textunderscore uncursed\/} option is `off'. +even when the \textit{implicit\textunderscore uncursed} option is `off'. %.lp \item[help] If more information is available for an object looked at -with the `{\tt /}' command, ask if you want to see it (default on). +with the `\texttt{/}' command, ask if you want to see it (default on). Turning help off makes just looking at things faster, since you aren't -interrupted with the ``{\tt More info?}'' prompt, but it also means that you +interrupted with the ``\texttt{More info?}'' prompt, but it also means that you might miss some interesting and/or important information. Persistent. %.lp \item[herecmd\textunderscore menu] When using a windowport that supports mouse and clicking on yourself or next to you, show a menu of possible actions for the location. -Same as ``{\tt \#herecmdmenu}'' and ``{\tt \#therecmdmenu}'' commands. +Same as ``\texttt{\#herecmdmenu}'' and ``\texttt{\#therecmdmenu}'' commands. %.lp \item[hilite\textunderscore pet] Visually distinguish pets from similar animals (default off). @@ -4409,9 +4408,9 @@ In text windowing, text highlighting or inverse video is often used; with tiles, generally displays a heart symbol near pets. %.lp "" -With the tty or curses interface, the {\it petattr\/} +With the tty or curses interface, the \textit{petattr} option controls how to highlight pets and setting it will turn the -{\it hilite\textunderscore pet\/} option on or off as warranted. +\textit{hilite\textunderscore pet} option on or off as warranted. %.lp \item[hilite\textunderscore pile] Visually distinguish piles of objects from individual objects (default off). @@ -4434,22 +4433,22 @@ the bar. If there is one for hitpoints in effect and it specifies color, that color will be used for the bar. However if it specifies video attributes, they will be ignored in -favor of {\it inverse}. -For tty and curses, {\it blink\/} will also be used if the current -hitpoint value is at or below the {\it critical HP\/} threshold. +favor of \textit{inverse}. +For tty and curses, \textit{blink} will also be used if the current +hitpoint value is at or below the \textit{critical HP} threshold. \\ %.lp The ``Qt'' interface also supports hitpointbar, by drawing a solid bar above the name and title with a hard-coded color scheme. (As of this writing, having the bar enabled unintentionally inhibits resizing the status panel. -To resize that, use the {\tt \#optionsfull} command to toggle the -{\it hitpointbar\/} option off, perform the resize while it's off, then +To resize that, use the \texttt{\#optionsfull} command to toggle the +\textit{hitpointbar} option off, perform the resize while it's off, then use the same command to toggle it back on.) %.lp \item[horsename] -Name your starting horse (for example, ``{\tt horsename:Trigger}''). -Cannot be set with the `{\tt O}' command. +Name your starting horse (for example, ``\texttt{horsename:Trigger}''). +Cannot be set with the `\texttt{O}' command. %.lp \item[ignintr] Ignore interrupt signals, including breaks (default off). Persistent. @@ -4472,16 +4471,16 @@ character as lit (default off). Persistent. %.lp \item[lootabc] When using a menu to interact with a container, -use the old `{\tt a}', `{\tt b}', and `{\tt c}' keyboard shortcuts -rather than the mnemonics `{\tt o}', `{\tt i}', and `{\tt b}' (default off). +use the old `\texttt{a}', `\texttt{b}', and `\texttt{c}' keyboard shortcuts +rather than the mnemonics `\texttt{o}', `\texttt{i}', and `\texttt{b}' (default off). Persistent. %.lp \item[mail] Enable mail delivery during the game (default on). Persistent. %.lp \item[male] -An obsolete synonym for ``{\tt gender:male}''. Cannot be set with the -`{\tt O}' command. +An obsolete synonym for ``\texttt{gender:male}''. Cannot be set with the +`\texttt{O}' command. %.lp \item[mention\textunderscore decor] Give feedback when walking onto various dungeon features such as stairs, @@ -4496,14 +4495,14 @@ Give feedback when walking against a wall (default off). Persistent. %.lp \item[menucolors] Enable coloring menu lines (default off). -See ``{\it Configuring Menu Colors\/}'' on how to configure the colors. +See ``\textit{Configuring Menu Colors}'' on how to configure the colors. %.lp \item[menustyle] Controls the method used when you need to choose various objects (in -response to the {\tt Drop} (aka {\tt droptype}) command, for instance). +response to the \texttt{Drop} (aka \texttt{droptype}) command, for instance). The value specified should be the first letter of one of the following: traditional, combination, full, or partial. -Default is {\tt full}. +Default is \texttt{full}. Persistent. \\ %.lp "" @@ -4519,7 +4518,7 @@ object classes rather than a character prompt, and then a menu of matching objects for selection. (Choosing its `A' (Autoselect-All) choice skips the second menu. To avoid choosing that by accident, -set {\it paranoid\textunderscore confirm:AutoAll\/} to require confirmation.) +set \textit{paranoid\textunderscore confirm:AutoAll} to require confirmation.) Partial skips the object class filtering and immediately displays a menu of all objects. \item[menu\textunderscore deselect\textunderscore all] @@ -4529,15 +4528,15 @@ Default `-'. \item[menu\textunderscore deselect\textunderscore page] Key to deselect all items on this page of a menu. Implemented by the Amiga, Gem and tty ports. -Default `\verb+\+'. +Default `\textbackslash'. \item[menu\textunderscore first\textunderscore page] Key to jump to the first page in a menu. Implemented by the Amiga, Gem and tty ports. -Default `\verb+^+'. +Default `\textasciicircum'. \item[menu\textunderscore headings] Controls how the headings in a menu are highlighted. Takes a text attribute, or text color and attribute separated by ampersand. -For allowed attributes and colors, see ``{\it Configuring Menu Colors\/}``. +For allowed attributes and colors, see ``\textit{Configuring Menu Colors}``. Not all ports can actually display all types. \item[menu\textunderscore invert\textunderscore all] Key to invert all items in a menu. @@ -4546,15 +4545,15 @@ Default `@'. \item[menu\textunderscore invert\textunderscore page] Key to invert all items on this page of a menu. Implemented by the Amiga, Gem and tty ports. -Default `\verb+~+'. +Default `\textasciitilde'. \item[menu\textunderscore last\textunderscore page] Key to jump to the last page in a menu. Implemented by the Amiga, Gem and tty ports. -Default `\verb+|+'. +Default `|'. \item[menu\textunderscore next\textunderscore page] Key to go to the next menu page. Implemented by the Amiga, Gem and tty ports. -Default `\verb+>+'. +Default `>'. \item[menu\textunderscore objsyms] % [originally menu_objsyms was a boolean] % Show object symbols in menu headings in menus where @@ -4589,7 +4588,7 @@ objects among classes. Supported by tty and curses. When setting the value, it can be specified by digit or keyword. -The default value is {\tt Conditional} (4). +The default value is \texttt{Conditional} (4). \item[menu\textunderscore overlay] Do not clear the screen before drawing menus, and align menus to the right edge of the screen. Only for the tty port. @@ -4597,7 +4596,7 @@ menus to the right edge of the screen. Only for the tty port. \item[menu\textunderscore previous\textunderscore page] Key to go to the previous menu page. Implemented by the Amiga, Gem and tty ports. -Default `\verb+<+'. +Default `<'. \item[menu\textunderscore search] Key to search for some text and toggle selection state of matching menu items. Default `:'. @@ -4614,15 +4613,15 @@ Default `,'. \item[menu\textunderscore shift\textunderscore left] Key to scroll a menu---one which has been scrolled right---back to the left. -Implemented for {\it perm\textunderscore invent\/} only by curses and X11. -Default `{\tt \verb+{+}'. +Implemented for \textit{perm\textunderscore invent} only by curses and X11. +Default `\texttt{\textbraceleft}'. %.lp \item[menu\textunderscore shift\textunderscore right] Key to scroll a menu which has text beyond the right edge to the right. -Implemented for {\it perm\textunderscore invent\/} only by curses by X11. -Default `{\tt \verb+}+}'. +Implemented for \textit{perm\textunderscore invent} only by curses by X11. +Default `\texttt{\textbraceright}'. % %.lp % \item[menu\textunderscore tab\textunderscore sep] % Format menu entries using TAB to separate columns (default off). @@ -4646,47 +4645,47 @@ Valid settings are: %.sd %.si -{\tt 0} --- disabled\\ -{\tt 1} --- enabled and make OS adjustments to support mouse use\\ -{\tt 2} --- like {\tt 1}, but does not make any OS adjustments\\ +\texttt{0} --- disabled\\ +\texttt{1} --- enabled and make OS adjustments to support mouse use\\ +\texttt{2} --- like \texttt{1}, but does not make any OS adjustments\\ %.ei %.ed -Omitting a value is the same as specifying {\tt 1} +Omitting a value is the same as specifying \texttt{1} and negating -{\it mouse\textunderscore support\/} -is the same as specifying {\tt 0}. +\textit{mouse\textunderscore support} +is the same as specifying \texttt{0}. %.lp \item[msghistory] The number of top line messages to save (and be able to recall -with `{\tt \^{}P}') (default 20). -Cannot be set with the `{\tt O}' command. +with `\texttt{\textasciicircum P}') (default 20). +Cannot be set with the `\texttt{O}' command. %.lp \item[msg\textunderscore window] Allows you to change the way recalled messages are displayed. Currently it is only supported for tty (all four choices) and for curses -(`{\tt f}' and `{\tt r}' choices, default `{\tt r}'). +(`\texttt{f}' and `\texttt{r}' choices, default `\texttt{r}'). The possible values are: %.sd %.si -{\tt s} --- single message (default; only choice prior to 3.4.0);\\ -{\tt c} --- combination, two messages as {\it single\/}, then as {\it full\/};\\ -{\tt f} --- full window, oldest message first;\\ -{\tt r} --- full window reversed, newest message first. +\texttt{s} --- single message (default; only choice prior to 3.4.0);\\ +\texttt{c} --- combination, two messages as \textit{single}, then as \textit{full};\\ +\texttt{f} --- full window, oldest message first;\\ +\texttt{r} --- full window reversed, newest message first. %.ei %.ed For backward compatibility, no value needs to be specified (which -defaults to {\it full\/}), or it can be negated (which defaults -to {\it single\/}). +defaults to \textit{full}), or it can be negated (which defaults +to \textit{single}). %.lp \item[name] Set your character's name (defaults to your user name). You can also set your character's role by appending a dash and one or more letters of the role (that is, by suffixing one of -``{\tt -A -B -C -H -K -M -P -Ra -Ro -S -T -V -W}''). -If ``{\tt -@}'' is used for the role, then a random one will be +``\texttt{-A -B -C -H -K -M -P -Ra -Ro -S -T -V -W}''). +If ``\texttt{-@}'' is used for the role, then a random one will be automatically chosen. %.lp \\ @@ -4694,12 +4693,12 @@ On some systems, the default is the player's user name; on others, there is no default and the player will be prompted. The former can made to behave like the latter by specifying a generic name such as ``player''. -Cannot be set with the `{\tt O}' command. +Cannot be set with the `\texttt{O}' command. %.lp \item[news] -Read the {\it NetHack\/} news file, if present (default on). +Read the \textit{NetHack} news file, if present (default on). Since the news is shown at the beginning of the game, there's no point -in setting this with the `{\tt O}' command. +in setting this with the `\texttt{O}' command. %.lp \item[nudist] Start the character with no armor (default false). Persistent. @@ -4714,44 +4713,44 @@ Valid settings are: %.sd %.si \newlength{\mwidth} -\settowidth{\mwidth}{\tt -0} -\newcommand{\numbox}[1]{\makebox[\mwidth][r]{{\tt #1}}} -\numbox{0} --- move by letters; `{\tt yuhjklbn}'\\ -\numbox{1} --- move by numbers; digit `{\tt 5}' acts as `{\tt G}' movement prefix\\ -\numbox{2} --- like {\tt 1} but `{\tt 5}' works as `{\tt g}' prefix instead of as `{\tt G}'\\ -\numbox{3} --- by numbers using phone key layout; {\tt 123} above, {\tt 789} below\\ -\numbox{4} --- combines {\tt 3} with {\tt 2}; phone layout plus MS-DOS compatibility\\ -\numbox{-1} --- by letters but use `{\tt z}' to go northwest, `{\tt y}' to zap wands +\settowidth{\mwidth}{\texttt{-0}} +\newcommand{\numbox}[1]{\makebox[\mwidth][r]{\texttt{#1}}} +\numbox{0} --- move by letters; `\texttt{yuhjklbn}'\\ +\numbox{1} --- move by numbers; digit `\texttt{5}' acts as `\texttt{G}' movement prefix\\ +\numbox{2} --- like \texttt{1} but `\texttt{5}' works as `\texttt{g}' prefix instead of as `\texttt{G}'\\ +\numbox{3} --- by numbers using phone key layout; \texttt{123} above, \texttt{789} below\\ +\numbox{4} --- combines \texttt{3} with \texttt{2}; phone layout plus MS-DOS compatibility\\ +\numbox{-1} --- by letters but use `\texttt{z}' to go northwest, `\texttt{y}' to zap wands %.ei %.ed -For backward compatibility, omitting a value is the same as specifying {\tt 1} +For backward compatibility, omitting a value is the same as specifying \texttt{1} and negating -{\it number\textunderscore pad\/} -is the same as specifying {\tt 0}. -(Settings {\tt 2} and {\tt 4} are for compatibility with MS-DOS or old PC Hack; -in addition to the different behavior for `{\tt 5}', `{\tt Alt-5}' acts as `{\tt G}' -and `{\tt Alt-0}' acts as `{\tt I}'. -Setting {\tt -1} is to accommodate some QWERTZ keyboards which have the -location of the `{\tt y}' and `{\tt z}' keys swapped.) +\textit{number\textunderscore pad} +is the same as specifying \texttt{0}. +(Settings \texttt{2} and \texttt{4} are for compatibility with MS-DOS or old PC Hack; +in addition to the different behavior for `\texttt{5}', `\texttt{Alt-5}' acts as `\texttt{G}' +and `\texttt{Alt-0}' acts as `\texttt{I}'. +Setting \texttt{-1} is to accommodate some QWERTZ keyboards which have the +location of the `\texttt{y}' and `\texttt{z}' keys swapped.) When moving by numbers, to enter a count prefix for those commands -which accept one (such as ``{\tt 12s}'' to search twelve times), precede it -with the letter `{\tt n}' (``{\tt n12s}''). +which accept one (such as ``\texttt{12s}'' to search twelve times), precede it +with the letter `\texttt{n}' (``\texttt{n12s}''). %.lp \item[packorder] Specify the order to list object types in (default -``\verb&")[%?+!=/(*`0_&''). The value of this option should be a string +``\texttt{")[\%?+!=/(*\textasciigrave 0\textunderscore}''). The value of this option should be a string containing the symbols for the various object types. Any omitted types are filled in at the end from the previous order. %.lp \item[paranoid\textunderscore confirmation] A space separated list of specific situations where alternate prompting is desired. -The default is ``{\it paranoid\textunderscore confirmation:pray swim trap}''. +The default is ``\textit{paranoid\textunderscore confirmation:pray swim trap}''. %.sd %.si \newlength{\pcwidth} -\settowidth{\pcwidth}{\tt Were-change} +\settowidth{\pcwidth}{\texttt{Were-change}} \addtolength{\pcwidth}{\labelsep} \begin{description}[leftmargin=\pcwidth, topsep=1mm, itemsep=0mm, labelwidth=*, font=\ttfamily, align=right] \item[Confirm] @@ -4759,47 +4758,47 @@ for any prompts which are set to require ``yes'' rather than `y', also require ``no'' to reject instead of accepting any non-yes response as no; changes pray and AutoAll to require ``yes'' or ``no'' too; \item[quit] -require ``{\tt yes}'' rather than `{\tt y}' to confirm quitting +require ``\texttt{yes}'' rather than `\texttt{y}' to confirm quitting the game or switching into non-scoring explore mode; \item[die] -require ``{\tt yes}'' rather than `{\tt y}' to confirm dying (not +require ``\texttt{yes}'' rather than `\texttt{y}' to confirm dying (not useful in normal play; applies to explore mode); \item[bones] -require ``{\tt yes}'' rather than `{\tt y}' to confirm saving +require ``\texttt{yes}'' rather than `\texttt{y}' to confirm saving bones data when dying in debug mode \item[attack] -require ``{\tt yes}'' rather than `{\tt y}' to confirm attacking +require ``\texttt{yes}'' rather than `\texttt{y}' to confirm attacking a peaceful monster; \item[wand-break] -require ``{\tt yes}'' rather than `{\tt y}' to confirm breaking -a wand with the {\it apply} command; +require ``\texttt{yes}'' rather than `\texttt{y}' to confirm breaking +a wand with the \textit{apply} command; \item[eating] -require ``{\tt yes}'' rather than `{\tt y}' to confirm whether to +require ``\texttt{yes}'' rather than `\texttt{y}' to confirm whether to continue eating; \item[Were-change] -require ``{\tt yes}'' rather than `{\tt y}' to confirm changing form due +require ``\texttt{yes}'' rather than `\texttt{y}' to confirm changing form due to lycanthropy when hero has polymorph control; \item[pray] -require `{\tt y}' to confirm an attempt to pray rather +require `\texttt{y}' to confirm an attempt to pray rather than immediately praying; on by default; (to require ``yes'' rather than just `y', set Confirm too); \item[trap] -require `{\tt y}' to confirm an attempt to move into or onto a known trap, +require `\texttt{y}' to confirm an attempt to move into or onto a known trap, unless doing so is considered to be harmless; when enabled, this confirmation is also used for moving into visible gas cloud regions; (to require ``yes'' rather than just `y', set Confirm too); -confirmation can be skipped by using the `{\tt m}' movement prefix; +confirmation can be skipped by using the `\texttt{m}' movement prefix; \item[swim] prevent walking into water or lava; on by default; (to deliberately step -onto/into such terrain when this is set, use the `{\tt m}' +onto/into such terrain when this is set, use the `\texttt{m}' movement prefix when adjacent); \item[AutoAll] require confirmation when the `A' (Autoselect-All) choice is selected -in object class filtering menus for {\it menustyle:Full}; +in object class filtering menus for \textit{menustyle:Full}; (to require ``yes'' rather than just `y', set Confirm too); \item[Remove] -require selection from inventory for `{\tt R}' and `{\tt T}' +require selection from inventory for `\texttt{R}' and `\texttt{T}' commands even when wearing just one applicable item; \item[all] turn on all of the above. @@ -4808,19 +4807,19 @@ turn on all of the above. %.ed By default, the pray, swim, and trap choices are enabled, the others disabled. To disable them without setting -any of the other choices, use ``{\it paranoid\textunderscore confirmation:none}''. +any of the other choices, use ``\textit{paranoid\textunderscore confirmation:none}''. To keep them enabled while setting any of the others, you can include them in the list, such as -``{\it par\-a\-noid\textunderscore con\-fir\-ma\-tion:attack~pray~swim~Remove\/}'' +``\textit{par\-a\-noid\textunderscore con\-fir\-ma\-tion:attack~pray~swim~Remove}'' or you can precede the first entry in the list with a plus sign, -``{\it paranoid\textunderscore confirmation:\verb|+|attack~Remove\/}''. +``\textit{paranoid\textunderscore confirmation:+attack~Remove}''. To remove an entry that has been previously set without removing others, precede the first entry in the list with a minus sign, -``{\it paranoid\textunderscore confirmation:-swim\/}. +``\textit{paranoid\textunderscore confirmation:-swim}. To both add some new entries and remove some old ones, you can use -multiple {\it paranoid\textunderscore confirmation\/} option settings, or you can -use the `{\tt \verb|+|}' form and list entries to be added by their name -and entries to be removed by `{\tt !}' and name. +multiple \textit{paranoid\textunderscore confirmation} option settings, or you can +use the `\texttt{+}' form and list entries to be added by their name +and entries to be removed by `\texttt{!}' and name. The positive (no `!') and negative (with `!') entries can be intermixed. %.lp @@ -4834,24 +4833,24 @@ If true, always display your current inventory in a window (default is false). This only makes sense for windowing system interfaces that implement this feature. For those that do, the -{\tt perminv\textunderscore mode} +\texttt{perminv\textunderscore mode} option can be used to refine what gets displayed -for {\it perm\textunderscore invent\/}. -Setting that to a value other than {\it none\/} -while {\it perm\textunderscore invent\/} is false will change it to true. +for \textit{perm\textunderscore invent}. +Setting that to a value other than \textit{none} +while \textit{perm\textunderscore invent} is false will change it to true. %.lp \item[perminv\textunderscore mode] Augments the -{\tt perm\textunderscore invent} +\texttt{perm\textunderscore invent} option. Value is one of %.PS "\f(CRin-use\fP" -\settowidth{\pcwidth}{\tt in-use} %reuse the paranoid_confirm width +\settowidth{\pcwidth}{\texttt{in-use}} %reuse the paranoid_confirm width \addtolength{\pcwidth}{\labelsep} \begin{description}[leftmargin=\pcwidth, topsep=1mm, itemsep=0mm, labelwidth=*, font=\mdseries, align=right] %.PL -\item[{\tt none}] -behave as if {\it perm\textunderscore invent\/} is false; +\item[\texttt{none}] +behave as if \textit{perm\textunderscore invent} is false; \item[{all}] show all inventory except for gold; \item[{full}] @@ -4860,18 +4859,18 @@ show full inventory including gold; only show items which are in use (worn, wielded, lit lamp). %.PE \end{description} -Default is {\it none\/} but if {\it perm\textunderscore invent\/} gets set to true -while it is {\it none\/} it will be changed to {\it all\/}. +Default is \textit{none} but if \textit{perm\textunderscore invent} gets set to true +while it is \textit{none} it will be changed to \textit{all}. %.lp "" \\ Note: if gold has been equipped in quiver/ammo-pouch then it will be -included for {\it all\/} despite that mode normally omitting gold. +included for \textit{all} despite that mode normally omitting gold. %.lp %.\" petattr is a wincap option but we'll document it here... \item[petattr] Specifies one or more text highlighting attributes to use when showing pets on the map. -Effectively a superset of the {\it hilite\textunderscore pet\/} boolean option. +Effectively a superset of the \textit{hilite\textunderscore pet} boolean option. Curses or tty interface only; value is one of none, bold, dim, underline, italic, blink, and inverse. Some of those choices might not work, @@ -4881,12 +4880,12 @@ depending upon terminal hardware or terminal emulation software. \item[pettype] Specify the type of your initial pet, if you are playing a character class that uses multiple types of pets; or choose to have no initial pet at all. -Possible values are ``{\tt cat}'', ``{\tt dog}'', ``{\tt horse}'' -and ``{\tt none}''. +Possible values are ``\texttt{cat}'', ``\texttt{dog}'', ``\texttt{horse}'' +and ``\texttt{none}''. If the choice is not allowed for the role you are currently playing, -it will be silently ignored. For example, ``{\tt horse}'' will only be +it will be silently ignored. For example, ``\texttt{horse}'' will only be honored when playing a knight. -Cannot be set with the `{\tt O}' command. +Cannot be set with the `\texttt{O}' command. %.lp \item[pickup\textunderscore burden] When you pick up an item that would exceed this encumbrance @@ -4895,42 +4894,42 @@ or overLoaded), you will be asked if you want to continue. (Default `S'). Persistent. %.lp \item[pickup\textunderscore stolen] -If this option is on and ``{\it autopickup\/}'' is also on, try to pick up +If this option is on and ``\textit{autopickup}'' is also on, try to pick up things that a monster stole from you, even if they aren't in -``{\it pickup\textunderscore types\/}'' or +``\textit{pickup\textunderscore types}'' or match an autopickup exception. Default is on. Persistent. %.lp \item[pickup\textunderscore thrown] -If this option is on and ``{\it autopickup\/}'' is also on, try to pick up +If this option is on and ``\textit{autopickup}'' is also on, try to pick up things that you threw, even if they aren't in -``{\it pickup\textunderscore types\/}'' or +``\textit{pickup\textunderscore types}'' or match an autopickup exception. Default is on. Persistent. %.lp \item[pickup\textunderscore types] -Specify the object types to be picked up when ``{\it autopickup\/}'' +Specify the object types to be picked up when ``\textit{autopickup}'' is on. Default is all types. Persistent. \\ %.lp "" The value is a list of object symbols, such as -{\tt \verb&pickup_types:$?!&} to pick up gold, scrolls, and potions. +\texttt{pickup\textunderscore types:\textdollar ?!\&} to pick up gold, scrolls, and potions. You can use -``{\it autopickup\textunderscore exception\/}'' -configuration file lines to further refine ``{\it autopickup\/}'' behavior. +``\textit{autopickup\textunderscore exception}'' +configuration file lines to further refine ``\textit{autopickup}'' behavior. \\ %.lp "" -There is no way to set {\it pickup\textunderscore types\/} to ``{\it none}''. -(Setting it to an empty value reverts to ``{\it all}''.) +There is no way to set \textit{pickup\textunderscore types} to ``\textit{none}''. +(Setting it to an empty value reverts to ``\textit{all}''.) If you want to avoid automatically picking up any types of items but do -want to have {\it autopickup\/} on in order to have -{\it autopickup\textunderscore exceptions\/} control what you do and don't pick -up, you can set {\it pickup\textunderscore types\/} to `{\tt .}'. -That is the type symbol for {\it venom\/} and you won't come across +want to have \textit{autopickup} on in order to have +\textit{autopickup\textunderscore exceptions} control what you do and don't pick +up, you can set \textit{pickup\textunderscore types} to `\texttt{.}'. +That is the type symbol for \textit{venom} and you won't come across any venom items so won't unintentionally pick such up. %.lp \item[pile\textunderscore limit] @@ -4942,7 +4941,7 @@ the objects'' since the pile size will always be at least that big; default value is 5. Persistent. %.lp \item[playmode] -Values are {\it normal\/}, {\it explore\/}, or {\it debug\/}. +Values are \textit{normal}, \textit{explore}, or \textit{debug}. Allows selection of explore mode (also known as discovery mode) or debug mode (also known as wizard mode) instead of normal play. Debug mode might only be allowed for someone logged in under a particular @@ -4952,9 +4951,9 @@ it when not allowed or not possible results in explore mode instead. Default is normal play. %.lp \item[pushweapon] -Using the `{\tt w}' (wield) command when already wielding +Using the `\texttt{w}' (wield) command when already wielding something pushes the old item into your alternate weapon slot (default off). -Likewise for the `{\tt a}' (apply) command if it causes the applied item to +Likewise for the `\texttt{a}' (apply) command if it causes the applied item to become wielded. Persistent. %.lp \item[query\textunderscore menu] @@ -4970,35 +4969,35 @@ objects or monsters is less intrusive. Default is off. Persistent. %.lp \item[race] -Choices are {\tt human}, {\tt dwarf}, {\tt elf}, {\tt gnome}, and -{\tt orc} but most roles restrict which of the non-human races are allowed. -See {\it role\/} +Choices are \texttt{human}, \texttt{dwarf}, \texttt{elf}, \texttt{gnome}, and +\texttt{orc} but most roles restrict which of the non-human races are allowed. +See \textit{role} for a description of how to use negation to exclude choices. %.lp "" \\ -If {\tt race} is not specified, there is no default value; +If \texttt{race} is not specified, there is no default value; player will be prompted unless role forces a choice for race. unless role forces a choice for race. -Cannot be set with the `{\tt O}' command. Persistent. +Cannot be set with the `\texttt{O}' command. Persistent. %.lp \item[rest\textunderscore on\textunderscore space] -Make the space bar a synonym for the `{\tt .}' (\#wait) command (default off). +Make the space bar a synonym for the `\texttt{.}' (\#wait) command (default off). Persistent. %.lp \item[role] -Pick your type of character (for example, ``{\tt role:Samurai}''); -synonym for ``{\it character\/}''. -See ``{\it name\/}'' for an alternate method of specifying your role. +Pick your type of character (for example, ``\texttt{role:Samurai}''); +synonym for ``\textit{character}''. +See ``\textit{name}'' for an alternate method of specifying your role. %.\" Normally only the first letter of the -%.\" value is examined; `r' is an exception with ``{\tt Rogue}'', -%.\" ``{\tt Ranger}'', and ``{\tt random}'' values. +%.\" value is examined; `r' is an exception with ``\texttt{Rogue}'', +%.\" ``\texttt{Ranger}'', and ``\texttt{random}'' values. %.lp "" This option can also be used to limit selection when role is chosen randomly. Use a space-separated list of roles and either negate each one or negate the option itself instead. -Negation is accomplished in the same manner as with {\it boolean options\/}, -by prefixing the option or its value(s) with `{\tt \verb+!+}' or ``{\tt no}''. +Negation is accomplished in the same manner as with \textit{boolean options}, +by prefixing the option or its value(s) with `\texttt{!}' or ``\texttt{no}''. %.BR 0 \\ Examples: @@ -5007,19 +5006,19 @@ Examples: OPTIONS=role:!arc !bar !kni OPTIONS=!role:arc bar kni \end{verbatim} -There can be multiple instances of the {\it role\/} +There can be multiple instances of the \textit{role} option if they're all negations. %.\" Only one positive value is allowed, and if present, it overrides any %.\" negations. %.lp "" \\ -If {\tt role} is not specified, there is no default value; +If \texttt{role} is not specified, there is no default value; player will be prompted. -Cannot be set with the `{\tt O}' command. Persistent. +Cannot be set with the `\texttt{O}' command. Persistent. %.lp \item[roguesymset] This option may be used to select one of the named symbol sets found within -{\tt symbols} to alter the symbols displayed on the screen on the +\texttt{symbols} to alter the symbols displayed on the screen on the rogue level. %.lp \item[rlecomp] @@ -5029,23 +5028,23 @@ effect on reading an existing save file. %.lp \item[runmode] Controls the amount of screen updating for the map window when engaged -in multi-turn movement (running via {\tt shift}+direction -or {\tt control}+direction +in multi-turn movement (running via \texttt{shift}+direction +or \texttt{control}+direction and so forth, or via the travel command or mouse click). The possible values are: %.sd %.si -{\tt teleport} --- update the map after movement has finished;\\ -{\tt run} --- update the map after every seven or so steps;\\ -{\tt walk} --- update the map after each step;\\ -{\tt crawl} --- like {\it walk\/}, but pause briefly after each step. +\texttt{teleport} --- update the map after movement has finished;\\ +\texttt{run} --- update the map after every seven or so steps;\\ +\texttt{walk} --- update the map after each step;\\ +\texttt{crawl} --- like \textit{walk}, but pause briefly after each step. %.ei %.ed This option only affects the game's screen display, not the actual -results of moving. The default is {\it run\/}; versions prior to 3.4.1 -used {\it teleport\/} only. Whether or not the effect is noticeable will +results of moving. The default is \textit{run}; versions prior to 3.4.1 +used \textit{teleport} only. Whether or not the effect is noticeable will depend upon the window port used or on the type of terminal. Persistent. %.lp \item[safe\textunderscore pet] @@ -5061,8 +5060,8 @@ Debug mode only. %.lp \item[scores] Control what parts of the score list you are shown at the end (for example, -``{\tt scores:5top scores/4around my score/own scores}''). Only the first -letter of each category (`{\tt t}', `{\tt a}' or `{\tt o}') is necessary. +``\texttt{scores:5top scores/4around my score/own scores}''). Only the first +letter of each category (`\texttt{t}', `\texttt{a}' or `\texttt{o}') is necessary. Persistent. %.lp \item[showdamage] @@ -5089,9 +5088,9 @@ Include the game's version number on the status lines (default off). Potentially useful if you switch between different versions or variants, or you are making screenshots or streaming video. Using the -{\it statuslines:3\/} +\textit{statuslines:3} option is recommended so that there will be more room available for -status information, unless you're using nethack's {\it Qt\/} interface +status information, unless you're using nethack's \textit{Qt} interface or your terminal emulator window displays fewer than 25 lines. Persistent. %.lp @@ -5099,29 +5098,29 @@ Persistent. Suppress terminal beeps (default on). Persistent. %.lp \item[sortdiscoveries] -Controls the sorting behavior for the output of the `{\tt $\backslash$}' -and `{\tt \`{}}' commands. +Controls the sorting behavior for the output of the `\texttt{\textbackslash}' +and `\texttt{\`{}}' commands. Persistent. \\ %.lp "" The possible values are: \\ %.PS -{\tt o} --- list object types by class, in discovery order within each class; +\texttt{o} --- list object types by class, in discovery order within each class; default; \\ -{\tt s} --- list object types by {\it sortloot\/} +\texttt{s} --- list object types by \textit{sortloot} classification: by class, by sub-class within class for classes which have substantial groupings (like helmets, boots, gloves, and so forth for armor), with object types partly-discovered via assigned name coming before fully identified types; \\ -{\tt c} --- list by class, alphabetically within each class;\\ -{\tt a} --- list alphabetically across all classes.\\ +\texttt{c} --- list by class, alphabetically within each class;\\ +\texttt{a} --- list alphabetically across all classes.\\ %.PE -Can be interactively set via the `{\tt O}' command or via using -the `{\tt m}' prefix before the `{\tt $\backslash$}' -or `{\tt \`{}}' command. +Can be interactively set via the `\texttt{O}' command or via using +the `\texttt{m}' prefix before the `\texttt{\textbackslash}' +or `\texttt{\textasciigrave}' command. %.lp \item[sortloot] Controls the sorting behavior of pickup lists for inventory @@ -5131,10 +5130,10 @@ The possible values are: \\ %.sd %.si -{\tt full} --- always sort the lists;\\ -{\tt loot} --- only sort the lists that don't use inventory +\texttt{full} --- always sort the lists;\\ +\texttt{loot} --- only sort the lists that don't use inventory letters, like with the \#loot and pickup commands;\\ -{\tt none} --- show lists the traditional way without sorting; default. +\texttt{none} --- show lists the traditional way without sorting; default. %.ei %.ed %.lp @@ -5143,45 +5142,45 @@ Sort the pack contents by type when displaying inventory (default on). Persistent. %.lp \item[sortvanquished] -Controls the sorting behavior for the output of the {\tt \#vanquished} command -and also for the {\tt \#genocided} command. +Controls the sorting behavior for the output of the \texttt{\#vanquished} command +and also for the \texttt{\#genocided} command. Persistent. \\ %.lp "" The possible values are: \\ %.PS -{\tt t} --- +\texttt{t} --- traditional: order by monster level; ties are broken by internal monster index; default; \\ -{\tt d} --- +\texttt{d} --- order by monster difficulty rating; ties broken by internal index; \\ -{\tt a} --- +\texttt{a} --- order alphabetically, first any unique monsters then all the others; \\ %note: 'A' and 'C' can be set in RC file or NETHACKOPTIONS but not by 'O' -% {\tt A} --- +% \texttt{A} --- % order alphabetically, unique monsters intermixed with other monsters; % \\ -% {\tt C} --- +% \texttt{C} --- % order by monster class, by high to low level within each class; % \\ -{\tt c} --- +\texttt{c} --- order by monster class, by low to high level within each class; \\ -{\tt n} --- +\texttt{n} --- order by count, high to low; ties are broken by internal monster index; \\ -{\tt z} --- +\texttt{z} --- order by count, low to high; ties broken by internal index. \\ %.PE -Can be interactively set via the `{\tt m O}' command or via using -the `{\tt m}' prefix before either the {\tt \#vanquished} command -or the {\tt \#genocided} command. +Can be interactively set via the `\texttt{m O}' command or via using +the `\texttt{m}' prefix before either the \texttt{\#vanquished} command +or the \texttt{\#genocided} command. %.lp \item[sounds] Allow sounds to be emitted from an integrated sound library (default on). @@ -5194,25 +5193,25 @@ attack to which it is resistant (default on). Persistent. Show a message when hero notices a monster (default is off). %.lp \item[standout] -Boldface monsters and ``{\tt --More--}'' (default off). Persistent. +Boldface monsters and ``\texttt{--More--}'' (default off). Persistent. %.lp \item[statushilites] Controls how many turns status hilite behaviors highlight the field. If negated or set to zero, disables status hiliting. -See ``{\it Configuring Status Hilites\/}'' for further information. +See ``\textit{Configuring Status Hilites}'' for further information. %.lp \item[status\textunderscore updates] Allow updates to the status lines at the bottom of the screen (default true). %.lp \item[suppress\textunderscore alert] -This option may be set to a {\it NetHack\/} version level to suppress +This option may be set to a \textit{NetHack} version level to suppress alert notification messages about feature changes for that -and prior versions (for example, ``{\tt suppress\textunderscore alert:3.3.1}'') +and prior versions (for example, ``\texttt{suppress\textunderscore alert:3.3.1}'') %.lp \item[symset] This option may be used to select one of the named symbol sets found within -{\tt symbols} to alter the symbols displayed on the screen. -Use ``{\tt symset:default}'' to explicitly select the default symbols. +\texttt{symbols} to alter the symbols displayed on the screen. +Use ``\texttt{symset:default}'' to explicitly select the default symbols. %.lp \item[time] Show the elapsed game time in turns on bottom line (default off). Persistent. @@ -5231,16 +5230,16 @@ Show some helpful tips during gameplay (default on). Persistent. Draw a tombstone graphic upon your death (default on). Persistent. %.lp \item[toptenwin] -Put the ending display in a {\it NetHack\/} window instead of on stdout (default off). +Put the ending display in a \textit{NetHack} window instead of on stdout (default off). Setting this option makes the score list visible when a windowing version -of {\it NetHack\/} is started without a parent window, but it no longer leaves +of \textit{NetHack} is started without a parent window, but it no longer leaves the score list around after game end on a terminal or emulating window. %.lp \item[travel] Allow the travel command via mouse click (default on). Turning this option off will prevent the game from attempting unintended moves if you make inadvertent mouse clicks on the map window. -Does not affect traveling via the `{\tt \textunderscore }' (``{\tt \#travel}'') +Does not affect traveling via the `\texttt{\textunderscore}' (``\texttt{\#travel}'') command. Persistent. % %.lp % \item[ib{travel\textunderscore debug}] @@ -5255,8 +5254,8 @@ Setting this option on or off in the config file will skip the query. Provide more commentary during the game (default on). Persistent. %.lp \item[whatis\textunderscore coord] -When using the `{\tt /}' or `{\tt ;}' commands to look around on the map with -``{\tt autodescribe}'' +When using the `\texttt{/}' or `\texttt{;}' commands to look around on the map with +``\texttt{autodescribe}'' on, display coordinates after the description. Also works in other situations where you are asked to pick a location.\\ @@ -5265,21 +5264,21 @@ The possible settings are: %.sd %.si -{\tt c} --- \verb#compass ('east' or '3s' or '2n,4w')#;\\ -{\tt f} --- \verb#full compass ('east' or '3south' or '2north,4west')#;\\ -{\tt m} --- \verb#map (map column x=0 is not used)#;\\ -{\tt s} --- \verb#screen [row,column] (row is offset to match tty usage)#;\\ -{\tt n} --- \verb#none (no coordinates shown) [default]#. +\texttt{c} --- \texttt{compass ('east' or '3s' or '2n,4w')};\\ +\texttt{f} --- \texttt{full compass ('east' or '3south' or '2north,4west')};\\ +\texttt{m} --- \texttt{map (map column x=0 is not used)};\\ +\texttt{s} --- \texttt{screen [row,column] (row is offset to match tty usage)};\\ +\texttt{n} --- \texttt{none (no coordinates shown) [default]}. %.ei %.ed %.lp "" The -{\it whatis\textunderscore coord\/} +\textit{whatis\textunderscore coord} option is also used with -the `{\tt /m}', `{\tt /M}', `{\tt /o}', and `{\tt /O}' sub-commands -of `{\tt /}', -where the `{\it none\/}' setting is overridden with `{\it map}'. +the `\texttt{/m}', `\texttt{/M}', `\texttt{/o}', and `\texttt{/O}' sub-commands +of `\texttt{/}', +where the `\textit{none}' setting is overridden with `\textit{map}'. %.lp \item[whatis\textunderscore filter] When getting a location on the map, and using the keys to cycle through @@ -5290,9 +5289,9 @@ The possible settings are: %.sd %.si -{\tt n} --- \verb#no filtering#;\\ -{\tt v} --- \verb#in view only#;\\ -{\tt a} --- \verb#in same area (room, corridor, etc)#. +\texttt{n} --- \texttt{no filtering};\\ +\texttt{v} --- \texttt{in view only};\\ +\texttt{a} --- \texttt{in same area (room, corridor, etc)}. %.ei %.ed %.lp "" @@ -5317,9 +5316,9 @@ move by skipping the same glyphs. %.lp \item[windowtype] When the program has been built to support multiple interfaces, -select whichone to use, such as ``{\tt tty}'' or ``{\tt X11}'' -(default depends on build-time settings; use ``{\tt \#version}'' to check). -Cannot be set with the `{\tt O}' command. +select whichone to use, such as ``\texttt{tty}'' or ``\texttt{X11}'' +(default depends on build-time settings; use ``\texttt{\#version}'' to check). +Cannot be set with the `\texttt{O}' command. %.lp "" When used, it should be the first option set since its value might @@ -5327,7 +5326,7 @@ enable or disable the availability of various other options. For multiple lines in a configuration file, that would be the first non-comment line. For a comma-separated list in NETHACKOPTIONS or an OPTIONS line in a -configuration file, that would be the {\it rightmost\/} option in the list. +configuration file, that would be the \textit{rightmost} option in the list. %.lp \item[wizweight] Augment object descriptions with their objects' weight (default off). @@ -5355,9 +5354,9 @@ can't it will silently ignore it. You can find out if an option is supported by the window port that you are currently using by checking to see if it shows up in the Options list. Some options are dynamic and can be specified during the game -with the `{\tt O}' command. +with the `\texttt{O}' command. -\begin{description}[font=\itshape] +\begin{description}[font=\mdseries\itshape] %.lp \item[align\textunderscore message] Where to align or place the message window (top, bottom, left, or right) @@ -5368,57 +5367,57 @@ with the `{\tt O}' command. \item[ascii\textunderscore map] %.hw DECgraphics IBMgraphics \% don't hyphenate these \hyphenation{DECgraphics IBMgraphics} -If {\it NetHack\/} can, it should display the map using simple -characters (letters and punctuation) rather than {\it tiles\/} graphics. +If \textit{NetHack} can, it should display the map using simple +characters (letters and punctuation) rather than \textit{tiles} graphics. In some cases, characters can be augmented with line-drawing symbols; -use the {\tt symset} -option to select a symbol set such as {\it DECgraphics\/} -or {\it IBMgraphics\/} if your display supports them. -Setting {\tt ascii\textunderscore map} to {\it True\/} forces -{\tt tiled\textunderscore map} to be {\it False}. +use the \texttt{symset} +option to select a symbol set such as \textit{DECgraphics} +or \textit{IBMgraphics} if your display supports them. +Setting \texttt{ascii\textunderscore map} to \textit{True} forces +\texttt{tiled\textunderscore map} to be \textit{False}. %.lp \item[color] -If {\it NetHack\/} can, it should display color for different monsters, +If \textit{NetHack} can, it should display color for different monsters, objects, and dungeon features (default on). %.lp \item[eight\textunderscore bit\textunderscore tty] -If {\it NetHack\/} can, it should pass eight-bit character values (for example, specified with the -{\it traps \/} option) straight through to your terminal (default off). +If \textit{NetHack} can, it should pass eight-bit character values (for example, specified with the +\textit{traps } option) straight through to your terminal (default off). %.lp \item[font\textunderscore map] -If {\it NetHack\/} can, it should use a font by the chosen name for the +If \textit{NetHack} can, it should use a font by the chosen name for the map window. %.lp \item[font\textunderscore menu] -If {\it NetHack\/} can, it should use a font by the chosen name for menu +If \textit{NetHack} can, it should use a font by the chosen name for menu windows. %.lp \item[font\textunderscore message] -If {\it NetHack\/} can, it should use a font by the chosen name for the message window. +If \textit{NetHack} can, it should use a font by the chosen name for the message window. %.lp \item[font\textunderscore status] -If {\it NetHack\/} can, it should use a font by the chosen name for the status window. +If \textit{NetHack} can, it should use a font by the chosen name for the status window. %.lp \item[font\textunderscore text] -If {\it NetHack\/} can, it should use a font by the chosen name for text windows. +If \textit{NetHack} can, it should use a font by the chosen name for text windows. %.lp \item[font\textunderscore size\textunderscore map] -If {\it NetHack\/} can, it should use this size font for the map window. +If \textit{NetHack} can, it should use this size font for the map window. %.lp \item[font\textunderscore size\textunderscore menu] -If {\it NetHack\/} can, it should use this size font for menu windows. +If \textit{NetHack} can, it should use this size font for menu windows. %.lp \item[font\textunderscore size\textunderscore message] -If {\it NetHack\/} can, it should use this size font for the message window. +If \textit{NetHack} can, it should use this size font for the message window. %.lp \item[font\textunderscore size\textunderscore status] -If {\it NetHack\/} can, it should use this size font for the status window. +If \textit{NetHack} can, it should use this size font for the status window. %.lp \item[font\textunderscore size\textunderscore text] -If {\it NetHack\/} can, it should use this size font for text windows. +If \textit{NetHack} can, it should use this size font for text windows. %.lp \item[fullscreen] -If {\it NetHack\/} can, it should try to display on the entire screen rather than in a window. +If \textit{NetHack} can, it should try to display on the entire screen rather than in a window. %.lp \item[guicolor] Use color text and/or highlighting attributes when displaying some @@ -5426,77 +5425,77 @@ non-map data (such as menu selector letters). Curses interface only; default is on. %.lp \item[large\textunderscore font] -If {\it NetHack\/} can, it should use a large font. +If \textit{NetHack} can, it should use a large font. %.lp \item[map\textunderscore mode] -If {\it NetHack\/} can, it should display the map in the manner specified. +If \textit{NetHack} can, it should display the map in the manner specified. %.lp \item[player\textunderscore selection] -If {\it NetHack\/} can, it should pop up dialog boxes or use prompts for character selection. +If \textit{NetHack} can, it should pop up dialog boxes or use prompts for character selection. %.lp \item[popup\textunderscore dialog] -If {\it NetHack\/} can, it should pop up dialog boxes for input. +If \textit{NetHack} can, it should pop up dialog boxes for input. %.lp \item[preload\textunderscore tiles] -If {\it NetHack\/} can, it should preload tiles into memory. +If \textit{NetHack} can, it should preload tiles into memory. For example, in the protected mode MS-DOS version, control whether tiles get pre-loaded into RAM at the start of the game. Doing so enhances performance of the tile graphics, but uses more memory. (default on). -Cannot be set with the `{\tt O}' command. +Cannot be set with the `\texttt{O}' command. %.lp \item[scroll\textunderscore amount] -If {\it NetHack\/} can, it should scroll the display by this number of cells +If \textit{NetHack} can, it should scroll the display by this number of cells when the hero reaches the scroll\textunderscore margin. %.lp \item[scroll\textunderscore margin] -If {\it NetHack\/} can, it should scroll the display when the hero or cursor +If \textit{NetHack} can, it should scroll the display when the hero or cursor is this number of cells away from the edge of the window. %.lp \item[selectsaved] -If {\it NetHack\/} can, it should display a menu of existing saved games for the player to +If \textit{NetHack} can, it should display a menu of existing saved games for the player to choose from at game startup, if it can. Not all ports support this option. %.lp \item[softkeyboard] -If {\it NetHack\/} can, it should display an onscreen keyboard. +If \textit{NetHack} can, it should display an onscreen keyboard. Handhelds are most likely to support this option. %.lp \item[splash\textunderscore screen] -If {\it NetHack\/} can, it should display an opening splash screen when +If \textit{NetHack} can, it should display an opening splash screen when it starts up (default yes). %.lp \item[statuslines] Number of lines for traditional below-the-map status display. -Acceptable values are {\tt 2} and {\tt 3} (default is {\tt 2}). +Acceptable values are \texttt{2} and \texttt{3} (default is \texttt{2}). %.lp "" -When set to {\tt 3}, the {\tt tty} interface moves some fields around and +When set to \texttt{3}, the \texttt{tty} interface moves some fields around and mainly shows status conditions on their own line. A display capable of showing at least 25 lines is recommended. -The value can be toggled back and forth during the game with the `{\tt O}' +The value can be toggled back and forth during the game with the `\texttt{O}' command. %.lp "" -The {\tt curses} interface does likewise if the -{\it align\textunderscore status\/} -option is set to {\it top\/} or {\it bottom\/} but ignores -{\it statuslines\/} -when set to {\it left\/} or {\it right}. +The \texttt{curses} interface does likewise if the +\textit{align\textunderscore status} +option is set to \textit{top} or \textit{bottom} but ignores +\textit{statuslines} +when set to \textit{left} or \textit{right}. %.lp "" -The {\tt Qt} interface already displays more than 3 lines for status +The \texttt{Qt} interface already displays more than 3 lines for status so uses the -{\it statuslines\/} +\textit{statuslines} value differently. -A value of {\tt 3} renders status in the {\tt Qt} interface's +A value of \texttt{3} renders status in the \texttt{Qt} interface's original format, with the status window spread out vertically. -A value of {\tt 2} makes status be slightly condensed, moving some +A value of \texttt{2} makes status be slightly condensed, moving some fields to different lines to eliminate one whole line, reducing the height needed. -(If NetHack has been built using a version of {\tt Qt} -older than {\tt qt-5.9}, -{\it statuslines\/} +(If NetHack has been built using a version of \texttt{Qt} +older than \texttt{qt-5.9}, +\textit{statuslines} can only be set in the run-time configuration file or via NETHACKOPTIONS, -not during play with the `{\tt O}' command.) +not during play with the `\texttt{O}' command.) %.lp \item[term\textunderscore cols \textrm{and}] %.lp @@ -5513,7 +5512,7 @@ Specify the name of an alternative tile file to override the default. %.lp "" Note: the X11 interface uses X resources rather than NetHack's options to select an alternate tile file. -See {\tt NetHack.ad}, the sample X ``application defaults'' file. +See \texttt{NetHack.ad}, the sample X ``application defaults'' file. %.lp \item[tile\textunderscore height] Specify the preferred height of each tile in a tile capable port. @@ -5522,24 +5521,24 @@ Specify the preferred height of each tile in a tile capable port. Specify the preferred width of each tile in a tile capable port %.lp \item[tiled\textunderscore map] -If {\it NetHack\/} can, it should display the map using {\it tiles} graphics +If \textit{NetHack} can, it should display the map using \textit{tiles} graphics rather than simple characters (letters and punctuation, possibly augmented by line-drawing symbols). -Setting {\tt tiled\textunderscore map} to {\it True\/} forces -{\tt ascii\textunderscore map} to be {\it False}. +Setting \texttt{tiled\textunderscore map} to \textit{True} forces +\texttt{ascii\textunderscore map} to be \textit{False}. %.lp \item[use\textunderscore darkgray] Use bold black instead of blue for black glyphs (TTY only). %.lp \item[use\textunderscore inverse] -If {\it NetHack\/} can, it should display inverse when the game specifies it. +If \textit{NetHack} can, it should display inverse when the game specifies it. %.lp \item[use\textunderscore menu\textunderscore glyphs] -If {\it NetHack\/} can, it should display glyphs next to objects in the +If \textit{NetHack} can, it should display glyphs next to objects in the inventory. %.lp \item[vary\textunderscore msgcount] -If {\it NetHack\/} can, it should display this number of messages at a time +If \textit{NetHack} can, it should display this number of messages at a time in the message window. %.lp \item[windowborders] @@ -5550,21 +5549,21 @@ Acceptable values are %.sd %.si -{\tt 0} --- off, never show borders\\ -{\tt 1} --- on, always show borders\\ -{\tt 2} --- auto, on display is at least -(\verb&24+2&)x(\verb&80+2&) [default]\\ -{\tt 3} --- on, except forced off for perm\textunderscore invent\\ -{\tt 4} --- auto, except forced off for perm\textunderscore invent\\ +\texttt{0} --- off, never show borders\\ +\texttt{1} --- on, always show borders\\ +\texttt{2} --- auto, on display is at least +$(24+2)\times(80+2)$ [default]\\ +\texttt{3} --- on, except forced off for perm\textunderscore invent\\ +\texttt{4} --- auto, except forced off for perm\textunderscore invent\\ %.ei %.ed %.lp "" (The 26x82 size threshold for `2' refers to number of rows and columns of the display. -A width of at least 110 columns (\verb&80+2+26+2&) is needed for -{\it align\textunderscore status\/} -set to {\tt left} or {\tt right}.) +A width of at least 110 columns ($80+2+26+2$) is needed for +\textit{align\textunderscore status} +set to \texttt{left} or \texttt{right}.) %.lp "" The persistent inventory window, when enabled, can grow until it is @@ -5575,32 +5574,32 @@ and status windows but have room for two additional lines of inventory plus widen each inventory line by two columns. %.lp \item[windowcolors] -If {\it NetHack\/} can, it should display all windows of a particular style +If \textit{NetHack} can, it should display all windows of a particular style with the specified foreground and background colors. Windows GUI and curses windowport only. The format is\\ -{\tt ~~~~OPTION=windowcolors:}{\it style foreground\/}{\tt /}{\it background}\\ -where {\it style} is one of {\tt menu}, {\tt message}, {\tt status}, -or {\tt text}, and -{\it foreground} and {\it background} are colors, either numeric (hash -sign followed by three pairs of hexadecimal digits, {\it \#rrggbb\/}), -one of the named colors ({\it black}, {\it red}, {\it green}, {\it brown}, -{\it blue}, {\it magenta}, {\it cyan}, {\it orange}, -{\it bright-green}, {\it yellow}, {\it bright-blue}, {\it bright-magenta}, -{\it bright-cyan}, {\it white}, {\it gray}, {\it purple}, -{\it silver}, {\it maroon}, {\it fuchsia}, {\it lime}, {\it olive}, -{\it navy}, {\it teal}, {\it aqua}), -or (for Windows only) one of Windows UI colors ({\it trueblack}, -{\it activeborder}, {\it activecaption}, {\it appworkspace}, {\it background}, -{\it btnface}, {\it btnshadow}, {\it btntext}, {\it captiontext}, -{\it graytext}, {\it greytext}, {\it highlight}, -{\it highlighttext}, {\it inactiveborder}, {\it inactivecaption}, {\it menu}, -{\it menutext}, {\it scrollbar}, {\it window}, {\it windowframe}, -{\it windowtext}). +\texttt{~~~~OPTION=windowcolors:}\textit{style foreground}\texttt{/}\textit{background}\\ +where \textit{style} is one of \texttt{menu}, \texttt{message}, \texttt{status}, +or \texttt{text}, and +\textit{foreground} and \textit{background} are colors, either numeric (hash +sign followed by three pairs of hexadecimal digits, \textit{\#rrggbb}), +one of the named colors (\textit{black}, \textit{red}, \textit{green}, \textit{brown}, +\textit{blue}, \textit{magenta}, \textit{cyan}, \textit{orange}, +\textit{bright-green}, \textit{yellow}, \textit{bright-blue}, \textit{bright-magenta}, +\textit{bright-cyan}, \textit{white}, \textit{gray}, \textit{purple}, +\textit{silver}, \textit{maroon}, \textit{fuchsia}, \textit{lime}, \textit{olive}, +\textit{navy}, \textit{teal}, \textit{aqua}), +or (for Windows only) one of Windows UI colors (\textit{trueblack}, +\textit{activeborder}, \textit{activecaption}, \textit{appworkspace}, \textit{background}, +\textit{btnface}, \textit{btnshadow}, \textit{btntext}, \textit{captiontext}, +\textit{graytext}, \textit{greytext}, \textit{highlight}, +\textit{highlighttext}, \textit{inactiveborder}, \textit{inactivecaption}, \textit{menu}, +\textit{menutext}, \textit{scrollbar}, \textit{window}, \textit{windowframe}, +\textit{windowtext}). %.lp \item[wraptext] -If {\it NetHack\/} can, it should wrap long lines of text if they don't fit +If \textit{NetHack} can, it should wrap long lines of text if they don't fit in the visible area of the window. \end{description} @@ -5613,9 +5612,9 @@ computer unless you manually click submit on a form. %.si \begin{description} %.lp -\item[OPTION=crash\textunderscore email:{\it email\textunderscore address}] +\item[OPTION=crash\textunderscore email:\textit{email\textunderscore address}] %.lp -\item[OPTION=crash\textunderscore name:{\it your\textunderscore name}] +\item[OPTION=crash\textunderscore name:\textit{your\textunderscore name}] %.ei \end{description} These options are used only to save you some typing on the crash @@ -5623,7 +5622,7 @@ report and \#bugreport forms. %.si \begin{description} %.lp -\item[OPTION=crash\textunderscore urlmax:{\it bytes}] +\item[OPTION=crash\textunderscore urlmax:\textit{bytes}] %.ei \end{description} This option is used to limit the length of the URLs generated and is only @@ -5636,11 +5635,11 @@ needed if your browser cannot handle arbitrarily long URLs. Here are explanations of options that are used by specific platforms or ports to customize and change the port behavior. -\begin{description}[font=\itshape] +\begin{description}[font=\mdseries\itshape] %.lp \item[altkeyhandling] -Select an alternate way to handle keystrokes ({\it Win32 tty\/ NetHack\/} only). -The name of the handling type is one of {\it default}, {\it ray}, {\it 340} +Select an alternate way to handle keystrokes (\textit{Win32 tty NetHack} only). +The name of the handling type is one of \textit{default}, \textit{ray}, \textit{340} %.\" \item[altmeta] %.\" On Amiga, this option controls whether typing ``Alt'' plus another key %.\" functions as a meta-shift for that key (default on). @@ -5648,16 +5647,16 @@ The name of the handling type is one of {\it default}, {\it ray}, {\it 340} \item[altmeta] %.\" On other (non-Amiga) systems where this option is available, it can be On systems where this option is available, it can be -set to tell {\it NetHack\/} to convert a two character sequence beginning with +set to tell \textit{NetHack} to convert a two character sequence beginning with ESC into a meta-shifted version of the second character (default off). %.lp "" This conversion is only done for commands, not for other input prompts. Note that typing one or more digits as a count prefix prior to a -command---preceded by {\tt n} if the {\it number\textunderscore pad\/} +command---preceded by \texttt{n} if the \textit{number\textunderscore pad} option is set---is also subject to this conversion, so attempting to -abort the count by typing ESC will leave {\it NetHack\/} waiting for another +abort the count by typing ESC will leave \textit{NetHack} waiting for another character to complete the two character sequence. Type a second ESC to finish cancelling such a count. At other prompts a single ESC suffices. @@ -5665,37 +5664,37 @@ At other prompts a single ESC suffices. \item[BIOS] Use BIOS calls to update the screen display quickly and to read the keyboard (allowing the use of arrow keys to move) on machines with an IBM PC -compatible BIOS ROM (default off, {\it OS/2, PC\/ {\rm and} ST NetHack\/} only). +compatible BIOS ROM (default off, \textit{OS/2, PC {\rm and} ST NetHack} only). %.lp \item[rawio] Force raw (non-cbreak) mode for faster output and more -bulletproof input (MS-DOS sometimes treats `{\tt \^{}P}' as a printer toggle -without it) (default off, {\it OS/2, PC\/ {\rm and} ST NetHack\/} only). +bulletproof input (MS-DOS sometimes treats `\texttt{\textasciicircum P}' as a printer toggle +without it) (default off, \textit{OS/2, PC {\rm and} ST NetHack} only). Note: DEC Rainbows hang if this is turned on. -Cannot be set with the `{\tt O}' command. +Cannot be set with the `\texttt{O}' command. %.lp \item[subkeyvalue] -({\it Win32 tty NetHack \/} only). +(\textit{Win32 tty NetHack } only). May be used to alter the value of keystrokes that the operating system -returns to {\it NetHack\/} to help compensate for international keyboard +returns to \textit{NetHack} to help compensate for international keyboard issues. OPTIONS=subkeyvalue:171/92 -will return 92 to {\it NetHack\/}, if 171 was originally going to be returned. +will return 92 to \textit{NetHack}, if 171 was originally going to be returned. You can use multiple subkeyvalue assignments in the configuration file if needed. -Cannot be set with the `{\tt O}' command. +Cannot be set with the `\texttt{O}' command. %.lp \item[video] -Set the video mode used ({\it PC\/ NetHack\/} only). -Values are {\it autodetect\/}, {\it default\/}, {\it vga\/}, or {\it vesa\/}. -Setting {\it vesa\/} will cause the game to display tiles, using the full +Set the video mode used (\textit{PC NetHack} only). +Values are \textit{autodetect}, \textit{default}, \textit{vga}, or \textit{vesa}. +Setting \textit{vesa} will cause the game to display tiles, using the full capability of the VGA hardware. -Setting {\it vga\/} will cause the game to display tiles, fixed at 640x480 +Setting \textit{vga} will cause the game to display tiles, fixed at 640x480 in 16 colors, a mode that is compatible with all VGA hardware. Third party tilesets will probably not work. -Setting {\it autodetect\/} attempts {\it vesa\/}, then {\it vga\/}, and -finally sets {\it default\/} if neither of those modes works. -Cannot be set with the `{\tt O}' command. +Setting \textit{autodetect} attempts \textit{vesa}, then \textit{vga}, and +finally sets \textit{default} if neither of those modes works. +Cannot be set with the `\texttt{O}' command. %.lp \item[video\textunderscore height] Set the VGA mode resolution height (MS-DOS only, with video:vesa) @@ -5706,19 +5705,19 @@ Set the VGA mode resolution width (MS-DOS only, with video:vesa) \item[videocolors] \begin{sloppypar} Set the color palette for PC systems using NO\textunderscore TERMS -(default 4-2-6-1-5-3-15-12-10-14-9-13-11, {\it PC\/ NetHack\/} only). +(default 4-2-6-1-5-3-15-12-10-14-9-13-11, \textit{PC NetHack} only). The order of colors is red, green, brown, blue, magenta, cyan, bright.white, bright.red, bright.green, yellow, bright.blue, bright.magenta, and bright.cyan. -Cannot be set with the `{\tt O}' command. +Cannot be set with the `\texttt{O}' command. \end{sloppypar} %.lp \item[videoshades] Set the intensity level of the three gray scales available -(default dark normal light, {\it PC\/ NetHack\/} only). +(default dark normal light, \textit{PC NetHack} only). If the game display is difficult to read, try adjusting these scales; -if this does not correct the problem, try {\tt !color}. -Cannot be set with the `{\tt O}' command. +if this does not correct the problem, try \texttt{!color}. +Cannot be set with the `\texttt{O}' command. \end{description} %.hn 2 @@ -5726,10 +5725,10 @@ Cannot be set with the `{\tt O}' command. %.pg Regular expressions are normally POSIX extended regular expressions. It is -possible to compile {\it NetHack\/} without regular expression support on +possible to compile \textit{NetHack} without regular expression support on a platform where there is no regular expression library. While this is not true of any modern -platform, if your {\it NetHack\/} was built this way, patterns are instead glob +platform, if your \textit{NetHack} was built this way, patterns are instead glob patterns; regardless, this document refers to both as ``regular expressions.'' This applies to Autopickup exceptions, Message types, Menu colors, and User sounds. @@ -5738,19 +5737,19 @@ and User sounds. \subsection*{Configuring Autopickup Exceptions} %.pg -You can further refine the behavior of the ``{\tt autopickup}'' option -beyond what is available through the ``{\tt pickup\textunderscore types}'' option. +You can further refine the behavior of the ``\texttt{autopickup}'' option +beyond what is available through the ``\texttt{pickup\textunderscore types}'' option. %.pg -By placing ``{\tt autopickup\textunderscore exception}'' lines in your configuration +By placing ``\texttt{autopickup\textunderscore exception}'' lines in your configuration file, you can define patterns to be checked when the game is about to autopickup something. \begin{description} %.lp \item[autopickup\textunderscore exception] -Sets an exception to the ``{\it pickup\textunderscore types}'' option. -The {\it autopickup\textunderscore exception\/} option should be followed by a regular +Sets an exception to the ``\textit{pickup\textunderscore types}'' option. +The \textit{autopickup\textunderscore exception} option should be followed by a regular expression to be used as a pattern to match against the singular form of the description of an object at your location. @@ -5759,20 +5758,20 @@ character in the pattern, specifically: %.sd %.si -{\tt <} --- always pickup an object that matches rest of pattern;\\ -{\tt >} --- never pickup an object that matches rest of pattern. +\texttt{<} --- always pickup an object that matches rest of pattern;\\ +\texttt{>} --- never pickup an object that matches rest of pattern. %.ei %.ed -The {\it autopickup\textunderscore exception\/} rules are processed in the order +The \textit{autopickup\textunderscore exception} rules are processed in the order in which they appear in your configuration file, thus allowing a later rule to override an earlier rule. %.lp "" -Exceptions can be set with the `{\tt O}' command, but because they are not +Exceptions can be set with the `\texttt{O}' command, but because they are not included in your configuration file, they won't be in effect if you save and then restore your game. -{\it autopickup\textunderscore exception\/} rules are not saved with the game. +\textit{autopickup\textunderscore exception} rules are not saved with the game. \end{description} %.lp "Here are some examples:" @@ -5798,8 +5797,8 @@ autopickup. It is possible to change the default key bindings of some special commands, menu accelerator keys, extended commands, by using BIND stanzas in the configuration file. Format is key, followed by the command to bind to, -separated by a colon. The key can be a single character (``{\tt x}''), -a control key (``{\tt \^{}X}'', ``{\tt C-x}''), a meta key (``{\tt M-x}''), +separated by a colon. The key can be a single character (``\texttt{x}''), +a control key (``\texttt{\textasciicircum X}'', ``\texttt{C-x}''), a meta key (``\texttt{M-x}''), a mouse button, or a three-digit decimal ASCII code. %.pg @@ -5811,12 +5810,12 @@ For example: BIND=v:loot \end{verbatim} -\begin{description}[font=\ttfamily] +\begin{description}[font=\mdseries\ttfamily] %.lp "Extended command keys" \item[Extended command keys] You can bind multiple keys to the same extended command. Unbind a key by -using ``{\tt nothing}'' as the extended command to bind to. You can also bind -the ``{\tt }'', ``{\tt }'', and ``{\tt }'' keys. +using ``\texttt{nothing}'' as the extended command to bind to. You can also bind +the ``\texttt{}'', ``\texttt{}'', and ``\texttt{}'' keys. %.lp "Menu accelerator keys" \item[Menu accelerator keys] @@ -5827,8 +5826,8 @@ Some interfaces only support some of the menu accelerators. %.lp "Mouse buttons" \item[Mouse buttons] -You can bind ``mouse1'' or ``mouse2'' to ``{\tt nothing}'', -``{\tt therecmdmenu}'', ``{\tt clicklook}'', or ``{\tt mouseaction}''. +You can bind ``mouse1'' or ``mouse2'' to ``\texttt{nothing}'', +``\texttt{therecmdmenu}'', ``\texttt{clicklook}'', or ``\texttt{mouseaction}''. %.lp "Special command keys" \item[Special command keys] @@ -5843,127 +5842,127 @@ can only be bound to a single key. %.lp \item[count] Prefix key to start a count, to repeat a command this many times. -With {\it number\textunderscore pad\/} only. Default is~`{\tt n}'. +With \textit{number\textunderscore pad} only. Default is~`\texttt{n}'. %.lp \item[getdir.help] -When asked for a direction, the key to show the help. Default is~`{\tt ?}'. +When asked for a direction, the key to show the help. Default is~`\texttt{?}'. %.lp \item[getdir.mouse] When asked for a direction, the key to initiate a simulated mouse click. You will be asked to pick a location. Use movement keystrokes to move the cursor around the map, then type -the getpos.pick.once key (default `{\tt ,}') -or the getpos.pick key (default `{\tt .}') +the getpos.pick.once key (default `\texttt{,}') +or the getpos.pick key (default `\texttt{.}') to finish as if performing a left or right click. -Only useful when using the {\tt \#therecmdmenu} command. -Default is~`{\tt \textunderscore }'. +Only useful when using the \texttt{\#therecmdmenu} command. +Default is~`\texttt{\textunderscore}'. %.lp \item[getdir.self] -When asked for a direction, the key to target yourself. Default is~`{\tt .}'. +When asked for a direction, the key to target yourself. Default is~`\texttt{.}'. %.lp \item[getdir.self2] When asked for a direction, an alternate key to target yourself. -Default is~`{\tt s}'. +Default is~`\texttt{s}'. %.lp \item[getpos.autodescribe] -When asked for a location, the key to toggle {\it autodescribe\/}. -Default is~`{\tt \#}'. +When asked for a location, the key to toggle \textit{autodescribe}. +Default is~`\texttt{\#}'. %.lp \item[getpos.all.next] When asked for a location, the key to go to next closest interesting thing. -Default is~`{\tt a}'. +Default is~`\texttt{a}'. %.lp \item[getpos.all.prev] When asked for a location, the key to go to previous closest interesting thing. -Default is~`{\tt A}'. +Default is~`\texttt{A}'. %.lp \item[getpos.door.next] When asked for a location, the key to go to next closest door or doorway. -Default is~`{\tt d}'. +Default is~`\texttt{d}'. %.lp \item[getpos.door.prev] When asked for a location, the key to go to previous closest door or doorway. -Default is~`{\tt D}'. +Default is~`\texttt{D}'. %.lp \item[getpos.help] -When asked for a location, the key to show help. Default is~`{\tt ?}'. +When asked for a location, the key to show help. Default is~`\texttt{?}'. %.lp \item[getpos.mon.next] When asked for a location, the key to go to next closest monster. -Default is~`{\tt m}'. +Default is~`\texttt{m}'. %.lp \item[getpos.mon.prev] When asked for a location, the key to go to previous closest monster. -Default is~`{\tt M}'. +Default is~`\texttt{M}'. %.lp \item[getpos.obj.next] When asked for a location, the key to go to next closest object. -Default is~`{\tt o}'. +Default is~`\texttt{o}'. %.lp \item[getpos.obj.prev] When asked for a location, the key to go to previous closest object. -Default is~`{\tt O}'. +Default is~`\texttt{O}'. %.lp \item[getpos.menu] When asked for a location, and using one of the next or previous keys to -cycle through targets, toggle showing a menu instead. Default is~`{\tt !}'. +cycle through targets, toggle showing a menu instead. Default is~`\texttt{!}'. %.lp \item[getpos.moveskip] When asked for a location, and using the shifted movement keys or meta-digit keys to fast-move around, move by skipping the same glyphs instead of by 8 units. -Default is~`{\tt *}'. +Default is~`\texttt{*}'. %.lp \item[getpos.filter] When asked for a location, change the filtering mode when using one of the next or previous keys to cycle through targets. Toggles between no -filtering, in view only, and in the same area only. Default is~`{\tt "}'. +filtering, in view only, and in the same area only. Default is~`\texttt{"}'. %.lp \item[getpos.pick] When asked for a location, the key to choose the location, and possibly ask for more info. When simulating a mouse click after being asked for a direction (see getdir.mouse above), the key to use to respond as right click. -Default is~`{\tt .}'. +Default is~`\texttt{.}'. %.lp \item[getpos.pick.once] When asked for a location, the key to choose the location, and skip asking for more info. When simulating a mouse click after being asked for a direction, the key to respond as left click. -Default is~`{\tt ,}'. +Default is~`\texttt{,}'. %.lp \item[getpos.pick.quick] When asked for a location, the key to choose the location, skip asking -for more info, and exit the location asking loop. Default is~`{\tt ;}'. +for more info, and exit the location asking loop. Default is~`\texttt{;}'. %.lp \item[getpos.pick.verbose] When asked for a location, the key to choose the location, and show more -info without asking. Default is~`{\tt :}'. +info without asking. Default is~`\texttt{:}'. %.lp \item[getpos.self] When asked for a location, the key to go to your location. -Default is~`{\tt @}'. +Default is~`\texttt{@}'. %.lp \item[getpos.unexplored.next] When asked for a location, the key to go to next closest unexplored location. -Default is~`{\tt x}'. +Default is~`\texttt{x}'. %.lp \item[getpos.unexplored.prev] When asked for a location, the key to go to previous closest unexplored -location. Default is~`{\tt X}'. +location. Default is~`\texttt{X}'. %.lp \item[getpos.valid] When asked for a location, the key to go to show valid target locations. -Default is~`{\tt \$}'. +Default is~`\texttt{\$}'. %.lp \item[getpos.valid.next] When asked for a location, the key to go to next closest valid location. -Default is~`{\tt z}'. +Default is~`\texttt{z}'. %.lp \item[getpos.valid.prev] When asked for a location, the key to go to previous closest valid location. -Default is~`{\tt Z}'. +Default is~`\texttt{Z}'. \end{description} @@ -5980,17 +5979,17 @@ look like this: \begin{verbatim} MSGTYPE=type "pattern" \end{verbatim} -\begin{description}[font=\itshape] +\begin{description}[font=\mdseries\itshape] %.lp \item[type] how the message should be shown: %.sd %.si \\ -{\tt show} --- show message normally.\\ -{\tt hide} --- never show the message.\\ -{\tt stop} --- wait for user with more-prompt.\\ -{\tt norep} --- show the message once, but not again if no other message is +\texttt{show} --- show message normally.\\ +\texttt{hide} --- never show the message.\\ +\texttt{stop} --- wait for user with more-prompt.\\ +\texttt{norep} --- show the message once, but not again if no other message is shown in between. %.ei %.ed @@ -6000,7 +5999,7 @@ the pattern to match. The pattern should be a regular expression. \end{description} %.lp "" -Here's an example of message types using {\it NetHack's\/} internal +Here's an example of message types using \textit{NetHack's} internal pattern matching facility: \begin{verbatim} @@ -6010,7 +6009,7 @@ pattern matching facility: specifies that whenever a message ``You feel hungry'' is shown, the user is prompted with more-prompt, and a message matching -``You displaced \verb+<+something\verb+>+'' is not shown at all. +``You displaced '' is not shown at all. %.lp The order of the defined MSGTYPE lines is important; the last matching @@ -6035,7 +6034,7 @@ look like this: MENUCOLOR="pattern"=color&attribute \end{verbatim} -\begin{description}[font=\itshape] +\begin{description}[font=\mdseries\itshape] %.lp \item[pattern] the pattern to match; @@ -6053,22 +6052,22 @@ If no attribute is defined, no attribute is used. The pattern should be a regular expression. %.lp "" -Allowed colors are {\it black}, {\it red}, {\it green}, {\it brown}, -{\it blue}, {\it magenta}, {\it cyan}, {\it gray}, {\it orange}, -{\it light-green}, {\it yellow}, {\it light-blue}, {\it light-magenta}, -{\it light-cyan}, and {\it white}. -And {\it no-color}, the default foreground color, which isn't necessarily +Allowed colors are \textit{black}, \textit{red}, \textit{green}, \textit{brown}, +\textit{blue}, \textit{magenta}, \textit{cyan}, \textit{gray}, \textit{orange}, +\textit{light-green}, \textit{yellow}, \textit{light-blue}, \textit{light-magenta}, +\textit{light-cyan}, and \textit{white}. +And \textit{no-color}, the default foreground color, which isn't necessarily the same as any of the other colors. %.lp "" -Allowed attributes are {\it none}, {\it bold}, {\it dim}, {\it italic}, -{\it underline},{\it blink}, and {\it inverse}. -{\it Normal\/} is a synonym for {\it none}. +Allowed attributes are \textit{none}, \textit{bold}, \textit{dim}, \textit{italic}, +\textit{underline},\textit{blink}, and \textit{inverse}. +\textit{Normal} is a synonym for \textit{none}. Note that the platform used may interpret the attributes any way it wants. %.lp "" -Here's an example of menu colors using {\it NetHack's\/} internal +Here's an example of menu colors using \textit{NetHack's} internal pattern matching facility: \begin{verbatim} @@ -6088,7 +6087,7 @@ a menu line will be used for the line. %.pg Note that if you intend to have one or more color specifications match ``~uncursed~'', you will probably want to turn the -{\it implicit\textunderscore uncursed\/} +\textit{implicit\textunderscore uncursed} option off so that all items known to be uncursed are actually displayed with the ``uncursed'' description. @@ -6106,7 +6105,7 @@ use of user sounds. The following configuration file entries are relevant to mapping user sounds to messages: -\begin{description}[font=\itshape] +\begin{description}[font=\mdseries\itshape] %.lp \item[SOUNDDIR] The directory that houses the sound files to be played. @@ -6117,12 +6116,12 @@ Each SOUND entry is broken down into the following parts: %.sd %.si -{\tt MESG } --- message window mapping (the only one supported in 3.7.0);\\ -{\tt msgtype } --- optional; message type to use, see ``Configuring User Sounds''\\ -{\tt pattern } --- the pattern to match;\\ -{\tt sound file } --- the sound file to play;\\ -{\tt volume } --- the volume to be set while playing the sound file;\\ -{\tt sound index} --- optional; the index corresponding to a sound file. +\texttt{MESG} --- message window mapping (the only one supported in 3.7.0);\\ +\texttt{msgtype} --- optional; message type to use, see ``Configuring User Sounds''\\ +\texttt{pattern} --- the pattern to match;\\ +\texttt{sound file} --- the sound file to play;\\ +\texttt{volume} --- the volume to be set while playing the sound file;\\ +\texttt{sound index} --- optional; the index corresponding to a sound file. %.ei %.ed \end{description} @@ -6144,8 +6143,8 @@ For example: \subsection*{Configuring Status Hilites} %.pg -Your copy of {\it NetHack\/} may have been compiled with support -for {\it Status Hilites}. +Your copy of \textit{NetHack} may have been compiled with support +for \textit{Status Hilites}. If so, you can customize your game display by setting thresholds to change the color or appearance of fields in the status display. @@ -6160,8 +6159,8 @@ drop to or below a threshold of 30%:\\ \begin{verbatim} OPTION=hilite_status:hitpoints/<=30%/red/normal \end{verbatim} -(That example is actually specifying {\tt red\&normal} for {\tt <=30\%} -and {\tt no-color\&normal} for {\tt >30\%}.)\\ +(That example is actually specifying \texttt{red\& normal} for \texttt{<=30\%} +and \texttt{no-color\& normal} for \texttt{>30\%}.)\\ For another example, the following line in your configuration file will cause wisdom to be displayed red if it drops and green if it rises:\\ @@ -6171,7 +6170,7 @@ OPTION=hilite_status:wisdom/down/red/up/green Allowed colors are black, red, green, brown, blue, magenta, cyan, gray, orange, light-green, yellow, light-blue, light-magenta, light-cyan, and white. -And {\it no-color}, the default foreground color on the display, which +And \textit{no-color}, the default foreground color on the display, which is not necessarily the same as black or white or any of the other colors. Allowed attributes are none, bold, dim, underline, italic, blink, and inverse. @@ -6182,7 +6181,7 @@ To specify both a color and an attribute, use `\&' to combine them. To specify multiple attributes, use `+' to combine those. %.lp "" -For example: {\tt magenta\&inverse+dim}. +For example: \texttt{magenta\& inverse+dim}. Note that the display may substitute or ignore particular attributes depending upon its capabilities, and in general may interpret the @@ -6192,7 +6191,7 @@ blink or vice versa. On others, issuing an attribute request while another is already set up will replace the earlier attribute rather than combine with it. Since nethack issues attribute requests sequentially (at least with -the {\it tty} interface) rather than all at once, the only way a +the \textit{tty} interface) rather than all at once, the only way a situation like that can be controlled is to specify just one attribute. You can adjust the display of the following status fields: @@ -6221,9 +6220,9 @@ depending upon your other option settings. %.lp "" Instead of a behavior, `condition' takes the following condition flags: -{\it stone}, {\it slime}, {\it strngl}, {\it foodpois}, {\it termill}, -{\it blind}, {\it deaf}, {\it stun}, {\it conf}, {\it hallu}, -{\it lev}, {\it fly}, and {\it ride}. +\textit{stone}, \textit{slime}, \textit{strngl}, \textit{foodpois}, \textit{termill}, +\textit{blind}, \textit{deaf}, \textit{stun}, \textit{conf}, \textit{hallu}, +\textit{lev}, \textit{fly}, and \textit{ride}. You can use `major\textunderscore troubles' as an alias for stone through termill, `minor\textunderscore troubles' for blind through hallu, `movement' for lev, fly, and ride, and `all' for every condition. @@ -6231,41 +6230,41 @@ for stone through termill, `minor\textunderscore troubles' for blind through hal %.lp "" Allowed behaviors are ``always'', ``up'', ``down'', ``changed'', a percentage or absolute number threshold, or text to match against. -For the {\it hitpoints\/} field, the additional behavior ``criticalhp'' +For the \textit{hitpoints} field, the additional behavior ``criticalhp'' is available. It overrides other behavior rules if -hit points are at or below the {\it major problem\/} threshold +hit points are at or below the \textit{major problem} threshold (which varies depending upon maximum hit points and experience level). -\begin{description} +\begin{description}[font=\mdseries\ttfamily] %.lp "*" -\item[{\tt always}] will set the default attributes for that field. +\item[always] will set the default attributes for that field. %.lp "*" -\item[{\tt up}{\normalfont, }{\tt down}] set the field attributes +\item[up \textrm{\textmd{,}} down] set the field attributes for when the field value changes upwards or downwards. This attribute -times out after {\tt statushilites} turns. +times out after \texttt{statushilites} turns. %.lp "*" -\item[{\tt changed}] sets the field attribute for when the field value -changes. This attribute times out after {\tt statushilites} turns. +\item[changed] sets the field attribute for when the field value +changes. This attribute times out after \texttt{statushilites} turns. (If a field has both a ``changed'' rule and an ``up'' or ``down'' rule which matches a change in the field's value, the ``up'' or ``down'' one takes precedence.) %.lp "*" -\item[{\tt percentage}] sets the field attribute when the field value +\item[percentage] sets the field attribute when the field value matches the percentage. -It is specified as a number between 0 and 100, followed by `{\tt \%}' +It is specified as a number between 0 and 100, followed by `\texttt{\%}' (percent sign). -If the percentage is prefixed with `{\tt <=}' or `{\tt >=}', +If the percentage is prefixed with `\texttt{<=}' or `\texttt{>=}', it also matches when value is below or above the percentage. -Use prefix `{\tt <}' or `{\tt >}' to match when strictly below or above. -(The numeric limit is relaxed slightly for those: {\tt >-1\%} -and {\tt <101\%} are allowed.) +Use prefix `\texttt{<}' or `\texttt{>}' to match when strictly below or above. +(The numeric limit is relaxed slightly for those: \texttt{>-1\%} +and \texttt{<101\%} are allowed.) Only four fields support percentage rules. -Percentages for ``{\it hitpoints\/}'' and ``{\it power\/}'' are +Percentages for ``\textit{hitpoints}'' and ``\textit{power}'' are straightforward; they're based on the corresponding maximum field. -Percentage highlight rules are also allowed for ``{\it experience level\/}'' -and ``{\it experience points\/}'' (valid when the -{\it showexp\/} +Percentage highlight rules are also allowed for ``\textit{experience level}'' +and ``\textit{experience points}'' (valid when the +\textit{showexp} option is enabled). For those, the percentage is based on the progress from the start of the current experience level to the start of the next level. @@ -6273,32 +6272,32 @@ So if level 2 starts at 20 points and level 3 starts at 40 points, having 30 points is 50\% and 35 points is 75\%. 100\% is unattainable for experience because you'll gain a level and the calculations will be reset for that new level, but a rule for -{\tt =100\%} is allowed and matches the special case of being +\texttt{=100\%} is allowed and matches the special case of being exactly 1 experience point short of the next level. % (If you manage to reach level 30, there is no next level and the % percentage will remain at 0\% no matter have many additional experience % points you earn.) %.lp "*" -\item[{\tt absolute}] value sets the attribute when the field value +\item[absolute] value sets the attribute when the field value matches that number. -The number must be 0 or higher, except for ``{\it armor-class\/} which -allows negative values, and may optionally be preceded by `{\tt =}'. -If the number is preceded by `{\tt <=}' or `{\tt >=}' instead, +The number must be 0 or higher, except for ``\textit{armor-class} which +allows negative values, and may optionally be preceded by `\texttt{=}'. +If the number is preceded by `\texttt{<=}' or `\texttt{>=}' instead, it also matches when value is below or above. -If the prefix is `{\tt <}' or `{\tt >}', only match when strictly +If the prefix is `\texttt{<}' or `\texttt{>}', only match when strictly above or below. %.lp "*" -\item[{\tt criticalhp}] only applies to the hitpoints field and only +\item[criticalhp] only applies to the hitpoints field and only when current hit points are below a threshold (which varies by maximum hit points and experience level). When the threshold is met, a criticalhp rule takes precedence over all other hitpoints rules. %.lp "*" -\item[{\tt text}] match sets the attribute when the field value +\item[text] match sets the attribute when the field value matches the text. -Text matches can only be used for ``{\it alignment\/}'', -``{\it carrying-capacity\/}'', ``{\it hunger\/}'', ``{\it dungeon-level\/}'', -and ``{\it title\/}''. +Text matches can only be used for ``\textit{alignment}'', +``\textit{carrying-capacity}'', ``\textit{hunger}'', ``\textit{dungeon-level}'', +and ``\textit{title}''. For title, only the role's rank title is tested; the character's name is ignored. %.ei @@ -6307,7 +6306,7 @@ is tested; the character's name is ignored. The in-game options menu can help you determine the correct syntax for a configuration file. -The whole feature can be disable by setting option {\it statushilites} to 0. +The whole feature can be disable by setting option \textit{statushilites} to 0. Example hilites: \begin{verbatim} @@ -6325,20 +6324,20 @@ Example hilites: %.lp %.hn 2 -\subsection*{Modifying {\it NetHack\/} Symbols} +\subsection*{Modifying \textit{NetHack} Symbols} %.pg -{\it NetHack\/} can load entire symbol sets from the symbol file. +\textit{NetHack} can load entire symbol sets from the symbol file. %.pg The options that are used to select a particular symbol set from the symbol file are: -\begin{description}[font=\itshape] +\begin{description}[font=\mdseries\itshape] %.lp \item[symset] Set the name of the symbol set that you want to load. -{\it symbols\/}. +\textit{symbols}. %.lp \item[roguesymset] @@ -6346,212 +6345,210 @@ Set the name of the symbol set that you want to load for display on the rogue level. \end{description} -You can also override one or more symbols using the {\it SYMBOLS\/} and -{\it ROGUESYMBOLS\/} configuration file options. -Symbols are specified as {\it name:value\/} pairs. -Note that {\it NetHack\/} escape-processes -the {\it value\/} string in conventional C fashion. -This means that `\verb+\+' is a prefix to take the following character +You can also override one or more symbols using the \textit{SYMBOLS} and +\textit{ROGUESYMBOLS} configuration file options. +Symbols are specified as \textit{name:value} pairs. +Note that \textit{NetHack} escape-processes +the \textit{value} string in conventional C fashion. +This means that `\texttt{\textbackslash}' is a prefix to take the following character literally. -Thus `\verb+\+' needs to be represented as `\verb+\\+'. +Thus `\texttt{\textbackslash}' needs to be represented as `\texttt{\textbackslash\textbackslash}'. The special prefix -`\verb+\m+' switches on the meta bit in the symbol value, and the -`{\tt \^{}}' prefix causes the following character to be treated as a control +`\texttt{\textbackslash m}' switches on the meta bit in the symbol value, and the +`\texttt{\textasciicircum}' prefix causes the following character to be treated as a control character. -{ \small \begin{longtable}{lll} \caption[]{NetHack Symbols}\\ Default & Symbol Name & Description\\ \hline \hline \endhead -\verb@ @ & S\textunderscore air & (air)\\ +\space @ & S\textunderscore air & (air)\\ \textunderscore & S\textunderscore altar & (altar)\\ -\verb@"@ & S\textunderscore amulet & (amulet)\\ -\verb@A@ & S\textunderscore angel & (angelic being)\\ -\verb@a@ & S\textunderscore ant & (ant or other insect)\\ -\verb@^@ & S\textunderscore anti\textunderscore magic\textunderscore trap & (anti-magic field)\\ -\verb@[@ & S\textunderscore armor & (suit or piece of armor)\\ -\verb@[@ & S\textunderscore armour & (suit or piece of armor)\\ -\verb@^@ & S\textunderscore arrow\textunderscore trap & (arrow trap)\\ -\verb@0@ & S\textunderscore ball & (iron ball)\\ +" & S\textunderscore amulet & (amulet)\\ +A & S\textunderscore angel & (angelic being)\\ +a & S\textunderscore ant & (ant or other insect)\\ +\textasciicircum & S\textunderscore anti\textunderscore magic\textunderscore trap & (anti-magic field)\\ +{[} & S\textunderscore armor & (suit or piece of armor)\\ +{[} & S\textunderscore armour & (suit or piece of armor)\\ +\textasciicircum & S\textunderscore arrow\textunderscore trap & (arrow trap)\\ +0 & S\textunderscore ball & (iron ball)\\ \# & S\textunderscore bars & (iron bars)\\ -\verb@B@ & S\textunderscore bat & (bat or bird)\\ -\verb@^@ & S\textunderscore bear\textunderscore trap & (bear trap)\\ -\verb@-@ & S\textunderscore blcorn & (bottom left corner)\\ -\verb@b@ & S\textunderscore blob & (blob)\\ -\verb@+@ & S\textunderscore book & (spellbook)\\ -\verb@)@ & S\textunderscore boomleft & (boomerang open left)\\ -\verb@(@ & S\textunderscore boomright & (boomerang open right)\\ -\verb@`@ & S\textunderscore boulder & (boulder)\\ -\verb@-@ & S\textunderscore brcorn & (bottom right corner)\\ -\verb@>@ & S\textunderscore brdnladder & (branch ladder down)\\ -\verb@>@ & S\textunderscore brdnstair & (branch staircase down)\\ -\verb@<@ & S\textunderscore brupladder & (branch ladder up)\\ -\verb@<@ & S\textunderscore brupstair & (branch staircase up)\\ -\verb@C@ & S\textunderscore centaur & (centaur)\\ -\verb@_@ & S\textunderscore chain & (iron chain)\\ +B & S\textunderscore bat & (bat or bird)\\ +\textasciicircum & S\textunderscore bear\textunderscore trap & (bear trap)\\ +- & S\textunderscore blcorn & (bottom left corner)\\ +b & S\textunderscore blob & (blob)\\ ++ & S\textunderscore book & (spellbook)\\ +) & S\textunderscore boomleft & (boomerang open left)\\ +( & S\textunderscore boomright & (boomerang open right)\\ +\textasciigrave & S\textunderscore boulder & (boulder)\\ +- & S\textunderscore brcorn & (bottom right corner)\\ +> & S\textunderscore brdnladder & (branch ladder down)\\ +> & S\textunderscore brdnstair & (branch staircase down)\\ +< & S\textunderscore brupladder & (branch ladder up)\\ +< & S\textunderscore brupstair & (branch staircase up)\\ +C & S\textunderscore centaur & (centaur)\\ +\textunderscore & S\textunderscore chain & (iron chain)\\ \# & S\textunderscore cloud & (cloud)\\ -\verb@c@ & S\textunderscore cockatrice & (cockatrice)\\ -\$ & S\textunderscore coin & (pile of coins)\\ +c & S\textunderscore cockatrice & (cockatrice)\\ +\textdollar & S\textunderscore coin & (pile of coins)\\ \# & S\textunderscore corr & (corridor)\\ -\verb@-@ & S\textunderscore crwall & (wall)\\ -\verb@-@ & S\textunderscore darkroom & (dark room)\\ -\verb@^@ & S\textunderscore dart\textunderscore trap & (dart trap)\\ -\verb@&@ & S\textunderscore demon & (major demon)\\ -\verb@*@ & S\textunderscore digbeam & (dig beam)\\ -\verb@>@ & S\textunderscore dnladder & (ladder down)\\ -\verb@>@ & S\textunderscore dnstair & (staircase down)\\ -\verb@d@ & S\textunderscore dog & (dog or other canine)\\ -\verb@D@ & S\textunderscore dragon & (dragon)\\ -\verb@;@ & S\textunderscore eel & (sea monster)\\ -\verb@E@ & S\textunderscore elemental & (elemental)\\ +- & S\textunderscore crwall & (wall)\\ +- & S\textunderscore darkroom & (dark room)\\ +\textasciicircum & S\textunderscore dart\textunderscore trap & (dart trap)\\ +\& & S\textunderscore demon & (major demon)\\ +* & S\textunderscore digbeam & (dig beam)\\ +> & S\textunderscore dnladder & (ladder down)\\ +> & S\textunderscore dnstair & (staircase down)\\ +d & S\textunderscore dog & (dog or other canine)\\ +D & S\textunderscore dragon & (dragon)\\ +; & S\textunderscore eel & (sea monster)\\ +E & S\textunderscore elemental & (elemental)\\ \# & S\textunderscore engrcorr & (engraving in a corridor)\\ -\verb@`@ & S\textunderscore engroom & (engraving in a room)\\ -\verb@/@ & S\textunderscore expl\textunderscore tl & (explosion top left)\\ -\verb@-@ & S\textunderscore expl\textunderscore tc & (explosion top center)\\ -\verb@\@ & S\textunderscore expl\textunderscore tr & (explosion top right)\\ -\verb@|@ & S\textunderscore expl\textunderscore ml & (explosion middle left)\\ -\verb@ @ & S\textunderscore expl\textunderscore mc & (explosion middle center)\\ -\verb@|@ & S\textunderscore expl\textunderscore mr & (explosion middle right)\\ -\verb@\@ & S\textunderscore expl\textunderscore bl & (explosion bottom left)\\ -\verb@-@ & S\textunderscore expl\textunderscore bc & (explosion bottom center)\\ -\verb@/@ & S\textunderscore expl\textunderscore br & (explosion bottom right)\\ -\verb@e@ & S\textunderscore eye & (eye or sphere)\\ -\verb@^@ & S\textunderscore falling\textunderscore rock\textunderscore trap & (falling rock trap)\\ -\verb@f@ & S\textunderscore feline & (cat or other feline)\\ -\verb@^@ & S\textunderscore fire\textunderscore trap & (fire trap)\\ -\verb@!@ & S\textunderscore flashbeam & (flash beam)\\ +\textasciigrave & S\textunderscore engroom & (engraving in a room)\\ +/ & S\textunderscore expl\textunderscore tl & (explosion top left)\\ +- & S\textunderscore expl\textunderscore tc & (explosion top center)\\ +\textbackslash & S\textunderscore expl\textunderscore tr & (explosion top right)\\ +| & S\textunderscore expl\textunderscore ml & (explosion middle left)\\ +~ & S\textunderscore expl\textunderscore mc & (explosion middle center)\\ +| & S\textunderscore expl\textunderscore mr & (explosion middle right)\\ +\textbackslash & S\textunderscore expl\textunderscore bl & (explosion bottom left)\\ +- & S\textunderscore expl\textunderscore bc & (explosion bottom center)\\ +/ & S\textunderscore expl\textunderscore br & (explosion bottom right)\\ +e & S\textunderscore eye & (eye or sphere)\\ +\textasciicircum & S\textunderscore falling\textunderscore rock\textunderscore trap & (falling rock trap)\\ +f & S\textunderscore feline & (cat or other feline)\\ +\textasciicircum & S\textunderscore fire\textunderscore trap & (fire trap)\\ +! & S\textunderscore flashbeam & (flash beam)\\ \% & S\textunderscore food & (piece of food)\\ -\{ & S\textunderscore fountain & (fountain)\\ -\verb@F@ & S\textunderscore fungus & (fungus or mold)\\ -\verb@*@ & S\textunderscore gem & (gem or rock)\\ -\verb@ @ & S\textunderscore ghost & (ghost)\\ -\verb@H@ & S\textunderscore giant & (giant humanoid)\\ -\verb@G@ & S\textunderscore gnome & (gnome)\\ -\verb@'@ & S\textunderscore golem & (golem)\\ -\verb@|@ & S\textunderscore grave & (grave)\\ -\verb@g@ & S\textunderscore gremlin & (gremlin)\\ -\verb@-@ & S\textunderscore hbeam & (wall)\\ +\textbraceleft & S\textunderscore fountain & (fountain)\\ +F & S\textunderscore fungus & (fungus or mold)\\ +* & S\textunderscore gem & (gem or rock)\\ +~ & S\textunderscore ghost & (ghost)\\ +H & S\textunderscore giant & (giant humanoid)\\ +G & S\textunderscore gnome & (gnome)\\ +\textquotesingle & S\textunderscore golem & (golem)\\ +| & S\textunderscore grave & (grave)\\ +g & S\textunderscore gremlin & (gremlin)\\ +- & S\textunderscore hbeam & (wall)\\ \# & S\textunderscore hcdbridge & (horizontal raised drawbridge)\\ -\verb@+@ & S\textunderscore hcdoor & (closed door)\\ -\verb@.@ & S\textunderscore hodbridge & (horizontal lowered drawbridge)\\ -\verb@|@ & S\textunderscore hodoor & (open door)\\ -\verb\^\ & S\textunderscore hole & (hole)\\ -\verb~@~ & S\textunderscore human & (human or elf)\\ -\verb@h@ & S\textunderscore humanoid & (humanoid)\\ -\verb@-@ & S\textunderscore hwall & (horizontal wall)\\ -\verb@.@ & S\textunderscore ice & (ice)\\ -\verb@i@ & S\textunderscore imp & (imp or minor demon)\\ -\verb@I@ & S\textunderscore invisible & (invisible monster)\\ -\verb@J@ & S\textunderscore jabberwock & (jabberwock)\\ -\verb@j@ & S\textunderscore jelly & (jelly)\\ -\verb@k@ & S\textunderscore kobold & (kobold)\\ -\verb@K@ & S\textunderscore kop & (Keystone Kop)\\ -\verb@^@ & S\textunderscore land\textunderscore mine & (land mine)\\ -\verb@}@ & S\textunderscore lava & (molten lava)\\ -\verb@}@ & S\textunderscore lavawall & (wall of lava)\\ -\verb@l@ & S\textunderscore leprechaun & (leprechaun)\\ -\verb@^@ & S\textunderscore level\textunderscore teleporter & (level teleporter)\\ -\verb@L@ & S\textunderscore lich & (lich)\\ -\verb@y@ & S\textunderscore light & (light)\\ ++ & S\textunderscore hcdoor & (closed door)\\ +. & S\textunderscore hodbridge & (horizontal lowered drawbridge)\\ +| & S\textunderscore hodoor & (open door)\\ +\textasciicircum & S\textunderscore hole & (hole)\\ +@ & S\textunderscore human & (human or elf)\\ +h & S\textunderscore humanoid & (humanoid)\\ +- & S\textunderscore hwall & (horizontal wall)\\ +. & S\textunderscore ice & (ice)\\ +i & S\textunderscore imp & (imp or minor demon)\\ +I & S\textunderscore invisible & (invisible monster)\\ +J & S\textunderscore jabberwock & (jabberwock)\\ +j & S\textunderscore jelly & (jelly)\\ +k & S\textunderscore kobold & (kobold)\\ +K & S\textunderscore kop & (Keystone Kop)\\ +\textasciicircum & S\textunderscore land\textunderscore mine & (land mine)\\ +\textbraceright & S\textunderscore lava & (molten lava)\\ +\textbraceright & S\textunderscore lavawall & (wall of lava)\\ +1 & S\textunderscore leprechaun & (leprechaun)\\ +\textasciicircum & S\textunderscore level\textunderscore teleporter & (level teleporter)\\ +L & S\textunderscore lich & (lich)\\ +y & S\textunderscore light & (light)\\ \# & S\textunderscore litcorr & (lit corridor)\\ -\verb@:@ & S\textunderscore lizard & (lizard)\\ -\verb@\@ & S\textunderscore lslant & (wall)\\ -\verb@^@ & S\textunderscore magic\textunderscore portal & (magic portal)\\ -\verb@^@ & S\textunderscore magic\textunderscore trap & (magic trap)\\ -\verb@m@ & S\textunderscore mimic & (mimic)\\ -\verb@]@ & S\textunderscore mimic\textunderscore def & (mimic)\\ -\verb@M@ & S\textunderscore mummy & (mummy)\\ -\verb@N@ & S\textunderscore naga & (naga)\\ -\verb@.@ & S\textunderscore ndoor & (doorway)\\ -\verb@n@ & S\textunderscore nymph & (nymph)\\ -\verb@O@ & S\textunderscore ogre & (ogre)\\ -\verb@o@ & S\textunderscore orc & (orc)\\ -\verb@p@ & S\textunderscore piercer & (piercer)\\ -\verb@^@ & S\textunderscore pit & (pit)\\ +: & S\textunderscore lizard & (lizard)\\ +\textbackslash & S\textunderscore lslant & (wall)\\ +\textasciicircum & S\textunderscore magic\textunderscore portal & (magic portal)\\ +\textasciicircum & S\textunderscore magic\textunderscore trap & (magic trap)\\ +m & S\textunderscore mimic & (mimic)\\ +{]} & S\textunderscore mimic\textunderscore def & (mimic)\\ +M & S\textunderscore mummy & (mummy)\\ +N & S\textunderscore naga & (naga)\\ +. & S\textunderscore ndoor & (doorway)\\ +n & S\textunderscore nymph & (nymph)\\ +O & S\textunderscore ogre & (ogre)\\ +o & S\textunderscore orc & (orc)\\ +p & S\textunderscore piercer & (piercer)\\ +\textasciicircum & S\textunderscore pit & (pit)\\ \# & S\textunderscore poisoncloud & (poison cloud)\\ -\verb@^@ & S\textunderscore polymorph\textunderscore trap & (polymorph trap)\\ -\verb@}@ & S\textunderscore pool & (water)\\ -\verb@!@ & S\textunderscore potion & (potion)\\ -\verb@P@ & S\textunderscore pudding & (pudding or ooze)\\ -\verb@q@ & S\textunderscore quadruped & (quadruped)\\ -\verb@Q@ & S\textunderscore quantmech & (quantum mechanic)\\ -\verb@=@ & S\textunderscore ring & (ring)\\ -\verb@`@ & S\textunderscore rock & (boulder or statue)\\ -\verb@r@ & S\textunderscore rodent & (rodent)\\ -\verb@^@ & S\textunderscore rolling\textunderscore boulder\textunderscore trap & (rolling boulder trap)\\ -\verb@.@ & S\textunderscore room & (floor of a room)\\ -\verb@/@ & S\textunderscore rslant & (wall)\\ -\verb@^@ & S\textunderscore rust\textunderscore trap & (rust trap)\\ -\verb@R@ & S\textunderscore rustmonst & (rust monster or disenchanter)\\ -\verb@?@ & S\textunderscore scroll & (scroll)\\ +\textasciicircum & S\textunderscore polymorph\textunderscore trap & (polymorph trap)\\ +\textbraceright & S\textunderscore pool & (water)\\ +! & S\textunderscore potion & (potion)\\ +P & S\textunderscore pudding & (pudding or ooze)\\ +q & S\textunderscore quadruped & (quadruped)\\ +Q & S\textunderscore quantmech & (quantum mechanic)\\ += & S\textunderscore ring & (ring)\\ +\textasciigrave & S\textunderscore rock & (boulder or statue)\\ +r & S\textunderscore rodent & (rodent)\\ +\textasciicircum & S\textunderscore rolling\textunderscore boulder\textunderscore trap & (rolling boulder trap)\\ +. & S\textunderscore room & (floor of a room)\\ +/ & S\textunderscore rslant & (wall)\\ +\textasciicircum & S\textunderscore rust\textunderscore trap & (rust trap)\\ +R & S\textunderscore rustmonst & (rust monster or disenchanter)\\ +? & S\textunderscore scroll & (scroll)\\ \# & S\textunderscore sink & (sink)\\ -\verb@^@ & S\textunderscore sleeping\textunderscore gas\textunderscore trap & (sleeping gas trap)\\ -\verb@S@ & S\textunderscore snake & (snake)\\ -\verb@s@ & S\textunderscore spider & (arachnid or centipede)\\ -\verb@^@ & S\textunderscore spiked\textunderscore pit & (spiked pit)\\ -\verb@^@ & S\textunderscore squeaky\textunderscore board & (squeaky board)\\ -\verb@0@ & S\textunderscore ss1 & (magic shield 1 of 4)\\ +\textasciicircum & S\textunderscore sleeping\textunderscore gas\textunderscore trap & (sleeping gas trap)\\ +S & S\textunderscore snake & (snake)\\ +s & S\textunderscore spider & (arachnid or centipede)\\ +\textasciicircum & S\textunderscore spiked\textunderscore pit & (spiked pit)\\ +\textasciicircum & S\textunderscore squeaky\textunderscore board & (squeaky board)\\ +0 & S\textunderscore ss1 & (magic shield 1 of 4)\\ \# & S\textunderscore ss2 & (magic shield 2 of 4)\\ -\verb+@+ & S\textunderscore ss3 & (magic shield 3 of 4)\\ -\verb@*@ & S\textunderscore ss4 & (magic shield 4 of 4)\\ -\verb@^@ & S\textunderscore statue\textunderscore trap & (statue trap)\\ -\verb@ @ & S\textunderscore stone & (solid rock)\\ -\verb@]@ & S\textunderscore strange\textunderscore obj & (strange object)\\ -\verb@-@ & S\textunderscore sw\textunderscore bc & (swallow bottom center)\\ -\verb@\@ & S\textunderscore sw\textunderscore bl & (swallow bottom left)\\ -\verb@/@ & S\textunderscore sw\textunderscore br & (swallow bottom right )\\ -\verb@|@ & S\textunderscore sw\textunderscore ml & (swallow middle left)\\ -\verb@|@ & S\textunderscore sw\textunderscore mr & (swallow middle right)\\ -\verb@-@ & S\textunderscore sw\textunderscore tc & (swallow top center)\\ -\verb@/@ & S\textunderscore sw\textunderscore tl & (swallow top left)\\ -\verb@\@ & S\textunderscore sw\textunderscore tr & (swallow top right)\\ -\verb@-@ & S\textunderscore tdwall & (wall)\\ -\verb@^@ & S\textunderscore teleportation\textunderscore trap & (teleportation trap)\\ -\verb@\@ & S\textunderscore throne & (opulent throne)\\ -\verb@-@ & S\textunderscore tlcorn & (top left corner)\\ -\verb@|@ & S\textunderscore tlwall & (wall)\\ -\verb@(@ & S\textunderscore tool & (useful item (pick-axe, key, lamp...))\\ -\verb@^@ & S\textunderscore trap\textunderscore door & (trap door)\\ -\verb@t@ & S\textunderscore trapper & (trapper or lurker above)\\ -\verb@-@ & S\textunderscore trcorn & (top right corner)\\ +@ & S\textunderscore ss3 & (magic shield 3 of 4)\\ +* & S\textunderscore ss4 & (magic shield 4 of 4)\\ +\textasciicircum & S\textunderscore statue\textunderscore trap & (statue trap)\\ +~ & S\textunderscore stone & (solid rock)\\ +{]} & S\textunderscore strange\textunderscore obj & (strange object)\\ +- & S\textunderscore sw\textunderscore bc & (swallow bottom center)\\ +\textbackslash & S\textunderscore sw\textunderscore bl & (swallow bottom left)\\ +/ & S\textunderscore sw\textunderscore br & (swallow bottom right )\\ +| & S\textunderscore sw\textunderscore ml & (swallow middle left)\\ +| & S\textunderscore sw\textunderscore mr & (swallow middle right)\\ +- & S\textunderscore sw\textunderscore tc & (swallow top center)\\ +/ & S\textunderscore sw\textunderscore tl & (swallow top left)\\ +\textbackslash & S\textunderscore sw\textunderscore tr & (swallow top right)\\ +- & S\textunderscore tdwall & (wall)\\ +\textasciicircum & S\textunderscore teleportation\textunderscore trap & (teleportation trap)\\ +\textbackslash & S\textunderscore throne & (opulent throne)\\ +- & S\textunderscore tlcorn & (top left corner)\\ +| & S\textunderscore tlwall & (wall)\\ +( & S\textunderscore tool & (useful item (pick-axe, key, lamp...))\\ +\textasciicircum & S\textunderscore trap\textunderscore door & (trap door)\\ +t & S\textunderscore trapper & (trapper or lurker above)\\ +- & S\textunderscore trcorn & (top right corner)\\ \# & S\textunderscore tree & (tree)\\ -\verb@T@ & S\textunderscore troll & (troll)\\ -\verb@|@ & S\textunderscore trwall & (wall)\\ -\verb@-@ & S\textunderscore tuwall & (wall)\\ -\verb@U@ & S\textunderscore umber & (umber hulk)\\ -\verb@ @ & S\textunderscore unexplored & (unexplored terrain)\\ -\verb@u@ & S\textunderscore unicorn & (unicorn or horse)\\ -\verb@<@ & S\textunderscore upladder & (ladder up)\\ -\verb@<@ & S\textunderscore upstair & (staircase up)\\ -\verb@V@ & S\textunderscore vampire & (vampire)\\ -\verb@|@ & S\textunderscore vbeam & (wall)\\ +T & S\textunderscore troll & (troll)\\ +| & S\textunderscore trwall & (wall)\\ +- & S\textunderscore tuwall & (wall)\\ +U & S\textunderscore umber & (umber hulk)\\ +~ & S\textunderscore unexplored & (unexplored terrain)\\ +u & S\textunderscore unicorn & (unicorn or horse)\\ +< & S\textunderscore upladder & (ladder up)\\ +< & S\textunderscore upstair & (staircase up)\\ +V & S\textunderscore vampire & (vampire)\\ +| & S\textunderscore vbeam & (wall)\\ \# & S\textunderscore vcdbridge & (vertical raised drawbridge)\\ -\verb@+@ & S\textunderscore vcdoor & (closed door)\\ -\verb@.@ & S\textunderscore venom & (splash of venom)\\ -\verb@^@ & S\textunderscore vibrating\textunderscore square & (vibrating square)\\ -\verb@.@ & S\textunderscore vodbridge & (vertical lowered drawbridge)\\ -\verb@-@ & S\textunderscore vodoor & (open door)\\ -\verb@v@ & S\textunderscore vortex & (vortex)\\ -\verb@|@ & S\textunderscore vwall & (vertical wall)\\ -\verb@/@ & S\textunderscore wand & (wand)\\ -\verb@}@ & S\textunderscore water & (water)\\ -\verb@)@ & S\textunderscore weapon & (weapon)\\ -\verb@"@ & S\textunderscore web & (web)\\ -\verb@w@ & S\textunderscore worm & (worm)\\ -\verb@~@ & S\textunderscore worm\textunderscore tail & (long worm tail)\\ -\verb@W@ & S\textunderscore wraith & (wraith)\\ -\verb@x@ & S\textunderscore xan & (xan or other extraordinary insect)\\ -\verb@X@ & S\textunderscore xorn & (xorn)\\ -\verb@Y@ & S\textunderscore yeti & (apelike creature)\\ -\verb@Z@ & S\textunderscore zombie & (zombie)\\ -\verb@z@ & S\textunderscore zruty & (zruty)\\ -\verb@ @ & S\textunderscore pet\textunderscore override & (any pet if ACCESSIBILITY=1 is set)\\ -\verb@ @ & S\textunderscore hero\textunderscore override & (hero if ACCESSIBILITY=1 is set) -\end{longtable}% -} ++ & S\textunderscore vcdoor & (closed door)\\ +. & S\textunderscore venom & (splash of venom)\\ +\textasciicircum & S\textunderscore vibrating\textunderscore square & (vibrating square)\\ +. & S\textunderscore vodbridge & (vertical lowered drawbridge)\\ +- & S\textunderscore vodoor & (open door)\\ +v & S\textunderscore vortex & (vortex)\\ +| & S\textunderscore vwall & (vertical wall)\\ +/ & S\textunderscore wand & (wand)\\ +\textbraceright & S\textunderscore water & (water)\\ +\textbraceright & S\textunderscore weapon & (weapon)\\ +" & S\textunderscore web & (web)\\ +w & S\textunderscore worm & (worm)\\ +\textasciitilde & S\textunderscore worm\textunderscore tail & (long worm tail)\\ +W & S\textunderscore wraith & (wraith)\\ +x & S\textunderscore xan & (xan or other extraordinary insect)\\ +X & S\textunderscore xorn & (xorn)\\ +Y & S\textunderscore yeti & (apelike creature)\\ +Z & S\textunderscore zombie & (zombie)\\ +z & S\textunderscore zruty & (zruty)\\ +~ & S\textunderscore pet\textunderscore override & (any pet if ACCESSIBILITY=1 is set)\\ +~ @ & S\textunderscore hero\textunderscore override & (hero if ACCESSIBILITY=1 is set) +\end{longtable} \hyphenation{sysconf} %no syllable breaks => don't hyphenate file name %.lp @@ -6612,11 +6609,11 @@ display of the customizations, such as the Enhanced symset. %.pg %.hn 2 -\subsection*{Configuring {\it NetHack\/} for Play by the Blind} +\subsection*{Configuring \textit{NetHack} for Play by the Blind} %.pg -{\it NetHack\/} can be set up to use only standard ASCII characters for making -maps of the dungeons. This makes even the MS-DOS versions of {\it NetHack} +\textit{NetHack} can be set up to use only standard ASCII characters for making +maps of the dungeons. This makes even the MS-DOS versions of \textit{NetHack} (which use special line-drawing characters by default) completely accessible to the blind who use speech and/or Braille access technologies. Players will require a good working knowledge of their screen-reader's @@ -6632,22 +6629,22 @@ gives you the row and column of your review cursor and the PC cursor. These co-ordinates are often useful in giving players a better sense of the overall location of items on the screen. %.pg -{\it NetHack\/} can also be compiled with support for sending the game +\textit{NetHack} can also be compiled with support for sending the game messages to an external program, such as a text-to-speech synthesizer. If -the ``{\tt \#version}'' extended command shows ``external program as a -message handler'', your {\it NetHack\/} -has been compiled with the capability. When compiling {\it NetHack\/} +the ``\texttt{\#version}'' extended command shows ``external program as a +message handler'', your \textit{NetHack} +has been compiled with the capability. When compiling \textit{NetHack} from source -on Linux and other POSIX systems, define {\tt MSGHANDLER\/} to enable it. +on Linux and other POSIX systems, define \texttt{MSGHANDLER} to enable it. To use -the capability, set the environment variable {\tt NETHACK\textunderscore MSGHANDLER\/} to +the capability, set the environment variable \texttt{NETHACK\textunderscore MSGHANDLER} to an executable, which will be executed with the game message as the program's only parameter. %.pg The most crucial settings to make the game more accessible are: %.pg -\begin{description}[font=\itshape] +\begin{description}[font=\mdseries\itshape] %.lp \item[symset:plain] Load a symbol set appropriate for use by blind players. @@ -6701,7 +6698,7 @@ of moving 8 units at a time. \item[nostatus\textunderscore updates] Prevent updates to the status lines at the bottom of the screen, if your screen-reader reads those lines. The same information can be -seen via the {\tt \#attributes} command. +seen via the \texttt{\#attributes} command. %.lp \item[showdamage] Give a message of damage taken and how many hit points are left. @@ -6711,18 +6708,18 @@ Give a message of damage taken and how many hit points are left. \subsection*{Global Configuration for System Administrators} %.pg -If {\it NetHack\/} is compiled with the SYSCF option, a system administrator +If \textit{NetHack} is compiled with the SYSCF option, a system administrator should set up a global configuration; this is a file in the same format as the traditional per-user configuration file (see above). This file should be named sysconf and placed in the same directory as -the other {\it NetHack\/} support files. +the other \textit{NetHack} support files. The options recognized in this file are listed below. Any option not set uses a compiled-in default (which may not be appropriate for your system). %.pg -\begin{description}[font=\itshape] +\begin{description}[font=\mdseries\itshape] %.lp \item[WIZARDS] A space-separated list of user name who are allowed to @@ -6731,7 +6728,7 @@ A value of a single asterisk (*) allows anyone to start a game in debug mode. %.lp \item[SHELLERS] -A list of users who are allowed to use the shell escape command (`{\tt !}'). +A list of users who are allowed to use the shell escape command (`\texttt{!}'). The syntax is the same as WIZARDS. %.lp \item[EXPLORERS] @@ -6768,7 +6765,7 @@ user who is restoring is the same one who saved). %.pg The following four options affect the score file: -\begin{description}[font=\itshape] +\begin{description}[font=\mdseries\itshape] %.pg %.lp \item[PERSMAX] @@ -6808,20 +6805,20 @@ Not defining this will prevent dumplog from being created. Only available if your game is compiled with DUMPLOG. Allows the following placeholders: % FIXME: this should be changed to a nested list or else be forcibly indented -{\tt \%\%} --- literal `{\tt \%}'\\ -{\tt \%v} --- version (eg. ``{\tt 3.7.0-0}'')\\ -{\tt \%u} --- game UID\\ -{\tt \%t} --- game start time, UNIX timestamp format\\ -{\tt \%T} --- current time, UNIX timestamp format\\ -{\tt \%d} --- game start time, YYYYMMDDhhmmss format\\ -{\tt \%D} --- current time, YYYYMMDDhhmmss format\\ -{\tt \%n} --- player name\\ -{\tt \%N} --- first character of player name +\texttt{\%\%} --- literal `\texttt{\%}'\\ +\texttt{\%v} --- version (eg. ``\texttt{3.7.0-0}'')\\ +\texttt{\%u} --- game UID\\ +\texttt{\%t} --- game start time, UNIX timestamp format\\ +\texttt{\%T} --- current time, UNIX timestamp format\\ +\texttt{\%d} --- game start time, YYYYMMDDhhmmss format\\ +\texttt{\%D} --- current time, YYYYMMDDhhmmss format\\ +\texttt{\%n} --- player name\\ +\texttt{\%N} --- first character of player name %.lp \item[LIVELOG] A bit-mask of types of events that should be written to -the {\it livelog\/} file if one is present. -The sample {\it sysconf\/} file accompanying the program contains a +the \textit{livelog} file if one is present. +The sample \textit{sysconf} file accompanying the program contains a comment which lists the meaning of the various bits used. Intended for server systems supporting simultaneous play by multiple players (to be clear, each one running a separate single player game), @@ -6833,7 +6830,7 @@ large unless it is actively maintained. %.lp \item[CRASHREPORTURL] If set to -{\tt https://www.nethack.org/links/cr-37BETA.html} +\url{https://www.nethack.org/links/cr-37BETA.html} and support is compiled in, brings up a browser window populated with the information needed to report a problem if the game panics or ends up in an internally inconsistent state, or if the \#bugreport command is @@ -6844,13 +6841,13 @@ invoked. \section{Scoring} %.pg -{\it NetHack\/} maintains a list of the top scores or scorers on your machine, +\textit{NetHack} maintains a list of the top scores or scorers on your machine, depending on how it is set up. In the latter case, each account on the machine can post only one non-winning score on this list. If you score higher than someone else on this list, or better your previous score, you will be inserted in the proper place under your current name. How many scores are kept can also be set up when -{\it NetHack\/} is compiled. +\textit{NetHack} is compiled. %.pg Your score is chiefly based upon how much experience you gained, how @@ -6875,7 +6872,7 @@ on most versions. \section{Explore mode} %.pg -{\it NetHack\/} is an intricate and difficult game. Novices might falter +\textit{NetHack} is an intricate and difficult game. Novices might falter in fear, aware of their ignorance of the means to survive. Well, fear not. Your dungeon comes equipped with an ``explore'' or ``discovery'' mode that enables you to keep old save files and cheat death, at the @@ -6883,10 +6880,10 @@ paltry cost of not getting on the high score list. %.pg There are two ways of enabling explore mode. One is to start the game -with the {\tt -X} +with the \texttt{-X} command-line switch or with the -{\it playmode:explore\/} -option. The other is to issue the `{\tt \#exploremode}' extended command while +\textit{playmode:explore} +option. The other is to issue the `\texttt{\#exploremode}' extended command while already playing the game. Starting a new game in explore mode provides your character with a wand of wishing in initial inventory; switching during play does not. The other benefits of explore mode are left for @@ -6905,16 +6902,16 @@ program rather than to provide god-like powers to your character, and players who attempt debugging are expected to figure out how to use it themselves. It is initiated by starting the game with the -{\tt -D} +\texttt{-D} command-line switch or with the -{\it playmode:debug\/} +\textit{playmode:debug} option. %.pg For some systems, the player must be logged in under a particular user name to be allowed to use debug mode; for others, the hero must be given a particular character name (but may be any role; -there's no connection between ``wizard mode'' and the {\it Wizard\/} role). +there's no connection between ``wizard mode'' and the \textit{Wizard} role). Attempting to start a game in debug mode when not allowed or not available will result in falling back to explore mode instead. @@ -6922,85 +6919,85 @@ or not available will result in falling back to explore mode instead. \section{Credits} %.pg The original % -{\it hack\/} game was modeled on the Berkeley +\textit{hack} game was modeled on the Berkeley %.ux UNIX -{\it rogue\/} game. Large portions of this document were shamelessly +\textit{rogue} game. Large portions of this document were shamelessly cribbed from % -{\it A Guide to the Dungeons of Doom}, by Michael C. Toy +\textit{A Guide to the Dungeons of Doom}, by Michael C. Toy and Kenneth C. R. C. Arnold. Small portions were adapted from -{\it Further Exploration of the Dungeons of Doom}, by Ken Arromdee. +\textit{Further Exploration of the Dungeons of Doom}, by Ken Arromdee. %.pg -{\it NetHack\/} is the product of literally scores of people's work. +\textit{NetHack} is the product of literally scores of people's work. Main events in the course of the game development are described below: %.pg \bigskip -\noindent {\it Jay Fenlason\/} wrote the original {\it Hack}, with help from {\it -Kenny Woodland}, {\it Mike Thome}, and {\it Jon Payne}. +\noindent \textit{Jay Fenlason} wrote the original \textit{Hack}, with help from {\it +Kenny Woodland}, \textit{Mike Thome}, and \textit{Jon Payne}. %.pg \medskip -\noindent {\it Andries Brouwer\/} did a major re-write while at +\noindent \textit{Andries Brouwer} did a major re-write while at Stichting Mathematisch Centrum (now Centrum Wiskunde \& Informatica), transforming Hack into a very different game. He published the Hack source code for use on UNIX systems by posting that to Usenet -newsgroup {\it net.sources\/} (later renamed {\it comp.sources}) +newsgroup \textit{net.sources} (later renamed \textit{comp.sources}) releasing version 1.0 in December of 1984, then versions 1.0.1, 1.0.2, and finally 1.0.3 in July of 1985. -Usenet newsgroup {\it net.games.hack\/} (later -renamed {\it rec.games.hack}, eventually replaced -by {\it rec.games.roguelike.nethack}) +Usenet newsgroup \textit{net.games.hack} (later +renamed \textit{rec.games.hack}, eventually replaced +by \textit{rec.games.roguelike.nethack}) was created for discussing it. %.pg \medskip -\noindent {\it Don G. Kneller\/} ported {\it Hack\/} 1.0.3 to Microsoft C and MS-DOS, -producing {\it PC Hack\/} 1.01e, added support for DEC Rainbow graphics in +\noindent \textit{Don G. Kneller} ported \textit{Hack} 1.0.3 to Microsoft C and MS-DOS, +producing \textit{PC Hack} 1.01e, added support for DEC Rainbow graphics in version 1.03g, and went on to produce at least four more versions (3.0, 3.2, 3.51, and 3.6; -note that these are old {\it Hack\/} version numbers, not contemporary -{\it NetHack\/} ones). +note that these are old \textit{Hack} version numbers, not contemporary +\textit{NetHack} ones). %.pg \medskip -\noindent {\it R. Black\/} ported {\it PC Hack\/} 3.51 to Lattice C and the Atari -520/1040ST, producing {\it ST Hack\/} 1.03. +\noindent \textit{R. Black} ported \textit{PC Hack} 3.51 to Lattice C and the Atari +520/1040ST, producing \textit{ST Hack} 1.03. %.pg \medskip -\noindent {\it Mike Stephenson\/} merged these various versions back together, -incorporating many of the added features, and produced {\it NetHack\/} version +\noindent \textit{Mike Stephenson} merged these various versions back together, +incorporating many of the added features, and produced \textit{NetHack} version 1.4 in 1987. He then coordinated a cast of thousands in enhancing and debugging -{\it NetHack\/} 1.4 and released {\it NetHack\/} versions 2.2 and 2.3. +\textit{NetHack} 1.4 and released \textit{NetHack} versions 2.2 and 2.3. Like Hack, they were released by posting their source code to Usenet where they remained available in various archives accessible -via {\it ftp\/} and {\it uucp\/} after expiring from the newsgroup. +via \textit{ftp} and \textit{uucp} after expiring from the newsgroup. %.pg \medskip \noindent Later, Mike coordinated a major re-write of the game, heading a team which -included {\it Ken Arromdee}, {\it Jean-Christophe Collet}, {\it Steve Creps}, -{\it Eric Hendrickson}, {\it Izchak Miller}, {\it Eric S. Raymond}, {\it John -Rupley}, {\it Mike Threepoint}, and {\it Janet Walz}, to produce -{\it NetHack\/} 3.0c. +included \textit{Ken Arromdee}, \textit{Jean-Christophe Collet}, \textit{Steve Creps}, +\textit{Eric Hendrickson}, \textit{Izchak Miller}, \textit{Eric S. Raymond}, \textit{John +Rupley}, \textit{Mike Threepoint}, and \textit{Janet Walz}, to produce +\textit{NetHack} 3.0c. %.pg \medskip -\noindent {\it NetHack\/} 3.0 was ported to the Atari by {\it Eric R. Smith}, to OS/2 by -{\it Timo Hakulinen}, and to VMS by {\it David Gentzel}. The three of them -and {\it Kevin Darcy\/} later joined the main {\it NetHack Development Team} to produce +\noindent \textit{NetHack} 3.0 was ported to the Atari by \textit{Eric R. Smith}, to OS/2 by +\textit{Timo Hakulinen}, and to VMS by \textit{David Gentzel}. The three of them +and \textit{Kevin Darcy} later joined the main \textit{NetHack Development Team} to produce subsequent revisions of 3.0. %.pg \medskip -\noindent {\it Olaf Seibert\/} ported {\it NetHack\/} 2.3 and 3.0 to the Amiga. {\it -Norm Meluch}, {\it Stephen Spackman\/} and {\it Pierre Martineau\/} designed -overlay code for {\it PC NetHack\/} 3.0. {\it Johnny Lee\/} ported {\it -NetHack\/} 3.0 to the Macintosh. Along with various other Dungeoneers, they +\noindent \textit{Olaf Seibert} ported \textit{NetHack} 2.3 and 3.0 to the Amiga. {\it +Norm Meluch}, \textit{Stephen Spackman} and \textit{Pierre Martineau} designed +overlay code for \textit{PC NetHack} 3.0. \textit{Johnny Lee} ported {\it +NetHack} 3.0 to the Macintosh. Along with various other Dungeoneers, they continued to enhance the PC, Macintosh, and Amiga ports through the later revisions of 3.0. @@ -7016,92 +7013,92 @@ the three component numbering scheme began to be used with 3.1.0. %.pg \medskip -\noindent Headed by {\it Mike Stephenson\/} and coordinated by {\it Izchak Miller\/} -and {\it Janet Walz}, the {\it NetHack Development Team} which now included -{\it Ken Arromdee}, -{\it David Cohrs}, {\it Jean-Christophe Collet}, {\it Kevin Darcy}, -{\it Matt Day}, {\it Timo Hakulinen}, {\it Steve Linhart}, {\it Dean Luick}, -{\it Pat Rankin}, {\it Eric Raymond}, and {\it Eric Smith\/} undertook a +\noindent Headed by \textit{Mike Stephenson} and coordinated by \textit{Izchak Miller} +and \textit{Janet Walz}, the \textit{NetHack Development Team} which now included +\textit{Ken Arromdee}, +\textit{David Cohrs}, \textit{Jean-Christophe Collet}, \textit{Kevin Darcy}, +\textit{Matt Day}, \textit{Timo Hakulinen}, \textit{Steve Linhart}, \textit{Dean Luick}, +\textit{Pat Rankin}, \textit{Eric Raymond}, and \textit{Eric Smith} undertook a radical revision of 3.0. They re-structured the game's design, and re-wrote major parts of the code. They added multiple dungeons, a new display, special individual character quests, a new endgame and many other new features, and -produced {\it NetHack\/} 3.1. +produced \textit{NetHack} 3.1. Version 3.1.0 was released in January of 1993. %.pg \medskip -\noindent {\it Ken Lorber}, {\it Gregg Wonderly\/} and {\it Greg Olson}, with help -from {\it Richard Addison}, {\it Mike Passaretti}, and {\it Olaf Seibert}, -developed {\it NetHack\/} 3.1 for the Amiga. +\noindent \textit{Ken Lorber}, \textit{Gregg Wonderly} and \textit{Greg Olson}, with help +from \textit{Richard Addison}, \textit{Mike Passaretti}, and \textit{Olaf Seibert}, +developed \textit{NetHack} 3.1 for the Amiga. %.pg \medskip -\noindent {\it Norm Meluch\/} and {\it Kevin Smolkowski}, with help from -{\it Carl Schelin}, {\it Stephen Spackman}, {\it Steve VanDevender}, -and {\it Paul Winner}, ported {\it NetHack\/} 3.1 to the PC. +\noindent \textit{Norm Meluch} and \textit{Kevin Smolkowski}, with help from +\textit{Carl Schelin}, \textit{Stephen Spackman}, \textit{Steve VanDevender}, +and \textit{Paul Winner}, ported \textit{NetHack} 3.1 to the PC. %.pg \medskip -\noindent {\it Jon W\{tte} and {\it Hao-yang Wang}, -with help from {\it Ross Brown}, {\it Mike Engber}, {\it David Hairston}, -{\it Michael Hamel}, {\it Jonathan Handler}, {\it Johnny Lee}, -{\it Tim Lennan}, {\it Rob Menke}, and {\it Andy Swanson}, -developed {\it NetHack\/} 3.1 for the Macintosh, porting it for MPW. -Building on their development, {\it Bart House} added a Think C port. +\noindent \textit{Jon W\textbraceleft tte} and \textit{Hao-yang Wang}, +with help from \textit{Ross Brown}, \textit{Mike Engber}, \textit{David Hairston}, +\textit{Michael Hamel}, \textit{Jonathan Handler}, \textit{Johnny Lee}, +\textit{Tim Lennan}, \textit{Rob Menke}, and \textit{Andy Swanson}, +developed \textit{NetHack} 3.1 for the Macintosh, porting it for MPW. +Building on their development, \textit{Bart House} added a Think C port. %.pg \medskip -\noindent {\it Timo Hakulinen\/} ported {\it NetHack\/} 3.1 to OS/2. -{\it Eric Smith\/} ported {\it NetHack\/} 3.1 to the Atari. -{\it Pat Rankin}, with help from {\it Joshua Delahunty}, -was responsible for the VMS version of {\it NetHack\/} 3.1. -{\it Michael Allison} ported {\it NetHack\/} 3.1 to Windows NT. +\noindent \textit{Timo Hakulinen} ported \textit{NetHack} 3.1 to OS/2. +\textit{Eric Smith} ported \textit{NetHack} 3.1 to the Atari. +\textit{Pat Rankin}, with help from \textit{Joshua Delahunty}, +was responsible for the VMS version of \textit{NetHack} 3.1. +\textit{Michael Allison} ported \textit{NetHack} 3.1 to Windows NT. %.pg \medskip -\noindent {\it Dean Luick}, with help from {\it David Cohrs}, developed -{\it NetHack\/} 3.1 for X11. +\noindent \textit{Dean Luick}, with help from \textit{David Cohrs}, developed +\textit{NetHack} 3.1 for X11. It drew the map as text rather than graphically but -included {\tt nh10.bdf}, an optionally used custom X11 font which has +included \texttt{nh10.bdf}, an optionally used custom X11 font which has tiny images in place of letters and punctuation, a precursor of tiles. Those images don't extend to individual monster and object types, just replacements for monster and object classes (so one custom image for all -``{\tt a}'' insects and another for all ``{\tt [}'' armor and so +``\texttt{a}'' insects and another for all ``\texttt{[}'' armor and so forth, not separate images for beetles and ants or for cloaks and boots). %.pg \medskip -\noindent {\it Warwick Allison\/} wrote a graphically displayed version -of {\it NetHack\/} +\noindent \textit{Warwick Allison} wrote a graphically displayed version +of \textit{NetHack} for the Atari where the tiny pictures were described as ``icons'' and were distinct for specific types of monsters and objects rather than just their classes. -He contributed them to the {\it NetHack Development Team\/} which +He contributed them to the \textit{NetHack Development Team} which rechristened them ``tiles'', original usage which has subsequently been picked up by various other games. -{\it NetHack's\/} tiles support was then implemented on other platforms +\textit{NetHack's} tiles support was then implemented on other platforms (initially MS-DOS but eventually Windows, Qt, and X11 too). %.pg \medskip -\noindent The 3.2 {\it NetHack Development Team}, comprised of {\it Michael Allison}, {\it Ken -Arromdee}, {\it David Cohrs}, {\it Jessie Collet}, {\it Steve Creps}, {\it -Kevin Darcy}, {\it Timo Hakulinen}, {\it Steve Linhart}, {\it Dean Luick}, -{\it Pat Rankin}, {\it Eric Smith}, {\it Mike Stephenson}, {\it Janet Walz}, -and {\it Paul Winner}, released version 3.2.0 in April of 1996. +\noindent The 3.2 \textit{NetHack Development Team}, comprised of \textit{Michael Allison}, \textit{Ken +Arromdee}, \textit{David Cohrs}, \textit{Jessie Collet}, \textit{Steve Creps}, {\it +Kevin Darcy}, \textit{Timo Hakulinen}, \textit{Steve Linhart}, \textit{Dean Luick}, +\textit{Pat Rankin}, \textit{Eric Smith}, \textit{Mike Stephenson}, \textit{Janet Walz}, +and \textit{Paul Winner}, released version 3.2.0 in April of 1996. %.pg \medskip \noindent Version 3.2 marked the tenth anniversary of the formation of the development team. In a testament to their dedication to the game, all thirteen members -of the original {\it NetHack Development Team} remained on the team at the +of the original \textit{NetHack Development Team} remained on the team at the start of work on that release. During the interval between the release of 3.1.3 and 3.2.0, -one of the founding members of the {\it NetHack Development Team}, -{\it Dr. Izchak Miller}, was diagnosed with cancer and passed away. +one of the founding members of the \textit{NetHack Development Team}, +\textit{Dr. Izchak Miller}, was diagnosed with cancer and passed away. That release of the game was dedicated to him by the development and porting teams. @@ -7112,31 +7109,31 @@ better game play. %.pg \medskip -During the lifespan of {\it NetHack\/} 3.1 and 3.2, several enthusiasts +During the lifespan of \textit{NetHack} 3.1 and 3.2, several enthusiasts of the game added their own modifications to the game and made these ``variants'' publicly available: %.pg \medskip -{\it Tom Proudfoot} and {\it Yuval Oren} created {\it NetHack++}, -which was quickly renamed {\it NetHack$--$\/} +\textit{Tom Proudfoot} and \textit{Yuval Oren} created \textit{NetHack++}, +which was quickly renamed \textit{NetHack$--$} when some people incorrectly assumed that it was a conversion of the -{\it C\/} source code to {\it C++}. -Working independently, {\it Stephen White} wrote {\it NetHack Plus}. -{\it Tom Proudfoot} later merged {\it NetHack Plus} -and his own {\it NetHack$--$} to produce {\it SLASH}. -{\it Larry Stewart-Zerba} and {\it Warwick Allison} improved the spell +\textit{C} source code to \textit{C++}. +Working independently, \textit{Stephen White} wrote \textit{NetHack Plus}. +\textit{Tom Proudfoot} later merged \textit{NetHack Plus} +and his own \textit{NetHack$--$} to produce \textit{SLASH}. +\textit{Larry Stewart-Zerba} and \textit{Warwick Allison} improved the spell casting system with the Wizard Patch. -{\it Warwick Allison} also ported {\it NetHack\/} to use the Qt interface. +\textit{Warwick Allison} also ported \textit{NetHack} to use the Qt interface. %.pg \medskip -{\it Warren Cheung} combined {\it SLASH} with the Wizard Patch -to produce {\it Slash'EM\/}, and -with the help of {\it Kevin Hugo}, added more features. -Kevin later joined the {\it NetHack Development Team} and incorporated -the best of these ideas into {\it NetHack\/} 3.3. +\textit{Warren Cheung} combined \textit{SLASH} with the Wizard Patch +to produce \textit{Slash'EM}, and +with the help of \textit{Kevin Hugo}, added more features. +Kevin later joined the \textit{NetHack Development Team} and incorporated +the best of these ideas into \textit{NetHack} 3.3. %.pg \medskip @@ -7147,7 +7144,7 @@ without any ready-to-play distribution for systems that usually had such. %.pg (To anyone considering resurrecting an old version: all versions before -3.2.3 had a {\it Y2K\/} bug. +3.2.3 had a \textit{Y2K} bug. The high scores file and the log file contained dates which were formatted using a two-digit year, and 1999's year 99 was followed by 2000's year 100. @@ -7159,11 +7156,11 @@ random ghost and statue names in the current game.) %.pg \medskip -The 3.3 {\it NetHack Development Team}, consisting of {\it Michael Allison}, {\it Ken Arromdee}, -{\it David Cohrs}, {\it Jessie Collet}, {\it Steve Creps}, {\it Kevin Darcy}, -{\it Timo Hakulinen}, {\it Kevin Hugo}, {\it Steve Linhart}, {\it Ken Lorber}, -{\it Dean Luick}, {\it Pat Rankin}, {\it Eric Smith}, {\it Mike Stephenson}, -{\it Janet Walz}, and {\it Paul Winner}, released 3.3.0 in +The 3.3 \textit{NetHack Development Team}, consisting of \textit{Michael Allison}, \textit{Ken Arromdee}, +\textit{David Cohrs}, \textit{Jessie Collet}, \textit{Steve Creps}, \textit{Kevin Darcy}, +\textit{Timo Hakulinen}, \textit{Kevin Hugo}, \textit{Steve Linhart}, \textit{Ken Lorber}, +\textit{Dean Luick}, \textit{Pat Rankin}, \textit{Eric Smith}, \textit{Mike Stephenson}, +\textit{Janet Walz}, and \textit{Paul Winner}, released 3.3.0 in December 1999 and 3.3.1 in August of 2000. %.pg @@ -7181,73 +7178,73 @@ more than a year and a half. %.pg \medskip -The 3.4 {\it NetHack Development Team} initially consisted of -{\it Michael Allison}, {\it Ken Arromdee}, -{\it David Cohrs}, {\it Jessie Collet}, {\it Kevin Hugo}, {\it Ken Lorber}, -{\it Dean Luick}, {\it Pat Rankin}, {\it Mike Stephenson}, -{\it Janet Walz}, and {\it Paul Winner}, with {\it Warwick Allison} joining -just before the release of {\it NetHack\/} 3.4.0 in March 2002. +The 3.4 \textit{NetHack Development Team} initially consisted of +\textit{Michael Allison}, \textit{Ken Arromdee}, +\textit{David Cohrs}, \textit{Jessie Collet}, \textit{Kevin Hugo}, \textit{Ken Lorber}, +\textit{Dean Luick}, \textit{Pat Rankin}, \textit{Mike Stephenson}, +\textit{Janet Walz}, and \textit{Paul Winner}, with \textit{ Warwick Allison} joining +just before the release of \textit{NetHack} 3.4.0 in March 2002. %.pg \medskip As with version 3.3, various people contributed to the game as a whole as -well as supporting ports on the different platforms that {\it NetHack\/} +well as supporting ports on the different platforms that \textit{NetHack} runs on: %.pg \medskip -\noindent{\it Pat Rankin} maintained 3.4 for VMS. +\noindent\textit{Pat Rankin} maintained 3.4 for VMS. %.pg \medskip -\noindent {\it Michael Allison} maintained {\it NetHack\/} 3.4 for the MS-DOS +\noindent \textit{Michael Allison} maintained \textit{NetHack} 3.4 for the MS-DOS platform. -{\it Paul Winner} and {\it Yitzhak Sapir} provided encouragement. +\textit{Paul Winner} and \textit{Yitzhak Sapir} provided encouragement. %.pg \medskip -\noindent {\it Dean Luick}, {\it Mark Modrall}, and {\it Kevin Hugo} maintained and +\noindent \textit{Dean Luick}, \textit{Mark Modrall}, and \textit{Kevin Hugo} maintained and enhanced the Macintosh port of 3.4. %.pg \medskip -\noindent {\it Michael Allison}, {\it David Cohrs}, {\it Alex Kompel}, -{\it Dion Nicolaas}, and -{\it Yitzhak Sapir} maintained and enhanced 3.4 for the Microsoft Windows +\noindent \textit{Michael Allison}, \textit{David Cohrs}, \textit{Alex Kompel}, +\textit{Dion Nicolaas}, and +\textit{Yitzhak Sapir} maintained and enhanced 3.4 for the Microsoft Windows platform. -{\it Alex Kompel} contributed a new graphical interface for the Windows port. -{\it Alex Kompel} also contributed a Windows CE port for 3.4.1. +\textit{Alex Kompel} contributed a new graphical interface for the Windows port. +\textit{Alex Kompel} also contributed a Windows CE port for 3.4.1. %.pg \medskip -\noindent {\it Ron Van Iwaarden} was the sole maintainer of {\it NetHack\/} for +\noindent \textit{Ron Van Iwaarden} was the sole maintainer of \textit{NetHack} for OS/2 the past several releases. Unfortunately Ron's last OS/2 machine stopped working in -early 2006. A great many thanks to Ron for keeping {\it NetHack\/} alive on +early 2006. A great many thanks to Ron for keeping \textit{NetHack} alive on OS/2 all these years. %.pg \medskip -\noindent {\it Janne Salmij\"{a}rvi} and {\it Teemu Suikki} maintained -and enhanced the Amiga port of 3.4 after {\it Janne Salmij\"{a}rvi} resurrected +\noindent \textit{Janne Salmij\"{a}rvi} and \textit{Teemu Suikki} maintained +and enhanced the Amiga port of 3.4 after \textit{Janne Salmij\"{a}rvi} resurrected it for 3.3.1. %.pg \medskip -\noindent {\it Christian ``Marvin'' Bressler} maintained 3.4 for the Atari after he +\noindent \textit{Christian ``Marvin'' Bressler} maintained 3.4 for the Atari after he resurrected it for 3.3.1. %.pg \medskip -The release of {\it NetHack\/} 3.4.3 in December 2003 marked the beginning of +The release of \textit{NetHack} 3.4.3 in December 2003 marked the beginning of a long release hiatus. 3.4.3 proved to be a remarkably stable version that provided continued enjoyment by the community for more than a decade. The -{\it NetHack Development Team} slowly and quietly continued to work on the game behind the scenes +\textit{NetHack Development Team} slowly and quietly continued to work on the game behind the scenes during the tenure of 3.4.3. It was during that same period that several new -variants emerged within the {\it NetHack\/} community. Notably sporkhack by -Derek S. Ray, {\it unnethack\/} by Patric Mueller, {\it nitrohack\/} and its +variants emerged within the \textit{NetHack} community. Notably sporkhack by +Derek S. Ray, \textit{unnethack} by Patric Mueller, \textit{nitrohack} and its successors originally by Daniel Thaler and then by Alex Smith, and -{\it Dynahack\/} by Tung Nguyen. +\textit{Dynahack} by Tung Nguyen. Some of those variants continue to be developed, maintained, and enjoyed by the community to this day. @@ -7258,9 +7255,9 @@ released publicly by other parties. Since that code was a work-in-progress and had not gone through the process of debugging it as a suitable release, it was decided that the version numbers present on that code snapshot would -be retired and never used in an official {\it NetHack\/} release. -An announcement was posted on the {\it NetHack Development Team}'s official -{\it nethack.org\/} website +be retired and never used in an official \textit{NetHack} release. +An announcement was posted on the \textit{NetHack Development Team}'s official +\textit{nethack.org} website to that effect, stating that there would never be a 3.4.4, 3.5, or 3.5.0 official release version. @@ -7271,20 +7268,20 @@ In January 2015, preparation began for the release of NetHack 3.6. %.pg \medskip At the beginning of development for what would eventually get released as -3.6.0, the {\it NetHack Development Team} consisted of {\it Warwick Allison}, -{\it Michael Allison}, {\it Ken Arromdee}, -{\it David Cohrs}, {\it Jessie Collet}, -{\it Ken Lorber}, {\it Dean Luick}, {\it Pat Rankin}, -{\it Mike Stephenson}, {\it Janet Walz}, and {\it Paul Winner}. +3.6.0, the \textit{NetHack Development Team} consisted of \textit{Warwick Allison}, +\textit{Michael Allison}, \textit{Ken Arromdee}, +\textit{David Cohrs}, \textit{Jessie Collet}, +\textit{Ken Lorber}, \textit{Dean Luick}, \textit{Pat Rankin}, +\textit{Mike Stephenson}, \textit{Janet Walz}, and \textit{Paul Winner}. In early 2015, ahead of the release of 3.6.0, new members -{\it Sean Hunt}, {\it Pasi Kallinen}, and {\it Derek S. Ray} -joined the {\it NetHack\/} development team. +\textit{Sean Hunt}, \textit{Pasi Kallinen}, and \textit{Derek S. Ray} +joined the \textit{NetHack} development team. %.pg \medskip Near the end of the development of 3.6.0, one of the significant inspirations for many of the humorous and fun features found in the game, -author Terry Pratchett, passed away. {\it NetHack\/} 3.6.0 introduced +author Terry Pratchett, passed away. \textit{NetHack} 3.6.0 introduced a tribute to him. %.pg @@ -7295,46 +7292,46 @@ patches. Many bugs were fixed and some code was restructured. %.pg \medskip -The {\it NetHack Development Team}, as well as {\it Steve VanDevender} and -{\it Kevin Smolkowski}, ensured that {\it NetHack\/} 3.6 continued to +The \textit{NetHack Development Team}, as well as \textit{Steve VanDevender} and +\textit{Kevin Smolkowski}, ensured that \textit{NetHack} 3.6 continued to operate on various UNIX flavors and maintained the X11 interface. %.pg \medskip -{\it Ken Lorber}, {\it Haoyang Wang}, {\it Pat Rankin}, and {\it Dean Luick} -maintained the port of {\it NetHack\/} 3.6 for MacOS. +\textit{Ken Lorber}, \textit{Haoyang Wang}, \textit{Pat Rankin}, and \textit{Dean Luick} +maintained the port of \textit{NetHack} 3.6 for MacOS. %.pg \medskip -{\it Michael Allison}, {\it David Cohrs}, {\it Bart House}, -{\it Pasi Kallinen}, {\it Alex Kompel}, {\it Dion Nicolaas}, -{\it Derek S. Ray} and {\it Yitzhak Sapir} -maintained the port of {\it NetHack\/} 3.6 for Microsoft Windows. +\textit{Michael Allison}, \textit{David Cohrs}, \textit{Bart House}, +\textit{Pasi Kallinen}, \textit{Alex Kompel}, \textit{Dion Nicolaas}, +\textit{Derek S. Ray} and \textit{Yitzhak Sapir} +maintained the port of \textit{NetHack} 3.6 for Microsoft Windows. %.pg \medskip -{\it Pat Rankin} attempted to keep the VMS port running for NetHack 3.6, -hindered by limited access. {\it Kevin Smolkowski} has updated and tested it +\textit{Pat Rankin} attempted to keep the VMS port running for NetHack 3.6, +hindered by limited access. \textit{Kevin Smolkowski} has updated and tested it for the most recent version of OpenVMS (V8.4 as of this writing) on Alpha and Integrity (aka Itanium aka IA64) but not VAX. %.pg \medskip -{\it Ray Chason} resurrected the MS-DOS port for 3.6 and contributed the +\textit{Ray Chason} resurrected the MS-DOS port for 3.6 and contributed the necessary updates to the community at large. %.pg \medskip In late April 2018, several hundred bug fixes for 3.6.0 and some new features were assembled and released as NetHack 3.6.1. -The {\it NetHack Development Team} at the +The \textit{NetHack Development Team} at the time of release of 3.6.1 consisted of -{\it Warwick Allison}, {\it Michael Allison}, {\it Ken Arromdee}, -{\it David Cohrs}, {\it Jessie Collet}, -{\it Pasi Kallinen}, {\it Ken Lorber}, {\it Dean Luick}, -{\it Patric Mueller}, {\it Pat Rankin}, {\it Derek S. Ray}, -{\it Alex Smith}, {\it Mike Stephenson}, {\it Janet Walz}, and -{\it Paul Winner}. +\textit{Warwick Allison}, \textit{Michael Allison}, \textit{Ken Arromdee}, +\textit{David Cohrs}, \textit{Jessie Collet}, +\textit{Pasi Kallinen}, \textit{Ken Lorber}, \textit{Dean Luick}, +\textit{Patric Mueller}, \textit{Pat Rankin}, \textit{Derek S. Ray}, +\textit{Alex Smith}, \textit{Mike Stephenson}, \textit{Janet Walz}, and +\textit{Paul Winner}. %.pg \medskip @@ -7343,8 +7340,8 @@ the adopted curses window port, were released as 3.6.2. %.pg \medskip -{\it Bart House}, who had contributed to the game as a porting team participant -for decades, joined the {\it NetHack Development Team} in late May 2019. +\textit{Bart House}, who had contributed to the game as a porting team participant +for decades, joined the \textit{NetHack Development Team} in late May 2019. %.pg \medskip @@ -7373,25 +7370,21 @@ some bug fixes. %.pg \medskip -\noindent The official {\it NetHack\/} web site is maintained by {\it Ken Lorber} at -{\catcode`\#=11 -\special{html:}} -https:{\tt /}{\tt /}www.nethack.org{\tt /}. -{\catcode`\#=11 -\special{html:}} +\noindent The official \textit{NetHack} web site is maintained by \textit{Ken Lorber} at +\url{https://www.nethack.org}. %.pg %.hn 2 \subsection*{Special Thanks} -\noindent On behalf of the {\it NetHack\/} community, thank you very much once -again to {\it M. Drew Streib} and {\it Pasi Kallinen} for providing a -public NetHack server at nethack.alt.org. Thanks to {\it Keith Simpson} -and {\it Andy Thomson} for hardfought.org. Thanks to all those +\noindent On behalf of the \textit{NetHack} community, thank you very much once +again to \textit{M. Drew Streib} and \textit{Pasi Kallinen} for providing a +public NetHack server at nethack.alt.org. Thanks to \textit{Keith Simpson} +and \textit{Andy Thomson} for hardfought.org. Thanks to all those unnamed dungeoneers who invest their time and effort into annual -{\it NetHack\/} tournaments such as {\it Junethack}, -{\it The November NetHack Tournament}, and in days past, -{\it devnull.net\/} (gone for now, but not forgotten). +\textit{NetHack} tournaments such as \textit{Junethack}, +\textit{The November NetHack Tournament}, and in days past, +\textit{devnull.net} (gone for now, but not forgotten). \clearpage %.hn 2 @@ -7399,7 +7392,7 @@ unnamed dungeoneers who invest their time and effort into annual %.pg \noindent From time to time, some depraved individual out there in netland sends a particularly intriguing modification to help out with the game. The -{\it NetHack Development Team} sometimes makes note of the names of the worst +\textit{NetHack Development Team} sometimes makes note of the names of the worst of these miscreants in this, the list of Dungeoneers: \medskip %.sd @@ -7416,7 +7409,7 @@ Andy Thomson & John Kallen & Patric Mueller\\ Ari Huttunen & John Rupley & Paul Winner\\ Bart House & John S. Bien & Pierre Martineau\\ Benson I. Margulies & Johnny Lee & Ralf Brown\\ -Bill Dyer & Jon W\{tte & Ray Chason\\ +Bill Dyer & Jon W\textbraceleft tte & Ray Chason\\ Boudewijn Waijers & Jonathan Handler & Richard Addison\\ Bruce Cox & Joshua Delahunty & Richard Beigel\\ Bruce Holloway & Karl Garrison & Richard P. Hughey\\ From 37df18b59c3a11f58946ba4ba3343ab7b1f66cbf Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 4 Jan 2026 13:56:02 +0200 Subject: [PATCH 171/442] Use struct to pass data out of mfndpos Instead of passing two array pointers to mfndpos, fold the data into a specific struct. --- include/extern.h | 8 ++++++-- include/mfndpos.h | 6 ++++++ src/dogmove.c | 27 +++++++++++++-------------- src/mon.c | 34 ++++++++++++++++++---------------- src/monmove.c | 41 +++++++++++++++++++---------------------- src/priest.c | 17 ++++++++--------- 6 files changed, 70 insertions(+), 63 deletions(-) diff --git a/include/extern.h b/include/extern.h index 8a5056d91..27732ea42 100644 --- a/include/extern.h +++ b/include/extern.h @@ -71,6 +71,10 @@ #include "artifact.h" #endif +#ifndef MFNDPOS_H +#include "mfndpos.h" +#endif + /* ### alloc.c ### */ #if 0 @@ -1748,7 +1752,7 @@ extern boolean can_touch_safely(struct monst *, struct obj *) NONNULLARG12; extern int can_carry(struct monst *, struct obj *) NONNULLARG12; extern long mon_allowflags(struct monst *) NONNULLARG1; extern boolean m_in_air(struct monst *) NONNULLARG1; -extern int mfndpos(struct monst *, coord *, long *, long) NONNULLPTRS; +extern int mfndpos(struct monst *, struct mfndposdata *, long) NONNULLPTRS; extern boolean monnear(struct monst *, coordxy, coordxy) NONNULLARG1; extern void dmonsfree(void); extern void elemental_clog(struct monst *) NONNULLARG1; @@ -1918,7 +1922,7 @@ extern boolean accessible(coordxy, coordxy); extern void set_apparxy(struct monst *) NONNULLARG1; extern boolean can_ooze(struct monst *) NONNULLARG1; extern boolean can_fog(struct monst *) NONNULLARG1; -extern boolean should_displace(struct monst *, coord *, long *, int, coordxy, +extern boolean should_displace(struct monst *, struct mfndposdata, coordxy, coordxy) NONNULLPTRS; extern boolean undesirable_disp(struct monst *, coordxy, coordxy) NONNULLARG1; extern boolean can_hide_under_obj(struct obj *); diff --git a/include/mfndpos.h b/include/mfndpos.h index 762f5cdb9..9e560a311 100644 --- a/include/mfndpos.h +++ b/include/mfndpos.h @@ -30,4 +30,10 @@ #endif /* clang-format on */ +struct mfndposdata { + int cnt; + coord poss[9]; + long info[9]; +}; + #endif /* MFNDPOS_H */ diff --git a/src/dogmove.c b/src/dogmove.c index 50f40f135..52ae569bf 100644 --- a/src/dogmove.c +++ b/src/dogmove.c @@ -983,8 +983,8 @@ dog_move( coordxy nx, ny; /* temporary coordinates */ xint16 cnt, uncursedcnt, chcnt; int chi = -1, nidist, ndist; - coord poss[9]; - long info[9], allowflags; + long allowflags; + struct mfndposdata mfp; #define GDIST(x, y) (dist2(x, y, gg.gx, gg.gy)) /* @@ -1020,7 +1020,6 @@ dog_move( nix = omx; /* set before newdogpos */ niy = omy; cursemsg[0] = FALSE; /* lint suppression */ - info[0] = 0; /* ditto */ if (edog) { j = dog_invent(mtmp, edog, udist); @@ -1054,7 +1053,7 @@ dog_move( } #endif allowflags = mon_allowflags(mtmp); - cnt = mfndpos(mtmp, poss, info, allowflags); + cnt = mfndpos(mtmp, &mfp, allowflags); /* Normally dogs don't step on cursed items, but if they have no * other choice they will. This requires checking ahead of time @@ -1062,16 +1061,16 @@ dog_move( */ uncursedcnt = 0; for (i = 0; i < cnt; i++) { - nx = poss[i].x; - ny = poss[i].y; - if (MON_AT(nx, ny) && !((info[i] & ALLOW_M) || info[i] & ALLOW_MDISP)) + nx = mfp.poss[i].x; + ny = mfp.poss[i].y; + if (MON_AT(nx, ny) && !((mfp.info[i] & ALLOW_M) || mfp.info[i] & ALLOW_MDISP)) continue; if (cursed_object_at(nx, ny)) continue; uncursedcnt++; } - better_with_displacing = should_displace(mtmp, poss, info, cnt, + better_with_displacing = should_displace(mtmp, mfp, gg.gx, gg.gy); chcnt = 0; @@ -1079,8 +1078,8 @@ dog_move( nidist = GDIST(nix, niy); for (i = 0; i < cnt; i++) { - nx = poss[i].x; - ny = poss[i].y; + nx = mfp.poss[i].x; + ny = mfp.poss[i].y; cursemsg[i] = FALSE; /* if leashed, we drag him along. */ @@ -1093,7 +1092,7 @@ dog_move( ranged_only = FALSE; - if ((info[i] & ALLOW_M) && MON_AT(nx, ny)) { + if ((mfp.info[i] & ALLOW_M) && MON_AT(nx, ny)) { int mstatus; struct monst *mtmp2 = m_at(nx, ny); /* weight the audacity of the pet to attack a differently-leveled @@ -1162,7 +1161,7 @@ dog_move( } return MMOVE_DONE; } - if ((info[i] & ALLOW_MDISP) && MON_AT(nx, ny) + if ((mfp.info[i] & ALLOW_MDISP) && MON_AT(nx, ny) && better_with_displacing && !undesirable_disp(mtmp, nx, ny)) { int mstatus; struct monst *mtmp2 = m_at(nx, ny); @@ -1189,7 +1188,7 @@ dog_move( */ struct trap *trap; - if ((info[i] & ALLOW_TRAPS) && (trap = t_at(nx, ny))) { + if ((mfp.info[i] & ALLOW_TRAPS) && (trap = t_at(nx, ny))) { if (mtmp->mleashed) { if (!Deaf) whimper(mtmp); @@ -1271,7 +1270,7 @@ dog_move( if (nix != omx || niy != omy) { boolean wasseen; - if (info[chi] & ALLOW_U) { + if (mfp.info[chi] & ALLOW_U) { if (mtmp->mleashed) { /* play it safe */ pline_mon(mtmp, "%s breaks loose of %s leash!", Monnam(mtmp), mhis(mtmp)); diff --git a/src/mon.c b/src/mon.c index bc5868e31..364a289ff 100644 --- a/src/mon.c +++ b/src/mon.c @@ -2111,8 +2111,7 @@ m_in_air(struct monst *mtmp) int mfndpos( struct monst *mon, - coord *poss, /* coord poss[9] */ - long *info, /* long info[9] */ + struct mfndposdata *data, long flag) { struct permonst *mdat = mon->data; @@ -2132,6 +2131,8 @@ mfndpos( y = mon->my; nowtyp = levl[x][y].typ; + (void)memset((genericptr_t) data, 0, sizeof(struct mfndposdata)); + nodiag = NODIAG(mdat - mons); wantpool = (mdat->mlet == S_EEL); poolok = ((!Is_waterlevel(&u.uz) && m_in_air(mon)) @@ -2247,11 +2248,11 @@ mfndpos( dispy = ny; } - info[cnt] = 0; + data->info[cnt] = 0; if (onscary(dispx, dispy, mon)) { if (!(flag & ALLOW_SSM)) continue; - info[cnt] |= ALLOW_SSM; + data->info[cnt] |= ALLOW_SSM; } if (u_at(nx, ny) || (nx == mon->mux && ny == mon->muy)) { @@ -2267,25 +2268,25 @@ mfndpos( } if (!(flag & ALLOW_U)) continue; - info[cnt] |= ALLOW_U; + data->info[cnt] |= ALLOW_U; } else { if (MON_AT(nx, ny)) { struct monst *mtmp2 = m_at(nx, ny); long mmflag = flag | mm_aggression(mon, mtmp2); if (mmflag & ALLOW_M) { - info[cnt] |= ALLOW_M; + data->info[cnt] |= ALLOW_M; if (mtmp2->mtame) { if (!(mmflag & ALLOW_TM)) continue; - info[cnt] |= ALLOW_TM; + data->info[cnt] |= ALLOW_TM; } } else { flag &= ~ALLOW_MDISP; /* depends upon defender */ mmflag = flag | mm_displacement(mon, mtmp2); if (!(mmflag & ALLOW_MDISP)) continue; - info[cnt] |= ALLOW_MDISP; + data->info[cnt] |= ALLOW_MDISP; } } /* Note: ALLOW_SANCT only prevents movement, not @@ -2296,23 +2297,23 @@ mfndpos( && in_your_sanctuary((struct monst *) 0, nx, ny)) { if (!(flag & ALLOW_SANCT)) continue; - info[cnt] |= ALLOW_SANCT; + data->info[cnt] |= ALLOW_SANCT; } } if (checkobj && sobj_at(CLOVE_OF_GARLIC, nx, ny)) { if (flag & NOGARLIC) continue; - info[cnt] |= NOGARLIC; + data->info[cnt] |= NOGARLIC; } if (checkobj && sobj_at(BOULDER, nx, ny)) { if (!(flag & ALLOW_ROCK)) continue; - info[cnt] |= ALLOW_ROCK; + data->info[cnt] |= ALLOW_ROCK; } if (monseeu && monlineu(mon, nx, ny)) { if (flag & NOTONL) continue; - info[cnt] |= NOTONL; + data->info[cnt] |= NOTONL; } /* check for diagonal tight squeeze */ if (nx != x && ny != y && bad_rock(mdat, x, ny) @@ -2332,17 +2333,17 @@ mfndpos( } /* fixed-destination teleport trap, was used by hero */ if (fixed_tele_trap(ttmp) && hastrack(nx, ny)) - info[cnt] |= ALLOW_TRAPS; + data->info[cnt] |= ALLOW_TRAPS; else if (!m_harmless_trap(mon, ttmp)) { if (!(flag & ALLOW_TRAPS)) { if (mon_knows_traps(mon, ttmp->ttyp)) continue; } - info[cnt] |= ALLOW_TRAPS; + data->info[cnt] |= ALLOW_TRAPS; } } - poss[cnt].x = nx; - poss[cnt].y = ny; + data->poss[cnt].x = nx; + data->poss[cnt].y = ny; cnt++; } } @@ -2350,6 +2351,7 @@ mfndpos( wantpool = FALSE; goto nexttry; } + data->cnt = cnt; return cnt; } diff --git a/src/monmove.c b/src/monmove.c index 0b0b27dfb..39a453e08 100644 --- a/src/monmove.c +++ b/src/monmove.c @@ -1069,9 +1069,7 @@ itsstuck(struct monst *mtmp) boolean should_displace( struct monst *mtmp, - coord *poss, /* coord poss[9] */ - long *info, /* long info[9] */ - int cnt, + struct mfndposdata data, coordxy ggx, coordxy ggy) { @@ -1081,11 +1079,11 @@ should_displace( int i, nx, ny; int ndist; - for (i = 0; i < cnt; i++) { - nx = poss[i].x; - ny = poss[i].y; + for (i = 0; i < data.cnt; i++) { + nx = data.poss[i].x; + ny = data.poss[i].y; ndist = dist2(nx, ny, ggx, ggy); - if (MON_AT(nx, ny) && (info[i] & ALLOW_MDISP) && !(info[i] & ALLOW_M) + if (MON_AT(nx, ny) && (data.info[i] & ALLOW_MDISP) && !(data.info[i] & ALLOW_M) && !undesirable_disp(mtmp, nx, ny)) { if (shortest_with_displacing == -1 || (ndist < shortest_with_displacing)) @@ -1726,7 +1724,7 @@ m_move(struct monst *mtmp, int after) struct permonst *ptr; int chi, mmoved = MMOVE_NOTHING, /* not strictly nec.: chi >= 0 will do */ preferredrange_min = 0, preferredrange_max = 0; - long info[9]; + struct mfndposdata mfp; long flag; coordxy omx = mtmp->mx, omy = mtmp->my; @@ -1922,9 +1920,8 @@ m_move(struct monst *mtmp, int after) int jcnt, cnt; int ndist, nidist; coord *mtrk; - coord poss[9]; - cnt = mfndpos(mtmp, poss, info, flag); + cnt = mfndpos(mtmp, &mfp, flag); if (cnt == 0 && !is_unicorn(mtmp->data)) { if (find_defensive(mtmp, TRUE) && use_defensive(mtmp)) return MMOVE_DONE; @@ -1941,22 +1938,22 @@ m_move(struct monst *mtmp, int after) if (is_unicorn(ptr) && noteleport_level(mtmp)) { /* on noteleport levels, perhaps we cannot avoid hero */ for (i = 0; i < cnt; i++) - if (!(info[i] & NOTONL)) + if (!(mfp.info[i] & NOTONL)) avoid = TRUE; } better_with_displacing = - should_displace(mtmp, poss, info, cnt, ggx, ggy); + should_displace(mtmp, mfp, ggx, ggy); for (i = 0; i < cnt; i++) { - if (avoid && (info[i] & NOTONL)) + if (avoid && (mfp.info[i] & NOTONL)) continue; - nx = poss[i].x; - ny = poss[i].y; + nx = mfp.poss[i].x; + ny = mfp.poss[i].y; if (m_avoid_kicked_loc(mtmp, nx, ny)) continue; - if (MON_AT(nx, ny) && (info[i] & ALLOW_MDISP) - && !(info[i] & ALLOW_M) && !better_with_displacing) + if (MON_AT(nx, ny) && (mfp.info[i] & ALLOW_MDISP) + && !(mfp.info[i] & ALLOW_M) && !better_with_displacing) continue; if (appr != 0) { mtrk = &mtmp->mtrack[0]; @@ -2004,8 +2001,8 @@ m_move(struct monst *mtmp, int after) * mfndpos) has no effect for normal attacks, though it lets a * confused monster attack you by accident. */ - assert(IndexOk(chi, info)); - if (info[chi] & ALLOW_U) { + assert(IndexOk(chi, mfp.info)); + if (mfp.info[chi] & ALLOW_U) { nix = mtmp->mux; niy = mtmp->muy; } @@ -2020,11 +2017,11 @@ m_move(struct monst *mtmp, int after) * Pets get taken care of above and shouldn't reach this code. * Conflict gets handled even farther away (movemon()). */ - if ((info[chi] & ALLOW_M) != 0 + if ((mfp.info[chi] & ALLOW_M) != 0 || (nix == mtmp->mux && niy == mtmp->muy)) return m_move_aggress(mtmp, nix, niy); - if ((info[chi] & ALLOW_MDISP) != 0) { + if ((mfp.info[chi] & ALLOW_MDISP) != 0) { struct monst *mtmp2; int mstatus; @@ -2041,7 +2038,7 @@ m_move(struct monst *mtmp, int after) if (!m_in_out_region(mtmp, nix, niy)) return MMOVE_DONE; - if ((info[chi] & ALLOW_ROCK) && m_can_break_boulder(mtmp)) { + if ((mfp.info[chi] & ALLOW_ROCK) && m_can_break_boulder(mtmp)) { (void) m_break_boulder(mtmp, nix, niy); return MMOVE_DONE; } diff --git a/src/priest.c b/src/priest.c index 9345fd94f..41e77179a 100644 --- a/src/priest.c +++ b/src/priest.c @@ -46,8 +46,7 @@ move_special(struct monst *mtmp, boolean in_his_shop, schar appr, coordxy nx, ny, nix, niy; schar i; schar chcnt, cnt; - coord poss[9]; - long info[9]; + struct mfndposdata mfp; long ninfo = 0; long allowflags; #if 0 /* dead code; see below */ @@ -64,11 +63,11 @@ move_special(struct monst *mtmp, boolean in_his_shop, schar appr, nix = omx; niy = omy; allowflags = mon_allowflags(mtmp); - cnt = mfndpos(mtmp, poss, info, allowflags); + cnt = mfndpos(mtmp, &mfp, allowflags); if (mtmp->isshk && avoid && uondoor) { /* perhaps we cannot avoid him */ for (i = 0; i < cnt; i++) - if (!(info[i] & NOTONL)) + if (!(mfp.info[i] & NOTONL)) goto pick_move; avoid = FALSE; } @@ -77,18 +76,18 @@ move_special(struct monst *mtmp, boolean in_his_shop, schar appr, pick_move: chcnt = 0; for (i = 0; i < cnt; i++) { - nx = poss[i].x; - ny = poss[i].y; + nx = mfp.poss[i].x; + ny = mfp.poss[i].y; if (IS_ROOM(levl[nx][ny].typ) || (mtmp->isshk && (!in_his_shop || ESHK(mtmp)->following))) { - if (avoid && (info[i] & NOTONL) && !(info[i] & ALLOW_M)) + if (avoid && (mfp.info[i] & NOTONL) && !(mfp.info[i] & ALLOW_M)) continue; if ((!appr && !rn2(++chcnt)) || (appr && GDIST(nx, ny) < GDIST(nix, niy)) - || (info[i] & ALLOW_M)) { + || (mfp.info[i] & ALLOW_M)) { nix = nx; niy = ny; - ninfo = info[i]; + ninfo = mfp.info[i]; } } } From f2cd27551d20b49323118e77853b0a7c755754dc Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 4 Jan 2026 16:34:28 +0200 Subject: [PATCH 172/442] Just pass a mfndposdata pointer to should_displace --- include/extern.h | 2 +- src/dogmove.c | 2 +- src/monmove.c | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/extern.h b/include/extern.h index 27732ea42..b9f2e5cce 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1922,7 +1922,7 @@ extern boolean accessible(coordxy, coordxy); extern void set_apparxy(struct monst *) NONNULLARG1; extern boolean can_ooze(struct monst *) NONNULLARG1; extern boolean can_fog(struct monst *) NONNULLARG1; -extern boolean should_displace(struct monst *, struct mfndposdata, coordxy, +extern boolean should_displace(struct monst *, struct mfndposdata *, coordxy, coordxy) NONNULLPTRS; extern boolean undesirable_disp(struct monst *, coordxy, coordxy) NONNULLARG1; extern boolean can_hide_under_obj(struct obj *); diff --git a/src/dogmove.c b/src/dogmove.c index 52ae569bf..c22eba74b 100644 --- a/src/dogmove.c +++ b/src/dogmove.c @@ -1070,7 +1070,7 @@ dog_move( uncursedcnt++; } - better_with_displacing = should_displace(mtmp, mfp, + better_with_displacing = should_displace(mtmp, &mfp, gg.gx, gg.gy); chcnt = 0; diff --git a/src/monmove.c b/src/monmove.c index 39a453e08..2721b54a0 100644 --- a/src/monmove.c +++ b/src/monmove.c @@ -1069,7 +1069,7 @@ itsstuck(struct monst *mtmp) boolean should_displace( struct monst *mtmp, - struct mfndposdata data, + struct mfndposdata *data, coordxy ggx, coordxy ggy) { @@ -1079,11 +1079,11 @@ should_displace( int i, nx, ny; int ndist; - for (i = 0; i < data.cnt; i++) { - nx = data.poss[i].x; - ny = data.poss[i].y; + for (i = 0; i < data->cnt; i++) { + nx = data->poss[i].x; + ny = data->poss[i].y; ndist = dist2(nx, ny, ggx, ggy); - if (MON_AT(nx, ny) && (data.info[i] & ALLOW_MDISP) && !(data.info[i] & ALLOW_M) + if (MON_AT(nx, ny) && (data->info[i] & ALLOW_MDISP) && !(data->info[i] & ALLOW_M) && !undesirable_disp(mtmp, nx, ny)) { if (shortest_with_displacing == -1 || (ndist < shortest_with_displacing)) @@ -1942,7 +1942,7 @@ m_move(struct monst *mtmp, int after) avoid = TRUE; } better_with_displacing = - should_displace(mtmp, mfp, ggx, ggy); + should_displace(mtmp, &mfp, ggx, ggy); for (i = 0; i < cnt; i++) { if (avoid && (mfp.info[i] & NOTONL)) continue; From aec4504337664ca21910c73045563ac3d0eae8d6 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 4 Jan 2026 10:37:19 -0500 Subject: [PATCH 173/442] let build be possible with current (5.5.0) Lua Lua 5.5.0 became the most-current Lua version on December 22, 2025 --- src/nhlua.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/nhlua.c b/src/nhlua.c index b43e4c089..ebf439af9 100644 --- a/src/nhlua.c +++ b/src/nhlua.c @@ -2216,8 +2216,12 @@ nhl_init(nhl_sandbox_info *sbi) /* It would be nice to import EXPECTED from each build system. XXX */ /* And it would be nice to do it only once, but it's cheap. */ #ifndef NHL_VERSION_EXPECTED +#if LUA_VERSION_NUM >= 505 +#define NHL_VERSION_EXPECTED 50500 +#else #define NHL_VERSION_EXPECTED 50406 #endif +#endif #ifdef NHL_SANDBOX if (NHL_VERSION_EXPECTED != LUA_VERSION_RELEASE_NUM) { @@ -2978,7 +2982,11 @@ nhlL_newstate(nhl_sandbox_info *sbi, const char *name) nud->sid = ++gl.lua_sid; } - lua_State *L = lua_newstate(nhl_alloc, nud); + lua_State *L = lua_newstate(nhl_alloc, nud +#if LUA_VERSION_NUM >= 505 + , 0 +#endif + ); if (!L) panic("NULL lua_newstate"); From 642cf9a93e86973857800e0edfe51d890fe7b7b4 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 4 Jan 2026 11:12:50 -0500 Subject: [PATCH 174/442] bump Lua references from 5.4.6 to 5.4.8 --- Cross-compiling | 2 +- Porting | 2 +- azure-pipelines.yml | 4 +- outdated/.travis.yml | 16 ++--- .../sys/unix/hints/include/cross-amiga-pre | 4 +- outdated/sys/windows/travis-gcc.sh | 4 +- sys/msdos/Install.dos | 4 +- sys/msdos/Makefile.GCC | 4 +- sys/msdos/fetch-cross-compiler.sh | 2 +- sys/unix/Makefile.src | 2 +- sys/unix/Makefile.top | 2 +- sys/unix/NetHack.xcodeproj/project.pbxproj | 72 +++++++++---------- sys/unix/hints/include/cross-pre2.370 | 6 +- sys/vms/Makefile_dat.vms | 4 +- sys/vms/Makefile_doc.vms | 4 +- sys/vms/Makefile_src.vms | 4 +- sys/vms/Makefile_top.vms | 4 +- sys/vms/Makefile_utl.vms | 4 +- sys/vms/vmsbuild.com | 6 +- sys/windows/GNUmakefile | 8 +-- sys/windows/Makefile.nmake | 14 ++-- sys/windows/build-msys2.txt | 2 +- sys/windows/build-nmake.txt | 2 +- sys/windows/build-vs.txt | 2 +- sys/windows/fetch.cmd | 2 +- sys/windows/fetch.sh | 6 +- sys/windows/vs/FetchPrereq/fetchprereq.nmake | 2 +- sys/windows/vs/NetHackProperties.props | 2 +- sys/windows/vs/dirs.props | 2 +- 29 files changed, 96 insertions(+), 96 deletions(-) diff --git a/Cross-compiling b/Cross-compiling index 0eb9488a7..d609ab585 100644 --- a/Cross-compiling +++ b/Cross-compiling @@ -413,7 +413,7 @@ Using the cross-compiler, build the following targets: b) Lua (mandatory in 3.7) - lib/lua-5.4.6/src + lib/lua-5.4.8/src from sources: lua.c, lapi.c, lauxlib.c, lbaselib.c, lcode.c, lcorolib.c, lctype.c, ldblib.c, ldebug.c, diff --git a/Porting b/Porting index 1abba0d93..45aa1bfc5 100644 --- a/Porting +++ b/Porting @@ -201,7 +201,7 @@ need to be included in the packaging of the game. 4.3. Lua Compile and link into a library, or obtain a prebuilt Lua library for -your platform. Place the Lua source into lib/lua-5.4.6 (or other folder +your platform. Place the Lua source into lib/lua-5.4.8 (or other folder representing an appropriate Lua version); place the compiled Lua library into lib. diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4c0a5b5d2..fa0dcc7e9 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -129,7 +129,7 @@ steps: export LUASRC=../submodules/lua export ADD_CURSES=Y export PDCURSES_TOP=../submodules/pdcursesmod - export LUA_VERSION=5.4.6 + export LUA_VERSION=5.4.8 # # 64-bit #export CURLSRC=https://github.com/brechtsanders/winlibs_mingw/releases/download/14.2.0posix-19.1.1-12.0.0-ucrt-r2/winlibs-x86_64-posix-seh-gcc-14.2.0-llvm-19.1.1-mingw-w64ucrt-12.0.0-r2.zip @@ -235,7 +235,7 @@ steps: sys/msdos/fetch-cross-compiler.sh retVal=$? if [ $retVal -eq 0 ]; then - make LUA_VERSION=5.4.6 WANT_WIN_TTY=1 WANT_WIN_CURSES=1 CROSS_TO_MSDOS=1 $IN_CI package + make LUA_VERSION=5.4.8 WANT_WIN_TTY=1 WANT_WIN_CURSES=1 CROSS_TO_MSDOS=1 $IN_CI package fi condition: and(eq( variables['Agent.OS'], 'Linux' ), eq( variables.toolchain, 'cross')) workingDirectory: $(Agent.BuildDirectory)/$(netHackPath) diff --git a/outdated/.travis.yml b/outdated/.travis.yml index 889051258..b0f0dfc3b 100644 --- a/outdated/.travis.yml +++ b/outdated/.travis.yml @@ -3,7 +3,7 @@ matrix: include: - name: linux-xenial-gcc-win-all os: linux - env: HINTS=linux.370 LUA_VERSION=5.4.6 + env: HINTS=linux.370 LUA_VERSION=5.4.8 compiler: gcc addons: apt: @@ -21,7 +21,7 @@ matrix: - make LUA_VERSION=$LUA_VERSION WANT_WIN_ALL=1 QT_SELECT=5 MOC=moc install - name: linux-bionic-gcc-win-all os: linux - env: HINTS=linux.370 LUA_VERSION=5.4.6 + env: HINTS=linux.370 LUA_VERSION=5.4.8 dist: bionic compiler: gcc addons: @@ -40,7 +40,7 @@ matrix: - make LUA_VERSION=$LUA_VERSION WANT_WIN_ALL=1 QT_SELECT=5 MOC=moc install - name: linux-focal-clang-win-all os: linux - env: HINTS=linux.370 LUA_VERSION=5.4.6 + env: HINTS=linux.370 LUA_VERSION=5.4.8 dist: focal compiler: clang addons: @@ -59,7 +59,7 @@ matrix: - make LUA_VERSION=$LUA_VERSION WANT_WIN_ALL=1 QT_SELECT=5 MOC=moc install - name: linux-xenial-gcc-nocommon os: linux - env: HINTS=linux.370 LUA_VERSION=5.4.6 + env: HINTS=linux.370 LUA_VERSION=5.4.8 dist: xenial compiler: gcc script: @@ -70,7 +70,7 @@ matrix: - make install - name: linux-focal-gcc9-win-all os: linux - env: HINTS=linux.370 LUA_VERSION=5.4.6 + env: HINTS=linux.370 LUA_VERSION=5.4.8 dist: focal compiler: gcc addons: @@ -91,7 +91,7 @@ matrix: - make LUA_VERSION=$LUA_VERSION WANT_WIN_ALL=1 QT_SELECT=5 MOC=moc install - name: linux-xenial-gcc-minimal os: linux - env: HINTS=linux-minimal LUA_VERSION=5.4.6 + env: HINTS=linux-minimal LUA_VERSION=5.4.8 compiler: gcc script: | cd sys/unix/ && sh setup.sh hints/$HINTS && cd ../../ @@ -128,7 +128,7 @@ matrix: script: - export ADD_CURSES=Y - export PDCURSES_TOP=../lib/pdcurses - - export LUA_VERSION=5.4.6 + - export LUA_VERSION=5.4.8 - sh sys/windows/travis-gcc.sh - test -d "lib/lua-$LUA_VERSION/src" || exit 0 - test -d "lib/pdcurses" || exit 0 @@ -137,7 +137,7 @@ matrix: - mingw32-make LUA_VERSION=$LUA_VERSION install - name: msdos-linux-focal-djgpp-crosscompile os: linux - env: HINTS=linux.370 LUA_VERSION=5.4.6 + env: HINTS=linux.370 LUA_VERSION=5.4.8 dist: focal compiler: gcc script: diff --git a/outdated/sys/unix/hints/include/cross-amiga-pre b/outdated/sys/unix/hints/include/cross-amiga-pre index 5c59f102d..2d5b24ede 100644 --- a/outdated/sys/unix/hints/include/cross-amiga-pre +++ b/outdated/sys/unix/hints/include/cross-amiga-pre @@ -24,9 +24,9 @@ endif ifdef BUILD_TARGET_LUA #===============-================================================= # LUA library -# Source from http://www.lua.org/ftp/lua-5.4.6.tar.gz +# Source from http://www.lua.org/ftp/lua-5.4.8.tar.gz #================================================================= -LUA_VERSION ?=5.4.6 +LUA_VERSION ?=5.4.8 LUATOP ?= ../lib/lua-$(LUA_VERSION) LUASRCDIR ?= $(LUATOP)/src LUAOBJFILES1 = $(TARGETPFX)lapi.o $(TARGETPFX)lauxlib.o \ diff --git a/outdated/sys/windows/travis-gcc.sh b/outdated/sys/windows/travis-gcc.sh index c61137790..e6092375f 100644 --- a/outdated/sys/windows/travis-gcc.sh +++ b/outdated/sys/windows/travis-gcc.sh @@ -3,6 +3,6 @@ mkdir -p lib cd lib git clone --depth 1 https://github.com/wmcbrine/PDCurses.git pdcurses #git clone --depth 1 https://github.com/universal-ctags/ctags.git ctags -curl -R -O http://www.lua.org/ftp/lua-5.4.6.tar.gz -tar zxf lua-5.4.6.tar.gz +curl -R -O http://www.lua.org/ftp/lua-5.4.8.tar.gz +tar zxf lua-5.4.8.tar.gz cd ../ diff --git a/sys/msdos/Install.dos b/sys/msdos/Install.dos index 10b94888d..e60eb72b3 100644 --- a/sys/msdos/Install.dos +++ b/sys/msdos/Install.dos @@ -42,7 +42,7 @@ II. There once was a time when people built NetHack right on their DOS machine. a DOS-extender (for including in msdos packaging) from: http://sandmann.dotster.com/cwsdpmi/csdpmi7b.zip and Lua from: - http://www.lua.org/ftp/lua-5.4.6.tar.gz + http://www.lua.org/ftp/lua-5.4.8.tar.gz If you want the DOSVGA build, to support higher resolutions and Unicode symbols, you also need: @@ -123,7 +123,7 @@ Appendix B - Common Difficulties Encountered For example: make CROSS_TO_MSDOS=1 package - ( cd src ; make LUA_VERSION=5.4.6 nethack ) + ( cd src ; make LUA_VERSION=5.4.8 nethack ) make[1]: Entering directory '/home/johndoe/NHsource/src' mkdir -p ../targets/msdos ; make ../targets/msdos/exceptn.o ; make[2]: Entering directory '/home/johndoe/NHsource/src' diff --git a/sys/msdos/Makefile.GCC b/sys/msdos/Makefile.GCC index 069b2679a..8ae2be4dc 100644 --- a/sys/msdos/Makefile.GCC +++ b/sys/msdos/Makefile.GCC @@ -56,7 +56,7 @@ endif # Location of LUA # # Original source needs to be obtained from: -# http://www.lua.org/ftp/lua-5.4.6.tar.gz +# http://www.lua.org/ftp/lua-5.4.8.tar.gz # # This build assumes that the LUA sources are located # at the specified location. If they are actually elsewhere @@ -324,7 +324,7 @@ ALLOBJ = $(VOBJ) $(SOBJ) $(TILOBJ) $(TILOBJ2) $(VVOBJ) #===============-================================================= # LUA library -# Source from http://www.lua.org/ftp/lua-5.4.6.tar.gz +# Source from http://www.lua.org/ftp/lua-5.4.8.tar.gz #================================================================= LUASRC = $(LUATOP)/src diff --git a/sys/msdos/fetch-cross-compiler.sh b/sys/msdos/fetch-cross-compiler.sh index 36558a00c..037d3aaf3 100755 --- a/sys/msdos/fetch-cross-compiler.sh +++ b/sys/msdos/fetch-cross-compiler.sh @@ -15,7 +15,7 @@ if [ -z "$GCCVER" ]; then fi if [ -z "$LUA_VERSION" ]; then - export LUA_VERSION=5.4.6 + export LUA_VERSION=5.4.8 fi if [ ! -d "$(pwd)/lib" ]; then diff --git a/sys/unix/Makefile.src b/sys/unix/Makefile.src index e5763785f..e5a30e3c1 100644 --- a/sys/unix/Makefile.src +++ b/sys/unix/Makefile.src @@ -502,7 +502,7 @@ TARGET_HACKLIB = $(TARGETPFX)hacklib.a MAKEDEFS = ../util/makedefs # -lm required by lua -LUA_VERSION ?=5.4.6 +LUA_VERSION ?=5.4.8 LUABASE = liblua-$(LUA_VERSION).a LUALIB = ../lib/lua/$(LUABASE) LUALIBS = $(LUALIB) -lm $(DLLIB) diff --git a/sys/unix/Makefile.top b/sys/unix/Makefile.top index 6e45eafa9..af0a966ef 100644 --- a/sys/unix/Makefile.top +++ b/sys/unix/Makefile.top @@ -85,7 +85,7 @@ VARDAT = $(VARDATD) $(VARDATND) #CHGRP = chgrp # Lua version -LUA_VERSION = 5.4.6 +LUA_VERSION = 5.4.8 # # end of configuration diff --git a/sys/unix/NetHack.xcodeproj/project.pbxproj b/sys/unix/NetHack.xcodeproj/project.pbxproj index 0ba3338fb..99cd6d6ac 100644 --- a/sys/unix/NetHack.xcodeproj/project.pbxproj +++ b/sys/unix/NetHack.xcodeproj/project.pbxproj @@ -561,40 +561,40 @@ 54FB2B4A246310A600397C0E /* symbols.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = symbols.c; path = ../../src/symbols.c; sourceTree = ""; }; 54FCE8282223261F00F393C8 /* isaac64.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = isaac64.c; path = ../../src/isaac64.c; sourceTree = ""; }; BAE8010A27B97760002B3786 /* libnhlua.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libnhlua.a; sourceTree = BUILT_PRODUCTS_DIR; }; - BAE8011427B99CAB002B3786 /* lopcodes.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lopcodes.c; path = "../../lib/lua-5.4.6/src/lopcodes.c"; sourceTree = ""; }; - BAE8011527B99CAB002B3786 /* linit.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = linit.c; path = "../../lib/lua-5.4.6/src/linit.c"; sourceTree = ""; }; - BAE8011627B99CAB002B3786 /* lobject.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lobject.c; path = "../../lib/lua-5.4.6/src/lobject.c"; sourceTree = ""; }; - BAE8011727B99CAC002B3786 /* loslib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = loslib.c; path = "../../lib/lua-5.4.6/src/loslib.c"; sourceTree = ""; }; - BAE8011827B99CAC002B3786 /* lcorolib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lcorolib.c; path = "../../lib/lua-5.4.6/src/lcorolib.c"; sourceTree = ""; }; - BAE8011927B99CAC002B3786 /* lutf8lib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lutf8lib.c; path = "../../lib/lua-5.4.6/src/lutf8lib.c"; sourceTree = ""; }; - BAE8011A27B99CAC002B3786 /* ltable.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ltable.c; path = "../../lib/lua-5.4.6/src/ltable.c"; sourceTree = ""; }; - BAE8011B27B99CAC002B3786 /* ltablib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ltablib.c; path = "../../lib/lua-5.4.6/src/ltablib.c"; sourceTree = ""; }; - BAE8011C27B99CAC002B3786 /* llex.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = llex.c; path = "../../lib/lua-5.4.6/src/llex.c"; sourceTree = ""; }; - BAE8011D27B99CAC002B3786 /* lcode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lcode.c; path = "../../lib/lua-5.4.6/src/lcode.c"; sourceTree = ""; }; - BAE8011E27B99CAC002B3786 /* lua.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lua.c; path = "../../lib/lua-5.4.6/src/lua.c"; sourceTree = ""; }; - BAE8011F27B99CAC002B3786 /* ldo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ldo.c; path = "../../lib/lua-5.4.6/src/ldo.c"; sourceTree = ""; }; - BAE8012027B99CAC002B3786 /* ldump.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ldump.c; path = "../../lib/lua-5.4.6/src/ldump.c"; sourceTree = ""; }; - BAE8012127B99CAC002B3786 /* lparser.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lparser.c; path = "../../lib/lua-5.4.6/src/lparser.c"; sourceTree = ""; }; - BAE8012227B99CAC002B3786 /* lstate.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lstate.c; path = "../../lib/lua-5.4.6/src/lstate.c"; sourceTree = ""; }; - BAE8012327B99CAC002B3786 /* lapi.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lapi.c; path = "../../lib/lua-5.4.6/src/lapi.c"; sourceTree = ""; }; - BAE8012427B99CAC002B3786 /* ldebug.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ldebug.c; path = "../../lib/lua-5.4.6/src/ldebug.c"; sourceTree = ""; }; - BAE8012527B99CAC002B3786 /* lstring.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lstring.c; path = "../../lib/lua-5.4.6/src/lstring.c"; sourceTree = ""; }; - BAE8012627B99CAC002B3786 /* ltm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ltm.c; path = "../../lib/lua-5.4.6/src/ltm.c"; sourceTree = ""; }; - BAE8012727B99CAC002B3786 /* lmem.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lmem.c; path = "../../lib/lua-5.4.6/src/lmem.c"; sourceTree = ""; }; - BAE8012827B99CAC002B3786 /* lvm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lvm.c; path = "../../lib/lua-5.4.6/src/lvm.c"; sourceTree = ""; }; - BAE8012927B99CAC002B3786 /* lfunc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lfunc.c; path = "../../lib/lua-5.4.6/src/lfunc.c"; sourceTree = ""; }; - BAE8012A27B99CAC002B3786 /* lgc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lgc.c; path = "../../lib/lua-5.4.6/src/lgc.c"; sourceTree = ""; }; - BAE8012B27B99CAD002B3786 /* lundump.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lundump.c; path = "../../lib/lua-5.4.6/src/lundump.c"; sourceTree = ""; }; - BAE8012C27B99CAD002B3786 /* lbaselib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lbaselib.c; path = "../../lib/lua-5.4.6/src/lbaselib.c"; sourceTree = ""; }; - BAE8012D27B99CAD002B3786 /* ldblib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ldblib.c; path = "../../lib/lua-5.4.6/src/ldblib.c"; sourceTree = ""; }; - BAE8012E27B99CAD002B3786 /* lauxlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lauxlib.c; path = "../../lib/lua-5.4.6/src/lauxlib.c"; sourceTree = ""; }; - BAE8012F27B99CAD002B3786 /* liolib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = liolib.c; path = "../../lib/lua-5.4.6/src/liolib.c"; sourceTree = ""; }; - BAE8013027B99CAD002B3786 /* lctype.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lctype.c; path = "../../lib/lua-5.4.6/src/lctype.c"; sourceTree = ""; }; - BAE8013127B99CAD002B3786 /* luac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = luac.c; path = "../../lib/lua-5.4.6/src/luac.c"; sourceTree = ""; }; - BAE8013227B99CAD002B3786 /* loadlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = loadlib.c; path = "../../lib/lua-5.4.6/src/loadlib.c"; sourceTree = ""; }; - BAE8013327B99CAD002B3786 /* lmathlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lmathlib.c; path = "../../lib/lua-5.4.6/src/lmathlib.c"; sourceTree = ""; }; - BAE8013427B99CAD002B3786 /* lstrlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lstrlib.c; path = "../../lib/lua-5.4.6/src/lstrlib.c"; sourceTree = ""; }; - BAE8013527B99CAD002B3786 /* lzio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lzio.c; path = "../../lib/lua-5.4.6/src/lzio.c"; sourceTree = ""; }; + BAE8011427B99CAB002B3786 /* lopcodes.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lopcodes.c; path = "../../lib/lua-5.4.8/src/lopcodes.c"; sourceTree = ""; }; + BAE8011527B99CAB002B3786 /* linit.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = linit.c; path = "../../lib/lua-5.4.8/src/linit.c"; sourceTree = ""; }; + BAE8011627B99CAB002B3786 /* lobject.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lobject.c; path = "../../lib/lua-5.4.8/src/lobject.c"; sourceTree = ""; }; + BAE8011727B99CAC002B3786 /* loslib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = loslib.c; path = "../../lib/lua-5.4.8/src/loslib.c"; sourceTree = ""; }; + BAE8011827B99CAC002B3786 /* lcorolib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lcorolib.c; path = "../../lib/lua-5.4.8/src/lcorolib.c"; sourceTree = ""; }; + BAE8011927B99CAC002B3786 /* lutf8lib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lutf8lib.c; path = "../../lib/lua-5.4.8/src/lutf8lib.c"; sourceTree = ""; }; + BAE8011A27B99CAC002B3786 /* ltable.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ltable.c; path = "../../lib/lua-5.4.8/src/ltable.c"; sourceTree = ""; }; + BAE8011B27B99CAC002B3786 /* ltablib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ltablib.c; path = "../../lib/lua-5.4.8/src/ltablib.c"; sourceTree = ""; }; + BAE8011C27B99CAC002B3786 /* llex.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = llex.c; path = "../../lib/lua-5.4.8/src/llex.c"; sourceTree = ""; }; + BAE8011D27B99CAC002B3786 /* lcode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lcode.c; path = "../../lib/lua-5.4.8/src/lcode.c"; sourceTree = ""; }; + BAE8011E27B99CAC002B3786 /* lua.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lua.c; path = "../../lib/lua-5.4.8/src/lua.c"; sourceTree = ""; }; + BAE8011F27B99CAC002B3786 /* ldo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ldo.c; path = "../../lib/lua-5.4.8/src/ldo.c"; sourceTree = ""; }; + BAE8012027B99CAC002B3786 /* ldump.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ldump.c; path = "../../lib/lua-5.4.8/src/ldump.c"; sourceTree = ""; }; + BAE8012127B99CAC002B3786 /* lparser.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lparser.c; path = "../../lib/lua-5.4.8/src/lparser.c"; sourceTree = ""; }; + BAE8012227B99CAC002B3786 /* lstate.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lstate.c; path = "../../lib/lua-5.4.8/src/lstate.c"; sourceTree = ""; }; + BAE8012327B99CAC002B3786 /* lapi.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lapi.c; path = "../../lib/lua-5.4.8/src/lapi.c"; sourceTree = ""; }; + BAE8012427B99CAC002B3786 /* ldebug.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ldebug.c; path = "../../lib/lua-5.4.8/src/ldebug.c"; sourceTree = ""; }; + BAE8012527B99CAC002B3786 /* lstring.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lstring.c; path = "../../lib/lua-5.4.8/src/lstring.c"; sourceTree = ""; }; + BAE8012627B99CAC002B3786 /* ltm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ltm.c; path = "../../lib/lua-5.4.8/src/ltm.c"; sourceTree = ""; }; + BAE8012727B99CAC002B3786 /* lmem.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lmem.c; path = "../../lib/lua-5.4.8/src/lmem.c"; sourceTree = ""; }; + BAE8012827B99CAC002B3786 /* lvm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lvm.c; path = "../../lib/lua-5.4.8/src/lvm.c"; sourceTree = ""; }; + BAE8012927B99CAC002B3786 /* lfunc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lfunc.c; path = "../../lib/lua-5.4.8/src/lfunc.c"; sourceTree = ""; }; + BAE8012A27B99CAC002B3786 /* lgc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lgc.c; path = "../../lib/lua-5.4.8/src/lgc.c"; sourceTree = ""; }; + BAE8012B27B99CAD002B3786 /* lundump.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lundump.c; path = "../../lib/lua-5.4.8/src/lundump.c"; sourceTree = ""; }; + BAE8012C27B99CAD002B3786 /* lbaselib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lbaselib.c; path = "../../lib/lua-5.4.8/src/lbaselib.c"; sourceTree = ""; }; + BAE8012D27B99CAD002B3786 /* ldblib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ldblib.c; path = "../../lib/lua-5.4.8/src/ldblib.c"; sourceTree = ""; }; + BAE8012E27B99CAD002B3786 /* lauxlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lauxlib.c; path = "../../lib/lua-5.4.8/src/lauxlib.c"; sourceTree = ""; }; + BAE8012F27B99CAD002B3786 /* liolib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = liolib.c; path = "../../lib/lua-5.4.8/src/liolib.c"; sourceTree = ""; }; + BAE8013027B99CAD002B3786 /* lctype.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lctype.c; path = "../../lib/lua-5.4.8/src/lctype.c"; sourceTree = ""; }; + BAE8013127B99CAD002B3786 /* luac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = luac.c; path = "../../lib/lua-5.4.8/src/luac.c"; sourceTree = ""; }; + BAE8013227B99CAD002B3786 /* loadlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = loadlib.c; path = "../../lib/lua-5.4.8/src/loadlib.c"; sourceTree = ""; }; + BAE8013327B99CAD002B3786 /* lmathlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lmathlib.c; path = "../../lib/lua-5.4.8/src/lmathlib.c"; sourceTree = ""; }; + BAE8013427B99CAD002B3786 /* lstrlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lstrlib.c; path = "../../lib/lua-5.4.8/src/lstrlib.c"; sourceTree = ""; }; + BAE8013527B99CAD002B3786 /* lzio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lzio.c; path = "../../lib/lua-5.4.8/src/lzio.c"; sourceTree = ""; }; F515A6F32DED074D006E1F63 /* sfctool.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = sfctool.c; path = ../../util/sfctool.c; sourceTree = ""; }; F515A6F52DED0916006E1F63 /* date.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = date.c; path = ../../src/date.c; sourceTree = ""; }; F515A6F62DED0916006E1F63 /* sfbase.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = sfbase.c; path = ../../src/sfbase.c; sourceTree = ""; }; @@ -2189,7 +2189,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = "$(SRCROOT)/../../include"; - LUA_VERSION = 5.4.6; + LUA_VERSION = 5.4.8; MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -2272,7 +2272,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = "$(SRCROOT)/../../include"; - LUA_VERSION = 5.4.6; + LUA_VERSION = 5.4.8; MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; diff --git a/sys/unix/hints/include/cross-pre2.370 b/sys/unix/hints/include/cross-pre2.370 index efa4ca3e0..d0cdf513a 100644 --- a/sys/unix/hints/include/cross-pre2.370 +++ b/sys/unix/hints/include/cross-pre2.370 @@ -7,9 +7,9 @@ ifdef BUILD_TARGET_LUA #===============-================================================= # LUA library -# Source from http://www.lua.org/ftp/lua-5.4.6.tar.gz +# Source from http://www.lua.org/ftp/lua-5.4.8.tar.gz #================================================================= -LUA_VERSION ?=5.4.6 +LUA_VERSION ?=5.4.8 LUATOP ?= ../lib/lua-$(LUA_VERSION) LUASRCDIR ?= $(LUATOP)/src LUAOBJFILES1 = $(TARGETPFX)lapi.o $(TARGETPFX)lauxlib.o \ @@ -119,7 +119,7 @@ ifdef CROSS_TO_MSDOS # 2. Then # make CROSS_TO_MSDOS=1 WANT_WIN_TTY=1 WANT_WIN_CURSES=1 all # -# Source from http://www.lua.org/ftp/lua-5.4.6.tar.gz +# Source from http://www.lua.org/ftp/lua-5.4.8.tar.gz #================================================================= CFLAGS += -DCROSSCOMPILE diff --git a/sys/vms/Makefile_dat.vms b/sys/vms/Makefile_dat.vms index 37dd5e2d3..4059aca4d 100644 --- a/sys/vms/Makefile_dat.vms +++ b/sys/vms/Makefile_dat.vms @@ -25,8 +25,8 @@ WINCHAIN=[-.win.chain] # Lua location relative to src LUAVER=546 -LUADOTVER=5.4.6 -LUAUNDERVER=5_4_6 +LUADOTVER=5.4.8 +LUAUNDERVER=5_4_8 LUAINC=[-.lib.lua.lua$(LUAVER).src] LUALIB=[-.lib.lua]lua$(LUAVER).olb LUASRCDIR =[-.lib.lua.lua$(LUAVER).src] diff --git a/sys/vms/Makefile_doc.vms b/sys/vms/Makefile_doc.vms index f797df0b7..854a303dd 100644 --- a/sys/vms/Makefile_doc.vms +++ b/sys/vms/Makefile_doc.vms @@ -26,8 +26,8 @@ WINCHAIN=[-.win.chain] # Lua location relative to src LUAVER=546 -LUADOTVER=5.4.6 -LUAUNDERVER=5_4_6 +LUADOTVER=5.4.8 +LUAUNDERVER=5_4_8 LUAINC=[-.lib.lua.lua$(LUAVER).src] LUALIB=[-.lib.lua]lua$(LUAVER).olb LUASRCDIR =[-.lib.lua.lua$(LUAVER).src] diff --git a/sys/vms/Makefile_src.vms b/sys/vms/Makefile_src.vms index bf7355722..937200a20 100644 --- a/sys/vms/Makefile_src.vms +++ b/sys/vms/Makefile_src.vms @@ -36,8 +36,8 @@ WINCHAIN=[-.win.chain] # Lua LUAVER=546 -LUADOTVER=5.4.6 -LUAUNDERVER=5_4_6 +LUADOTVER=5.4.8 +LUAUNDERVER=5_4_8 # Lua location relative to src LUAINC=[-.lib.lua.lua$(LUAVER).src] LUALIB=[-.lib.lua]lua$(LUAVER).olb diff --git a/sys/vms/Makefile_top.vms b/sys/vms/Makefile_top.vms index 7a28ff098..94e506fea 100644 --- a/sys/vms/Makefile_top.vms +++ b/sys/vms/Makefile_top.vms @@ -55,8 +55,8 @@ WINCHAIN=[.win.chain] # Lua macros LUAVER=546 -LUADOTVER=5.4.6 -LUAUNDERVER=5_4_6 +LUADOTVER=5.4.8 +LUAUNDERVER=5_4_8 LUAINC=[.lib.lua.lua$(LUAVER).src] LUALIB=[.lib.lua]lua$(LUAVER).olb LUASRCDIR =[.lib.lua.lua$(LUAVER).src] diff --git a/sys/vms/Makefile_utl.vms b/sys/vms/Makefile_utl.vms index 83c112c95..d30d0b629 100644 --- a/sys/vms/Makefile_utl.vms +++ b/sys/vms/Makefile_utl.vms @@ -31,8 +31,8 @@ WINCHAIN=[-.win.chain] # Lua location relative to here LUAVER=546 -LUADOTVER=5.4.6 -LUAUNDERVER=5_4_6 +LUADOTVER=5.4.8 +LUAUNDERVER=5_4_8 LUAINC=[-.lib.lua.lua$(LUAVER).src] LUALIB=[-.lib.lua]lua$(LUAVER).olb LUASRCDIR =[-.lib.lua.lua$(LUAVER).src] diff --git a/sys/vms/vmsbuild.com b/sys/vms/vmsbuild.com index 737fbf015..e34309fce 100755 --- a/sys/vms/vmsbuild.com +++ b/sys/vms/vmsbuild.com @@ -28,9 +28,9 @@ $ ! one, use "" in the earlier one's position, such as $ ! $ @[-.sys.vms]vmsbuild "" "" "" "" "TTY+CURSES" $ ! $ ! Lua Version -$ luaver = "546" -$ luadotver = "5.4.6" -$ luaunderver = "5_4_6" +$ luaver = "548" +$ luadotver = "5.4.8" +$ luaunderver = "5_4_8" $ $ decc_dflt = f$trnlnm("DECC$CC_DEFAULT") $ j = (decc_dflt.nes."") .and. 1 diff --git a/sys/windows/GNUmakefile b/sys/windows/GNUmakefile index 48fc53e03..0341d4aa9 100644 --- a/sys/windows/GNUmakefile +++ b/sys/windows/GNUmakefile @@ -104,7 +104,7 @@ USE_LUADLL = Y WANT_LUAC = N ifndef LUA_VERSION -LUAVER=5.4.6 +LUAVER=5.4.8 else LUAVER=$(LUA_VERSION) endif @@ -393,7 +393,7 @@ CLEAN_DIR = $(GAMEDIR) $(OBJ) #================================================================= # LUA library -# Source from http://www.lua.org/ftp/lua-5.4.6.tar.gz +# Source from http://www.lua.org/ftp/lua-5.4.8.tar.gz #================================================================= OLUA = $(O)lua @@ -1065,8 +1065,8 @@ fetchpdcurses: git submodule update ../submodules/$(PDCURSES) ; fi # git submodule update --remote ../submodules/$(PDCURSES) ; fi else # GIT_AVAILABLE no -CURLLUASRC=http://www.lua.org/ftp/lua-5.4.6.tar.gz -CURLLUADST=lua-5.4.6.tar.gz +CURLLUASRC=http://www.lua.org/ftp/lua-5.4.8.tar.gz +CURLLUADST=lua-5.4.8.tar.gz CURLPDCSRC=https://github.com/Bill-Gray/PDCursesMod/archive/refs/tags/v4.5.1.zip CURLPDCDST=$(PDCURSES).zip diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index a6bae9d28..e5a180663 100644 --- a/sys/windows/Makefile.nmake +++ b/sys/windows/Makefile.nmake @@ -142,7 +142,7 @@ Q=@ SKIP_NETHACKW = N !IFNDEF LUA_VERSION -LUAVER=5.4.6 +LUAVER=5.4.8 !ELSE LUAVER=$(LUA_VERSION) !ENDIF @@ -534,9 +534,9 @@ GITBRANCH = -DNETHACK_GIT_BRANCH=\"$(GIT_BRANCH)\" GITPREFIX = -DNETHACK_GIT_PREFIX=\"$(GIT_PREFIX)\" !ENDIF -#===============-================================================= +#================================================================= # LUA library -# Official source for Lua is http://www.lua.org/ftp/lua-5.4.6.tar.gz +# Official source for Lua is http://www.lua.org/ftp/lua-5.4.8.tar.gz # # Source for the NetHack repository submodule in ../submodules/lua # is https://github.com/lua/lua.git @@ -545,7 +545,7 @@ GITPREFIX = -DNETHACK_GIT_PREFIX=\"$(GIT_PREFIX)\" # Location of LUA # # Original source needs to be obtained from: -# http://www.lua.org/ftp/lua-5.4.6.tar.gz +# http://www.lua.org/ftp/lua-5.4.8.tar.gz # # This build assumes that the LUA sources are located # at the specified location. If they are actually elsewhere @@ -555,7 +555,7 @@ GITPREFIX = -DNETHACK_GIT_PREFIX=\"$(GIT_PREFIX)\" # !IFNDEF LUAVER -LUAVER=5.4.6 +LUAVER=5.4.8 !ENDIF LUALIB = $(LIBDIR)lua$(LUAVER)-$(TARGET_CPU)-static.lib @@ -1603,8 +1603,8 @@ $(PDCURSES_TOP)curses.h: # git submodule update --remote $(SUBM)$(R_PDCDIST) !ENDIF !ELSE # GIT_AVAILABLE no -CURLLUASRC=http://www.lua.org/ftp/lua-5.4.6.tar.gz -CURLLUADST=lua-5.4.6.tar.gz +CURLLUASRC=http://www.lua.org/ftp/lua-5.4.8.tar.gz +CURLLUADST=lua-5.4.8.tar.gz #CURLPDCSRC=https://github.com/wmcbrine/PDCurses/archive/refs/tags/3.9.zip CURLPDCSRC=https://github.com/Bill-Gray/PDCursesMod/archive/refs/tags/v4.4.0.zip diff --git a/sys/windows/build-msys2.txt b/sys/windows/build-msys2.txt index 1258f7be9..06938bfaa 100644 --- a/sys/windows/build-msys2.txt +++ b/sys/windows/build-msys2.txt @@ -30,7 +30,7 @@ Prerequisite Requirements: +----------+ +------+ +----+ +----+ | | | | | | | | | | | | | | | | - lua-5.4.6 pdcursesmod share windows tty win32 lua pdcursesmod + lua-5.4.8 pdcursesmod share windows tty win32 lua pdcursesmod | vs | diff --git a/sys/windows/build-nmake.txt b/sys/windows/build-nmake.txt index a917eb3ad..35c339e7e 100644 --- a/sys/windows/build-nmake.txt +++ b/sys/windows/build-nmake.txt @@ -43,7 +43,7 @@ You can use one of the following build environments: +----------+ +------+ +----+ +----+ | | | | | | | | | | | | | | | | - lua-5.4.6 pdcursesmod share windows tty win32 lua pdcursesmod + lua-5.4.8 pdcursesmod share windows tty win32 lua pdcursesmod | vs | diff --git a/sys/windows/build-vs.txt b/sys/windows/build-vs.txt index 04f69c9e1..bd939a81b 100644 --- a/sys/windows/build-vs.txt +++ b/sys/windows/build-vs.txt @@ -68,7 +68,7 @@ versions: +----------+ +------+ +----+ +----+ | | | | | | | | | | | | | | | | - lua-5.4.6 pdcursesmod share windows tty win32 lua pdcursesmod + lua-5.4.8 pdcursesmod share windows tty win32 lua pdcursesmod | vs | diff --git a/sys/windows/fetch.cmd b/sys/windows/fetch.cmd index c484f9b13..a508ad8dc 100644 --- a/sys/windows/fetch.cmd +++ b/sys/windows/fetch.cmd @@ -2,7 +2,7 @@ if not exist lib\* mkdir lib if [%1] == [lua] ( - set LUA_VERSION=5.4.6 + set LUA_VERSION=5.4.8 set LUASRC=../lib/lua set CURLLUASRC=http://www.lua.org/ftp/lua-%LUA_VERSION%.tar.gz set CURLLUADST=lua-%LUA_VERSION%.tar.gz diff --git a/sys/windows/fetch.sh b/sys/windows/fetch.sh index 4d9b36202..e04dd31d3 100644 --- a/sys/windows/fetch.sh +++ b/sys/windows/fetch.sh @@ -6,12 +6,12 @@ fi if [ $1 == "lua" ]; then if [ -z "$LUA_VERSION" ]; then - export LUA_VERSION=5.4.6 + export LUA_VERSION=5.4.8 export LUASRC=../lib/lua fi - export CURLLUASRC=http://www.lua.org/ftp/lua-5.4.6.tar.gz - export CURLLUADST=lua-5.4.6.tar.gz + export CURLLUASRC=http://www.lua.org/ftp/lua-5.4.8.tar.gz + export CURLLUADST=lua-5.4.8.tar.gz if [ ! -f lib/lua.h ] ;then cd lib diff --git a/sys/windows/vs/FetchPrereq/fetchprereq.nmake b/sys/windows/vs/FetchPrereq/fetchprereq.nmake index 2b826a302..2d4f69913 100644 --- a/sys/windows/vs/FetchPrereq/fetchprereq.nmake +++ b/sys/windows/vs/FetchPrereq/fetchprereq.nmake @@ -9,7 +9,7 @@ NHV=$(NETHACK_VERSION:.=) NHV=$(NHV:"=) # The version of Lua we want -LUA_VERSION=5.4.6 +LUA_VERSION=5.4.8 CURLLUASRC=https://www.lua.org/ftp/lua-$(LUA_VERSION).tar.gz CURLLUADST=lua-$(LUA_VERSION).tar.gz diff --git a/sys/windows/vs/NetHackProperties.props b/sys/windows/vs/NetHackProperties.props index 1bda6a0f0..164cc760e 100644 --- a/sys/windows/vs/NetHackProperties.props +++ b/sys/windows/vs/NetHackProperties.props @@ -8,7 +8,7 @@ $(VERSION_MAJOR).$(VERSION_MINOR).$(PATCHLEVEL) 5 4 - 6> + 8> $(LUA_MAJOR_VERSION).$(LUA_MINOR_VERSION).$(LUA_PATCH_LEVEL) $(ROOTDIR)lib\fmod\api\core\ fmod_vc.lib diff --git a/sys/windows/vs/dirs.props b/sys/windows/vs/dirs.props index 640a5aa41..82d8dc1b1 100644 --- a/sys/windows/vs/dirs.props +++ b/sys/windows/vs/dirs.props @@ -1,7 +1,7 @@ - 5.4.6 + 5.4.8 $(MSBuildProjectDirectory)\..\..\..\..\ $(RootDir)binary\$(Configuration)\$(Platform)\ $(ProjectDir)obj\$(Configuration)\$(Platform)\$(TargetName)\ From 44fcbb180d3cc54f2cfbadc73e6bf578832977de Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 4 Jan 2026 11:16:45 -0500 Subject: [PATCH 175/442] bump COPYRIGHT_BANNER_A --- include/patchlevel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/patchlevel.h b/include/patchlevel.h index 8034d40d4..9dde5579e 100644 --- a/include/patchlevel.h +++ b/include/patchlevel.h @@ -36,7 +36,7 @@ #define DEBUG #endif -#define COPYRIGHT_BANNER_A "NetHack, Copyright 1985-2025" +#define COPYRIGHT_BANNER_A "NetHack, Copyright 1985-2026" #define COPYRIGHT_BANNER_B \ " By Stichting Mathematisch Centrum and M. Stephenson." /* nomakedefs.copyright_banner_c is generated at runtime */ From 4b5b1466740855f0696d7110b6a0412bf976812c Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 4 Jan 2026 11:54:28 -0500 Subject: [PATCH 176/442] should_displace() does not modify mfndposdata struct pointed at by data param --- include/extern.h | 4 ++-- src/monmove.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/extern.h b/include/extern.h index b9f2e5cce..e372f52ff 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1922,8 +1922,8 @@ extern boolean accessible(coordxy, coordxy); extern void set_apparxy(struct monst *) NONNULLARG1; extern boolean can_ooze(struct monst *) NONNULLARG1; extern boolean can_fog(struct monst *) NONNULLARG1; -extern boolean should_displace(struct monst *, struct mfndposdata *, coordxy, - coordxy) NONNULLPTRS; +extern boolean should_displace(struct monst *, const struct mfndposdata *, + coordxy, coordxy) NONNULLPTRS; extern boolean undesirable_disp(struct monst *, coordxy, coordxy) NONNULLARG1; extern boolean can_hide_under_obj(struct obj *); diff --git a/src/monmove.c b/src/monmove.c index 2721b54a0..3830e3713 100644 --- a/src/monmove.c +++ b/src/monmove.c @@ -1069,7 +1069,7 @@ itsstuck(struct monst *mtmp) boolean should_displace( struct monst *mtmp, - struct mfndposdata *data, + const struct mfndposdata *data, coordxy ggx, coordxy ggy) { From b9d781e0b268a49cd0576e5f1cd6813dc177fad0 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Tue, 6 Jan 2026 20:58:13 +0200 Subject: [PATCH 177/442] Fix segfault when carried box explodes Opening a trapped box in inventory, if the box exploded and was not destroyed, then temporary variable otmp was pointing to null. --- src/trap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/trap.c b/src/trap.c index a5602cd5a..41f2cad94 100644 --- a/src/trap.c +++ b/src/trap.c @@ -6400,7 +6400,7 @@ chest_trap( bot(); /* to get immediate botl re-display */ } - otmp->tknown = 1; /* hero knows chest is no longer trapped */ + obj->tknown = 1; /* hero knows chest is no longer trapped */ return FALSE; } From e724711f464c1de591beaa6e69ccf8193c877218 Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 6 Jan 2026 15:41:36 -0500 Subject: [PATCH 178/442] Lua version bump follow-up --- src/nhlua.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nhlua.c b/src/nhlua.c index ebf439af9..439f06d95 100644 --- a/src/nhlua.c +++ b/src/nhlua.c @@ -2219,7 +2219,7 @@ nhl_init(nhl_sandbox_info *sbi) #if LUA_VERSION_NUM >= 505 #define NHL_VERSION_EXPECTED 50500 #else -#define NHL_VERSION_EXPECTED 50406 +#define NHL_VERSION_EXPECTED 50408 #endif #endif From 8d87886458eed541599cd2f5c817827c8681633b Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Wed, 7 Jan 2026 13:07:07 +0200 Subject: [PATCH 179/442] Fix vision when vampire changes form on a door --- src/mon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mon.c b/src/mon.c index 364a289ff..980e4694d 100644 --- a/src/mon.c +++ b/src/mon.c @@ -2951,7 +2951,7 @@ vamprises(struct monst *mtmp) set_msg_xy(0, 0); /* in case none of the messages was delivered */ door->doormask = D_NODOOR; - unblock_point(x, y); + recalc_block_point(x, y); if (trapped) { boolean trap_killed, save_verbose = flags.verbose; From 4a4b0e60f90d4e3fd657e8f1bca92aa674db576c Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 7 Jan 2026 11:21:46 -0500 Subject: [PATCH 180/442] whitespace style in a generated file --- util/sftags.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/util/sftags.c b/util/sftags.c index 39bfe24f9..e13ea94ce 100644 --- a/util/sftags.c +++ b/util/sftags.c @@ -971,7 +971,7 @@ static void output_types(FILE *fp1) for (k = 0; k < SIZE(readtagstypes); ++k) { if (readtagstypes[k].dtclass == NHTYPE_SIMPLE) { - Fprintf(fp1,"\t{NHTYPE_SIMPLE, (char *) \"%s\", sizeof(%s)},\n", + Fprintf(fp1," {NHTYPE_SIMPLE, (char *) \"%s\", sizeof(%s)},\n", readtagstypes[k].dtype, (strncmpi(readtagstypes[k].dtype, "Bitfield", 8) == 0) ? "uint8_t" : @@ -981,10 +981,10 @@ static void output_types(FILE *fp1) "anything" : readtagstypes[k].dtype); /* dtmacro(readtagstypes[k].dtype,0)); */ #if 0 - Fprintf(fp2, "#define %s\t%s%d\n", dtmacro(readtagstypes[k].dtype,1), + Fprintf(fp2, "#define %s %s%d\n", dtmacro(readtagstypes[k].dtype,1), (strlen(readtagstypes[k].dtype) > 12) ? "" : - (strlen(readtagstypes[k].dtype) < 5) ? "\t\t" : - "\t", hcnt++); + (strlen(readtagstypes[k].dtype) < 5) ? " " : + " ", hcnt++); #endif } } @@ -997,7 +997,7 @@ static void output_types(FILE *fp1) } if (cnt > 0) Fprintf(fp1, "%s", ",\n"); - Fprintf(fp1, "\t{NHTYPE_COMPLEX, (char *) \"%s\", sizeof(%s %s)}", + Fprintf(fp1, " {NHTYPE_COMPLEX, (char *) \"%s\", sizeof(%s %s)}", t->tag, (t->tagtype == 's') ? "struct" : "union", t->tag); cnt += 1; @@ -1005,7 +1005,7 @@ static void output_types(FILE *fp1) t = t->next; } Fprintf(fp1, "%s", "\n};\n\n"); - Fprintf(fp1, "int nhdatatypes_size(void)\n{\n\treturn SIZE(nhdatatypes);\n}\n\n"); + Fprintf(fp1, "int nhdatatypes_size(void)\n{\n return SIZE(nhdatatypes);\n}\n\n"); } static void generate_c_files(void) @@ -1357,7 +1357,7 @@ static void generate_c_files(void) "d_%s->%s = bitfield;\n\n", readtagstypes[k].dtype, t->tag); Fprintf(SFDATATMP, - "\t\"%s:%s:%s\",\n", + " \"%s:%s:%s\",\n", sfparent, t->tag, ft); } else { /**************** not a bitfield ****************/ @@ -1571,7 +1571,7 @@ static void generate_c_files(void) strcmp(altbuf, "char") != 0 ? "" : arrbuf); Fprintf(SFI_DATA, "%s", lbuf); Fprintf(SFDATATMP, - "\t\"%s:%s:%s\",\n", + " \"%s:%s:%s\",\n", sfparent, t->tag,fieldfix(ft,ssdef)); kludge_sbrooms = FALSE; array_of_ptrs = FALSE; @@ -1604,7 +1604,7 @@ static void generate_c_files(void) } Fprintf(SFDATATMP,"};\n\n"); - Fprintf(SFDATATMP, "int critical_members_count(void)\n{\n\treturn SIZE(critical_members);\n}\n\n"); + Fprintf(SFDATATMP, "int critical_members_count(void)\n{\n return SIZE(critical_members);\n}\n\n"); fclose(SFO_DATA); fclose(SFI_DATA); @@ -1724,7 +1724,7 @@ dtmacro(const char *str, } else if (strncmpi(c, "const ", 6) == 0) { c = buf + 6; } else if ((strncmpi(c, "struct ", 7) == 0) || - (strncmpi(c, "struct\t", 7) == 0)) { + (strncmpi(c, "struct ", 7) == 0)) { c = buf + 7; } else if (strncmpi(c, "union ", 6) == 0) { c = buf + 6; @@ -1785,7 +1785,7 @@ dtfn(const char *str, } else if (strncmpi(c, "const ", 6) == 0) { c = buf + 6; } else if ((strncmpi(c, "struct ", 7) == 0) || - (strncmpi(c, "struct\t", 7) == 0)) { + (strncmpi(c, "struct ", 7) == 0)) { c = buf + 7; } else if (strncmpi(c, "union ", 6) == 0) { c = buf + 6; From b19db444c53f54b5399dd35b0f040d201fcee13c Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Fri, 9 Jan 2026 16:13:30 +0200 Subject: [PATCH 181/442] No wish resuming for the fuzzer --- src/zap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/zap.c b/src/zap.c index 8849fb75a..779d8b32e 100644 --- a/src/zap.c +++ b/src/zap.c @@ -6208,7 +6208,8 @@ makewish(void) getlin(promptbuf, buf); if (iflags.term_gone) { - svc.context.resume_wish = 1; + if (!iflags.debug_fuzzer) + svc.context.resume_wish = 1; return; } From 99b893c786c7ec9c60c3269361fd46192d65a0e4 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Fri, 9 Jan 2026 19:35:03 +0200 Subject: [PATCH 182/442] Add a new bigroom variant "pillars" Some variants have a bigroom variant called "Mines of Moria"; this one is very much that, but has several different arrangements of the pillars. --- dat/bigrm-13.lua | 71 ++++++++++++++++++++++ dat/dungeon.lua | 2 +- doc/fixes3-7-0.txt | 2 +- sys/unix/NetHack.xcodeproj/project.pbxproj | 1 + sys/windows/GNUmakefile | 2 +- sys/windows/Makefile.nmake | 1 + sys/windows/vs/files.props | 1 + 7 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 dat/bigrm-13.lua diff --git a/dat/bigrm-13.lua b/dat/bigrm-13.lua new file mode 100644 index 000000000..5c9a7d25c --- /dev/null +++ b/dat/bigrm-13.lua @@ -0,0 +1,71 @@ +-- NetHack bigroom bigrm-13.lua $NHDT-Date: 1652196024 2022/05/10 15:20:24 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.0 $ +-- Copyright (c) 2026 by Pasi Kallinen +-- NetHack may be freely redistributed. See license for details. +-- +-- Pillars + +des.level_init({ style = "solidfill", fg = " " }); +des.level_flags("mazelevel", "noflip"); + +des.map([[ +--------------------------------------------------------------------------- +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +--------------------------------------------------------------------------- +]]); + +local pillar = [[ +--- +| | +---]]; + +filters = { + function(x, y) return true; end, + function(x, y) return (x%2 == 1); end, + function(x, y) return (((x+y)%2) == 0); end, + function(x, y) return (y%2 == 1); end, + function(x, y) return (y%2 == 0); end, +}; + +idx = math.random(1, #filters); + +for y = 0,2 do + for x = 0,6 do + if (filters[idx](x, y)) then + des.map({ coord = {12 + x*9, 4 + y*5}, map = pillar, contents=function() end }); + end + end +end + +des.region(selection.area(00,00,75,18), "lit"); +des.wallify(); +des.non_diggable(); + +des.stair("up"); +des.stair("down"); + +for i = 1,15 do + des.object(); +end +for i = 1,6 do + des.trap(); +end +for i = 1,28 do + des.monster(); +end + diff --git a/dat/dungeon.lua b/dat/dungeon.lua index eb6223d66..d90eb9214 100644 --- a/dat/dungeon.lua +++ b/dat/dungeon.lua @@ -70,7 +70,7 @@ dungeon = { base = 10, range = 3, chance = 40, - nlevels = 12 + nlevels = 13 }, { name = "medusa", diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 59666461d..227ad02e7 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -2712,7 +2712,7 @@ menu for what-is command supports /^ and /" to view a list of nearby or whole level visible and remembered traps spiders will occasionally spin webs when moving around drinking a burning potion of oil will cure being turned into slime -new bigroom variants, a boulder maze and hexagons +new bigroom variants, a boulder maze, hexagons, pillars vomiting on an altar provokes the deities wrath branch stairs have a different glyph, show up in yellow color in tty duration of confusion when drinking booze depends upon hunger state diff --git a/sys/unix/NetHack.xcodeproj/project.pbxproj b/sys/unix/NetHack.xcodeproj/project.pbxproj index 99cd6d6ac..3139be9b4 100644 --- a/sys/unix/NetHack.xcodeproj/project.pbxproj +++ b/sys/unix/NetHack.xcodeproj/project.pbxproj @@ -1570,6 +1570,7 @@ "$(NH_DAT_DIR)/bigrm-10.lua", "$(NH_DAT_DIR)/bigrm-11.lua", "$(NH_DAT_DIR)/bigrm-12.lua", + "$(NH_DAT_DIR)/bigrm-13.lua", "$(NH_DAT_DIR)/bigrm-2.lua", "$(NH_DAT_DIR)/bigrm-3.lua", "$(NH_DAT_DIR)/bigrm-4.lua", diff --git a/sys/windows/GNUmakefile b/sys/windows/GNUmakefile index 0341d4aa9..c964d780c 100644 --- a/sys/windows/GNUmakefile +++ b/sys/windows/GNUmakefile @@ -475,7 +475,7 @@ CLEAN_FILE += $(NHLUAH) LUALIST = air Arc-fila Arc-filb Arc-goal Arc-loca Arc-strt \ asmodeus astral baalz Bar-fila Bar-filb Bar-goal \ Bar-loca Bar-strt bigrm-1 bigrm-10 bigrm-11 bigrm-2 \ - bigrm-12 \ + bigrm-12 bigrm-13 \ bigrm-3 bigrm-4 bigrm-5 bigrm-6 bigrm-7 bigrm-8 \ bigrm-9 castle Cav-fila Cav-filb Cav-goal Cav-loca \ Cav-strt dungeon earth fakewiz1 fakewiz2 fire \ diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index e5a180663..e50f9fb21 100644 --- a/sys/windows/Makefile.nmake +++ b/sys/windows/Makefile.nmake @@ -711,6 +711,7 @@ HACKINCL = \ LUA_FILES = $(DAT)asmodeus.lua $(DAT)baalz.lua $(DAT)bigrm-1.lua \ $(DAT)bigrm-10.lua $(DAT)bigrm-11.lua $(DAT)bigrm-12.lua \ + $(DAT)bigrm-13.lua \ $(DAT)bigrm-2.lua $(DAT)bigrm-3.lua $(DAT)bigrm-4.lua \ $(DAT)bigrm-5.lua $(DAT)bigrm-6.lua $(DAT)bigrm-7.lua \ $(DAT)bigrm-8.lua $(DAT)bigrm-9.lua $(DAT)castle.lua \ diff --git a/sys/windows/vs/files.props b/sys/windows/vs/files.props index 16a62721e..07f3c7421 100644 --- a/sys/windows/vs/files.props +++ b/sys/windows/vs/files.props @@ -33,6 +33,7 @@ + From e504d44de2e5e9086100aae59efe024bec66ab72 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sat, 10 Jan 2026 11:19:44 +0200 Subject: [PATCH 183/442] Add "snake wall" to bigroom variant 1 Also increase the chances of having walls in bigrm-1 from 75% to 80% --- dat/bigrm-1.lua | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/dat/bigrm-1.lua b/dat/bigrm-1.lua index a4003aa4b..2c734a5ac 100644 --- a/dat/bigrm-1.lua +++ b/dat/bigrm-1.lua @@ -28,10 +28,10 @@ des.map([[ ]]); -if percent(75) then +if percent(80) then local terrains = { "-", "F", "L", "T", "C" }; local tidx = math.random(1, #terrains); - local choice = math.random(0, 4); + local choice = math.random(0, 5); if choice == 0 then -- one horizontal line des.terrain(selection.line(10,8, 65,8), terrains[tidx]); @@ -48,6 +48,14 @@ if percent(75) then des.terrain(selection.rect(4,4, 70,13), terrains[tidx]); local sel = selection.line(25,4, 50,4) | selection.line(25,13, 50,13); des.terrain(sel, '.'); + elseif choice == 4 then + -- snake + des.terrain(selection.fillrect(5,5, 69, 12), terrains[tidx]); + for i = 0, 7 do + local x = 6 + i*8; + local y = 5 + (i%2); + des.terrain(selection.fillrect(x, y, x+6, y+6), '.'); + end else -- nothing end From e93f0dedbf8b52e23254299fda149bd54bb22d30 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sat, 10 Jan 2026 14:05:03 +0200 Subject: [PATCH 184/442] Make lyncanthropy less crippling Now, the were-creature instincts will keep the hero from unintentionally changing shape while next to hostile creatures. --- doc/fixes3-7-0.txt | 1 + src/were.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 227ad02e7..08436ca6e 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1560,6 +1560,7 @@ monster elves shooting arrows weren't getting intended small to-hit and damage safe armor enchantment limit is lowered by one for magical armor luck has a reduced effect on to-hit chance monsters use wand of teleportation to move hero away from item pile +lycanthrope instincts keep hero from changing shape while next to hostiles Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/were.c b/src/were.c index aa65341cb..59197177f 100644 --- a/src/were.c +++ b/src/were.c @@ -202,6 +202,8 @@ you_were(void) an(mons[u.ulycn].pmnames[NEUTRAL] + 4)); if (!paranoid_query(ParanoidWerechange, qbuf)) return; + } else if (monster_nearby()) { + return; } gw.were_changes++; (void) polymon(u.ulycn); @@ -217,6 +219,7 @@ you_unwere(boolean purify) set_ulycn(NON_PM); /* cure lycanthropy */ } if (!Unchanging && is_were(gy.youmonst.data) + && !monster_nearby() && (!controllable_poly || !paranoid_query(ParanoidWerechange, "Remain in beast form?"))) rehumanize(); From 3edd07b23efcab685789d2a6079c70de84443db7 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Sat, 10 Jan 2026 15:16:02 +0000 Subject: [PATCH 185/442] Changes to the scroll of enchant armor After discussing with paxed, I decided that instead of reducing the safe enchantment limit for magical armor, it is instead better to make the scrolls less effective on it. So this commit restores the previous rules for the safely-enchant-from level and changes the effectiveness of the scrolls to compensate. Non-elven magical armor now only gets +1 from blessed scrolls of enchant armor when enchanted from +3, coming to a total of +4. But scrolls of enchant armor are now more effective on nonmagical and elven and previously unenchanted armor, giving more of an incentive to use them in the early game. Cursed and uncursed scrolls of enchant armor are now also more powerful than they were (but less powerful than blessed scrolls), hopefully making it a more interesting decision as to whether to use scrolls of enchant armor even if you don't have the means to bless them. --- doc/fixes3-7-0.txt | 7 +++-- src/read.c | 73 +++++++++++++++++++++------------------------- 2 files changed, 38 insertions(+), 42 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 08436ca6e..d2d6b8195 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1557,10 +1557,12 @@ praying will not restore monster-form HP while polymorphed, unless you winter wolf cub was missing for monster to lycanthrope conversion monster elves shooting arrows weren't getting intended small to-hit and damage bonuses -safe armor enchantment limit is lowered by one for magical armor luck has a reduced effect on to-hit chance monsters use wand of teleportation to move hero away from item pile lycanthrope instincts keep hero from changing shape while next to hostiles +scroll of enchant armor formula has changed (in particular, magical armor now + gains less enchantment when enchanted than nonmagical armor and + uncursed scrolls can sometimes enchant by more than one point) Fixes to 3.7.0-x General Problems Exposed Via git Repository @@ -2927,7 +2929,7 @@ Unix: support --nethackrc=filename on the command line; same effect as NETHACKOPTIONS='@filename' but leaves NETHACKOPTIONS available for specifying options; --no-nethackrc is same as --nethackrc=/dev/null Windows: implement MSGHANDLER (pr #749 by argrath) -Windows: Add configuration to support Curses on WinGUI (pr #1028 by chasonr) +Windows: Add configuration to support Curses on WinGUI (pr #1028 by chasonr) X11: implement 'selectsaved', restore via menu of saved games X11: echo getline prompt and response (wishes, applying names) to message window and dumplog message history @@ -3200,4 +3202,3 @@ relocate general-purpose function choose_classes_menu(), from options.c to windows.c remove register from variable declarations make glyph_to_cmap() a function instead of a macro - diff --git a/src/read.c b/src/read.c index e35d3abe7..8b8f6bc79 100644 --- a/src/read.c +++ b/src/read.c @@ -21,8 +21,6 @@ staticfn void forget(int); staticfn int maybe_tame(struct monst *, struct obj *); staticfn boolean can_center_cloud(coordxy, coordxy); staticfn void display_stinking_cloud_positions(boolean); -staticfn boolean is_special_armor_enchant(struct obj *); -staticfn int armor_enchant_limit(struct obj *); staticfn void seffect_enchant_armor(struct obj **); staticfn void seffect_destroy_armor(struct obj **); staticfn void seffect_confuse_monster(struct obj **); @@ -1112,37 +1110,12 @@ display_stinking_cloud_positions(boolean on_off) } } -/* some armor vibrates warningly when enchanted beyond a limit, - or can be enchanted higher than usual */ -staticfn boolean -is_special_armor_enchant(struct obj *otmp) -{ - return is_elven_armor(otmp) - || (Role_if(PM_WIZARD) && otmp->otyp == CORNUTHAUM); -} - -/* return the safe enchantment limit for an armor */ -staticfn int -armor_enchant_limit(struct obj *otmp) -{ - int limit = 3; /* default safe armor enchantment limit */ - - /* some armor can take a higher enchantment */ - if (is_special_armor_enchant(otmp)) - limit += 2; - /* object's internal magic interferes */ - if (objects[otmp->otyp].oc_magic) - limit--; - - return limit; -} - staticfn void seffect_enchant_armor(struct obj **sobjp) { struct obj *sobj = *sobjp; schar s; - int safe_spe_limit; + boolean special_armor; boolean same_color; struct obj *otmp = some_armor(&gy.youmonst); boolean sblessed = sobj->blessed; @@ -1187,6 +1160,9 @@ seffect_enchant_armor(struct obj **sobjp) otmp->oerodeproof = new_erodeproof ? 1 : 0; return; } + /* elven armor vibrates warningly when enchanted beyond a limit */ + special_armor = is_elven_armor(otmp) + || (Role_if(PM_WIZARD) && otmp->otyp == CORNUTHAUM); if (scursed) same_color = (otmp->otyp == BLACK_DRAGON_SCALE_MAIL || otmp->otyp == BLACK_DRAGON_SCALES); @@ -1197,11 +1173,9 @@ seffect_enchant_armor(struct obj **sobjp) if (Blind) same_color = FALSE; - safe_spe_limit = armor_enchant_limit(otmp); - /* KMH -- catch underflow */ s = scursed ? -otmp->spe : otmp->spe; - if (s > safe_spe_limit && rn2(s)) { + if (s > (special_armor ? 5 : 3) && rn2(s)) { otmp->in_use = TRUE; pline("%s violently %s%s%s for a while, then %s.", Yname2(otmp), otense(otmp, Blind ? "vibrate" : "glow"), @@ -1213,12 +1187,33 @@ seffect_enchant_armor(struct obj **sobjp) useup(otmp); return; } - s = scursed ? -1 - : (otmp->spe >= 9) - ? (rn2(otmp->spe) == 0) - : sblessed - ? rnd(3 - otmp->spe / 3) - : 1; + if (s < -100) s = -100; /* avoid integer overflow with very negative armor */ + + /* Base power of the enchantment: + + 2 for -1 to +0 armor; + 1 for +1 to +2 armor; + 0 for +3 to +4 armor, etc. + + When disenchanting, everything is done with reversed signs. */ + s = (4 - s) / 2; + + /* Elven/artifact and nonmagical armor is easier to enchant; + blessed scrolls are more effective. */ + if (special_armor) ++s; + if (!objects[otmp->otyp].oc_magic) ++s; + if (sblessed) ++s; + + if (s <= 0) { + s = 0; + if (otmp->spe > 0 && !rn2(otmp->spe)) s = 1; + } else { + s = rnd(s); + } + if (s > 11) s = 11; /* unlikely but possible: avoids an overflow later */ + + if (scursed) s = -s; + if (s >= 0 && Is_dragon_scales(otmp)) { unsigned was_lit = otmp->lamplit; int old_light = artifact_light(otmp) ? arti_light_radius(otmp) : 0; @@ -1280,8 +1275,8 @@ seffect_enchant_armor(struct obj **sobjp) alter_cost(otmp, 0L); } - if ((otmp->spe > safe_spe_limit) - && (is_special_armor_enchant(otmp) || !rn2(7))) + if ((otmp->spe > (special_armor ? 5 : 3)) + && (special_armor || !rn2(7))) pline("%s %s.", Yobjnam2(otmp, "suddenly vibrate"), Blind ? "again" : "unexpectedly"); } From 40b39c10260601863223a03b1fb585a5b4ed5db9 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sat, 10 Jan 2026 17:47:34 +0200 Subject: [PATCH 186/442] Bigroom 13: Add few more pillar arrangements --- dat/bigrm-13.lua | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/dat/bigrm-13.lua b/dat/bigrm-13.lua index 5c9a7d25c..ff834a666 100644 --- a/dat/bigrm-13.lua +++ b/dat/bigrm-13.lua @@ -35,11 +35,22 @@ local pillar = [[ ---]]; filters = { + -- 1: all pillars function(x, y) return true; end, + -- 2: 3 vertical lines function(x, y) return (x%2 == 1); end, + -- 3: checkerboard function(x, y) return (((x+y)%2) == 0); end, + -- 4: center row function(x, y) return (y%2 == 1); end, + -- 5: top and bottom rows function(x, y) return (y%2 == 0); end, + -- 6: random 50% + function(x, y) return (math.random(0,1) == 0); end, + -- 7: corners and center + function(x, y) return ((x/3)%2 == y%2); end, + -- 8: slanted + function(x, y) return ((x+1)//3 == y); end, }; idx = math.random(1, #filters); From f2a958e7e8e6acfdbf397057a5a6144b445a39fd Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Sat, 10 Jan 2026 18:56:56 +0000 Subject: [PATCH 187/442] Make dragonhide affectable by rotting attacks Dragon corpses are capable of rotting, so it's plausible that the skin of a dead dragon might also be possible to rot. This is unlikely to come up in actual play at present (unless wishing for rotten DSM, it can only end up rotting as a consequence of brown pudding attacks): the primary motivation is to open up new possibilities for armor-damaging attacks, which in current NetHack aren't very relevant in the late game because everyone is wearing dragon scale mail. --- doc/fixes3-7-0.txt | 1 + src/mkobj.c | 5 +++-- src/objnam.c | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index d2d6b8195..db980003c 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1563,6 +1563,7 @@ lycanthrope instincts keep hero from changing shape while next to hostiles scroll of enchant armor formula has changed (in particular, magical armor now gains less enchantment when enchanted than nonmagical armor and uncursed scrolls can sometimes enchant by more than one point) +dragonhide can rot Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/mkobj.c b/src/mkobj.c index 31fe3c89a..f2adf2185 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -2286,8 +2286,9 @@ is_rottable(struct obj *otmp) { int otyp = otmp->otyp; - return (boolean) (objects[otyp].oc_material <= WOOD - && objects[otyp].oc_material != LIQUID); + return (boolean) ((objects[otyp].oc_material <= WOOD + && objects[otyp].oc_material != LIQUID) + || objects[otyp].oc_material == DRAGON_HIDE); } /* diff --git a/src/objnam.c b/src/objnam.c index ee9a29836..762b9c25d 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -1186,7 +1186,8 @@ add_erosion_words(struct obj *obj, char *prefix) : is_corrodeable(obj) ? "corrodeproof " : is_flammable(obj) ? "fireproof " : is_crackable(obj) ? "tempered " /* hardened */ - : ""); + : is_rottable(obj) ? "rotproof " + : ""); } /* used to prevent rust on items where rust makes no difference */ From 09b879d7ade2895bb8cae0165b66f2d393019cf5 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Sat, 10 Jan 2026 20:00:59 +0000 Subject: [PATCH 188/442] Give a message when throwing ammo without a launcher This is primarily intended to help new players understand the mechanics (the "wield the launcher, throw the arrow" sequence may be unfamiliar to newer players), so the message is worded to indicate the correct way to use the ammo. --- doc/fixes3-7-0.txt | 1 + src/dothrow.c | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index db980003c..a85c925f7 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1564,6 +1564,7 @@ scroll of enchant armor formula has changed (in particular, magical armor now gains less enchantment when enchanted than nonmagical armor and uncursed scrolls can sometimes enchant by more than one point) dragonhide can rot +throwing ammo without a launcher produces a message Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/dothrow.c b/src/dothrow.c index e7bfd2789..35b52aa6c 100644 --- a/src/dothrow.c +++ b/src/dothrow.c @@ -1607,8 +1607,13 @@ throwit( range = BOLT_LIM; else range++; - } else if (obj->oclass != GEM_CLASS) + } else if (obj->oclass != GEM_CLASS) { range /= 2; + pline("You aren't wielding %s, so you throw your %s by %s.", + an(skill_name(weapon_type(obj))), + weapon_descr(obj), + body_part(HAND)); + } } if (Is_airlevel(&u.uz) || Levitation) { From 96394394b81967b0464133c11c9ba55258b70973 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 11 Jan 2026 11:42:16 +0200 Subject: [PATCH 189/442] Fix comment typo --- include/rm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/rm.h b/include/rm.h index e22f48054..8ff3c3cea 100644 --- a/include/rm.h +++ b/include/rm.h @@ -476,7 +476,7 @@ typedef struct { #define fmon svl.level.monlist /* - * Covert a trap number into the defsym graphics array. + * Convert a trap number into the defsym graphics array. * Convert a defsym number into a trap number. * Assumes that arrow trap will always be the first trap. */ From ac7f0d3615695b59dd6e571202887083845001b5 Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 11 Jan 2026 01:49:06 -0800 Subject: [PATCH 190/442] context sensitive item action for candles When picking candles from an inventory display, expand the 'a' choice if carrying the Candelabrum of Invocation. --- src/invent.c | 13 ++++++++++--- src/pager.c | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/invent.c b/src/invent.c index a7cc92bfc..523250197 100644 --- a/src/invent.c +++ b/src/invent.c @@ -3305,9 +3305,16 @@ itemactions(struct obj *otmp) Sprintf(buf, "%s the candelabrum", light); ia_addmenu(win, IA_APPLY_OBJ, 'a', buf); } else if (otmp->otyp == WAX_CANDLE || otmp->otyp == TALLOW_CANDLE) { - Sprintf(buf, "%s %s %s", light, - is_plural(otmp) ? "these" : "this", - simpleonames(otmp)); + boolean multiple = (otmp->quan == 1L) ? FALSE : TRUE; + const char *s = multiple ? "these" : "this"; + struct obj *o = carrying(CANDELABRUM_OF_INVOCATION); + + if (o && o->spe < 7) + Sprintf(buf, "Attach %s to your candelabrum, or %s %s", s, + !otmp->lamplit ? "light" : "extinguish", /* [lowercase] */ + multiple ? "them" : "it"); + else + Sprintf(buf, "%s %s %s", light, s, simpleonames(otmp)); ia_addmenu(win, IA_APPLY_OBJ, 'a', buf); } else if (otmp->otyp == OIL_LAMP || otmp->otyp == MAGIC_LAMP || otmp->otyp == BRASS_LANTERN) { diff --git a/src/pager.c b/src/pager.c index c5a122401..ca5ed2810 100644 --- a/src/pager.c +++ b/src/pager.c @@ -1721,7 +1721,7 @@ do_look(int mode, coord *click_cc) * introduced. * * When lootabc is set, abandon the 'y'|'n' compatibility in - * favor of newer '/' and '?' compatobility instead. + * favor of newer '/' and '?' compatibility instead. */ any.a_char = '/'; From d0b9846367baf8ffe4bc24ec889ff61f1defd094 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 11 Jan 2026 14:43:43 +0200 Subject: [PATCH 191/442] Move item actions into separate src file Haven't tested compilation on Windows and VMS ... --- include/extern.h | 4 + include/hack.h | 5 + src/iactions.c | 716 +++++++++++++++++++++++ src/invent.c | 712 ---------------------- sys/msdos/Makefile.GCC | 3 +- sys/unix/Makefile.src | 4 +- sys/vms/Makefile.src | 5 +- sys/vms/Makefile_src.vms | 3 +- sys/vms/vmsbuild.com | 2 +- sys/windows/GNUmakefile | 2 +- sys/windows/Makefile.nmake | 5 + sys/windows/vs/NetHack/NetHack.vcxproj | 1 + sys/windows/vs/NetHackW/NetHackW.vcxproj | 1 + 13 files changed, 744 insertions(+), 719 deletions(-) create mode 100644 src/iactions.c diff --git a/include/extern.h b/include/extern.h index e372f52ff..40de54078 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1240,6 +1240,10 @@ extern boolean pmatchi(const char *, const char *) NONNULLPTRS; extern boolean pmatchz(const char *, const char *) NONNULLPTRS; */ +/* ### iactions.c ### */ + +extern int itemactions(struct obj *otmp) NONNULLARG1; + /* ### insight.c ### */ extern int doattributes(void); diff --git a/include/hack.h b/include/hack.h index e91df8365..7163bf6a4 100644 --- a/include/hack.h +++ b/include/hack.h @@ -571,6 +571,11 @@ enum hunger_state_types { STARVED = 6 }; +/* fake inventory letters, not 'a'..'z' or 'A'..'Z' */ +#define NOINVSYM '#' /* overflow because all 52 letters are in use */ +#define CONTAINED_SYM '>' /* designator for inside a container */ +#define HANDS_SYM '-' /* hands|fingers|self depending on context */ + /* inventory counts (slots in tty parlance) * a...zA..Z invlet_basic (52) * $a...zA..Z# 2 special additions diff --git a/src/iactions.c b/src/iactions.c new file mode 100644 index 000000000..ca00a2be6 --- /dev/null +++ b/src/iactions.c @@ -0,0 +1,716 @@ +/* NetHack 3.7 iactions.c $NHDT-Date: 1762680996 2025/11/09 01:36:36 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.543 $ */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/*-Copyright (c) Pasi Kallinen, 2026. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +staticfn boolean item_naming_classification(struct obj *, char *, char *); +staticfn int item_reading_classification(struct obj *, char *); +staticfn void ia_addmenu(winid, int, char, const char *); +staticfn void itemactions_pushkeys(struct obj *, int); + +enum item_action_actions { + IA_NONE = 0, + IA_UNWIELD, /* hack for 'w-' */ + IA_APPLY_OBJ, /* 'a' */ + IA_DIP_OBJ, /* 'a' on a potion == dip */ + IA_NAME_OBJ, /* 'c' name individual item */ + IA_NAME_OTYP, /* 'C' name item's type */ + IA_DROP_OBJ, /* 'd' */ + IA_EAT_OBJ, /* 'e' */ + IA_ENGRAVE_OBJ, /* 'E' */ + IA_FIRE_OBJ, /* 'f' */ + IA_ADJUST_OBJ, /* 'i' #adjust inventory letter */ + IA_ADJUST_STACK, /* 'I' #adjust with count to split stack */ + IA_SACRIFICE, /* 'O' offer sacrifice */ + IA_BUY_OBJ, /* 'p' pay shopkeeper */ + IA_QUAFF_OBJ, + IA_QUIVER_OBJ, + IA_READ_OBJ, + IA_RUB_OBJ, + IA_THROW_OBJ, + IA_TAKEOFF_OBJ, + IA_TIP_CONTAINER, + IA_INVOKE_OBJ, + IA_WIELD_OBJ, + IA_WEAR_OBJ, + IA_SWAPWEAPON, + IA_TWOWEAPON, + IA_ZAP_OBJ, + IA_WHATIS_OBJ, /* '/' specify inventory object */ +}; + +/* construct text for the menu entries for IA_NAME_OBJ and IA_NAME_OTYP */ +staticfn boolean +item_naming_classification( + struct obj *obj, + char *onamebuf, + char *ocallbuf) +{ + static const char + Name[] = "Name", + Rename[] = "Rename or un-name", + Call[] = "Call", + /* "re-call" seems a bit weird, but "recall" and + "rename" don't fit for changing a type name */ + Recall[] = "Re-call or un-call"; + + onamebuf[0] = ocallbuf[0] = '\0'; + if (name_ok(obj) == GETOBJ_SUGGEST) { + Sprintf(onamebuf, "%s %s %s", + (!has_oname(obj) || !*ONAME(obj)) ? Name : Rename, + the_unique_obj(obj) ? "the" + : !is_plural(obj) ? "this specific" + : "this stack of", /*"these",*/ + simpleonames(obj)); + } + if (call_ok(obj) == GETOBJ_SUGGEST) { + char *callname = simpleonames(obj); + + /* prefix known unique item with "the", make all other types plural */ + if (the_unique_obj(obj)) /* treats unID'd fake amulets as if real */ + callname = the(callname); + else if (!is_plural(obj)) + callname = makeplural(callname); + Sprintf(ocallbuf, "%s the type for %s", + (!objects[obj->otyp].oc_uname + || !*objects[obj->otyp].oc_uname) ? Call : Recall, + callname); + } + return (*onamebuf || *ocallbuf) ? TRUE : FALSE; +} + +/* construct text for the menu entries for IA_READ_OBJ */ +staticfn int +item_reading_classification(struct obj *obj, char *outbuf) +{ + int otyp = obj->otyp, res = IA_READ_OBJ; + + *outbuf = '\0'; + if (otyp == FORTUNE_COOKIE) { + Strcpy(outbuf, "Read the message inside this cookie"); + } else if (otyp == T_SHIRT) { + Strcpy(outbuf, "Read the slogan on the shirt"); + } else if (otyp == ALCHEMY_SMOCK) { + Strcpy(outbuf, "Read the slogan on the apron"); + } else if (otyp == HAWAIIAN_SHIRT) { + Strcpy(outbuf, "Look at the pattern on the shirt"); + } else if (obj->oclass == SCROLL_CLASS) { + const char *magic = ((obj->dknown +#ifdef MAIL_STRUCTURES + && otyp != SCR_MAIL +#endif + && (otyp != SCR_BLANK_PAPER + || !objects[otyp].oc_name_known)) + ? " to activate its magic" : ""); + + Sprintf(outbuf, "Read this scroll%s", magic); + } else if (obj->oclass == SPBOOK_CLASS) { + boolean novel = (otyp == SPE_NOVEL), + blank = (otyp == SPE_BLANK_PAPER + && objects[otyp].oc_name_known), + tome = (otyp == SPE_BOOK_OF_THE_DEAD + && objects[otyp].oc_name_known); + + Sprintf(outbuf, "%s this %s", + (novel || blank) ? "Read" : tome ? "Examine" : "Study", + novel ? simpleonames(obj) /* "novel" or "paperback book" */ + : tome ? "tome" : "spellbook"); + } else { + res = IA_NONE; + } + return res; +} + +staticfn void +ia_addmenu(winid win, int act, char let, const char *txt) +{ + anything any; + int clr = NO_COLOR; + + any = cg.zeroany; + any.a_int = act; + add_menu(win, &nul_glyphinfo, &any, let, 0, + ATR_NONE, clr, txt, MENU_ITEMFLAGS_NONE); +} + +/* set up a command to execute on a specific item next */ +staticfn void +itemactions_pushkeys(struct obj *otmp, int act) +{ + switch (act) { + default: + impossible("Unknown item action %d", act); + break; + case IA_NONE: + break; + case IA_UNWIELD: + cmdq_add_ec(CQ_CANNED, (otmp == uwep) ? dowield + : (otmp == uswapwep) ? remarm_swapwep + : (otmp == uquiver) ? dowieldquiver + : donull); /* can't happen */ + cmdq_add_key(CQ_CANNED, HANDS_SYM); + break; + case IA_APPLY_OBJ: + cmdq_add_ec(CQ_CANNED, doapply); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_DIP_OBJ: + /* #altdip instead of normal #dip - takes potion to dip into + first (the inventory item instigating this) and item to + be dipped second, also ignores floor features such as + fountain/sink so we don't need to force m-prefix here */ + cmdq_add_ec(CQ_CANNED, dip_into); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_NAME_OBJ: + case IA_NAME_OTYP: + cmdq_add_ec(CQ_CANNED, docallcmd); + cmdq_add_key(CQ_CANNED, (act == IA_NAME_OBJ) ? 'i' : 'o'); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_DROP_OBJ: + cmdq_add_ec(CQ_CANNED, dodrop); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_EAT_OBJ: + /* start with m-prefix; for #eat, it means ignore floor food + if present and eat food from invent */ + cmdq_add_ec(CQ_CANNED, do_reqmenu); + cmdq_add_ec(CQ_CANNED, doeat); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_ENGRAVE_OBJ: + cmdq_add_ec(CQ_CANNED, doengrave); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_FIRE_OBJ: + cmdq_add_ec(CQ_CANNED, dofire); + break; + case IA_ADJUST_OBJ: + cmdq_add_ec(CQ_CANNED, doorganize); /* #adjust */ + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_ADJUST_STACK: + cmdq_add_ec(CQ_CANNED, adjust_split); /* #altadjust */ + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_SACRIFICE: + cmdq_add_ec(CQ_CANNED, dosacrifice); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_BUY_OBJ: + cmdq_add_ec(CQ_CANNED, dopay); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_QUAFF_OBJ: + /* start with m-prefix; for #quaff, it means ignore fountain + or sink if present and drink a potion from invent */ + cmdq_add_ec(CQ_CANNED, do_reqmenu); + cmdq_add_ec(CQ_CANNED, dodrink); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_QUIVER_OBJ: + cmdq_add_ec(CQ_CANNED, dowieldquiver); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_READ_OBJ: + cmdq_add_ec(CQ_CANNED, doread); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_RUB_OBJ: + cmdq_add_ec(CQ_CANNED, dorub); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_THROW_OBJ: + cmdq_add_ec(CQ_CANNED, dothrow); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_TAKEOFF_OBJ: + cmdq_add_ec(CQ_CANNED, ia_dotakeoff); /* #altdotakeoff */ + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_TIP_CONTAINER: + /* start with m-prefix to skip floor containers; + for menustyle:Traditional when more than one floor container + is present, player will get a #tip menu and have to pick + the "tip something being carried" choice, then this item + will be already chosen from inventory; suboptimal but + possibly an acceptable tradeoff since combining item actions + with use of traditional ggetobj() is an unlikely scenario */ + cmdq_add_ec(CQ_CANNED, do_reqmenu); + cmdq_add_ec(CQ_CANNED, dotip); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_INVOKE_OBJ: + cmdq_add_ec(CQ_CANNED, doinvoke); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_WIELD_OBJ: + cmdq_add_ec(CQ_CANNED, dowield); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_WEAR_OBJ: + cmdq_add_ec(CQ_CANNED, dowear); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_SWAPWEAPON: + cmdq_add_ec(CQ_CANNED, doswapweapon); + break; + case IA_TWOWEAPON: + cmdq_add_ec(CQ_CANNED, dotwoweapon); + break; + case IA_ZAP_OBJ: + cmdq_add_ec(CQ_CANNED, dozap); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_WHATIS_OBJ: + cmdq_add_ec(CQ_CANNED, dowhatis); /* "/" command */ + cmdq_add_key(CQ_CANNED, 'i'); /* "i" == item from inventory */ + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + } +} + +/* Show menu of possible actions hero could do with item otmp */ +int +itemactions(struct obj *otmp) +{ + int n, act = IA_NONE; + winid win; + char buf[BUFSZ], buf2[BUFSZ]; + menu_item *selected; + struct monst *mtmp; + const char *light = otmp->lamplit ? "Extinguish" : "Light"; + boolean already_worn = (otmp->owornmask & (W_ARMOR | W_ACCESSORY)) != 0; + + win = create_nhwindow(NHW_MENU); + start_menu(win, MENU_BEHAVE_STANDARD); + + /* -: unwield; picking current weapon offers an opportunity for 'w-' + to wield bare/gloved hands; likewise for 'Q-' with quivered item(s) */ + if (otmp == uwep || otmp == uswapwep || otmp == uquiver) { + const char *verb = (otmp == uquiver) ? "Quiver" : "Wield", + *action = (otmp == uquiver) ? "un-ready" : "un-wield", + *which = is_plural(otmp) ? "these" : "this", + *what = ((otmp->oclass == WEAPON_CLASS || is_weptool(otmp)) + ? "weapon" : "item"); + /* + * TODO: if uwep is ammo, tell player that to shoot instead of toss, + * the corresponding launcher must be wielded; + */ + Sprintf(buf, "%s '%c' to %s %s %s", + verb, HANDS_SYM, action, which, + is_plural(otmp) ? makeplural(what) : what); + ia_addmenu(win, IA_UNWIELD, '-', buf); + } + + /* a: apply */ + if (otmp->oclass == COIN_CLASS) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Flip a coin"); + else if (otmp->otyp == CREAM_PIE) + ia_addmenu(win, IA_APPLY_OBJ, 'a', + "Hit yourself with this cream pie"); + else if (otmp->otyp == BULLWHIP) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Lash out with this whip"); + else if (otmp->otyp == GRAPPLING_HOOK) + ia_addmenu(win, IA_APPLY_OBJ, 'a', + "Grapple something with this hook"); + else if (otmp->otyp == BAG_OF_TRICKS && objects[otmp->otyp].oc_name_known) + /* bag of tricks skips this unless discovered */ + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Reach into this bag"); + else if (Is_container(otmp)) + /* bag of tricks gets here only if not yet discovered */ + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Open this container"); + else if (otmp->otyp == CAN_OF_GREASE) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Use the can to grease an item"); + else if (otmp->otyp == LOCK_PICK + || otmp->otyp == CREDIT_CARD + || otmp->otyp == SKELETON_KEY) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Use this tool to pick a lock"); + else if (otmp->otyp == TINNING_KIT) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Use this kit to tin a corpse"); + else if (otmp->otyp == LEASH) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Tie a pet to this leash"); + else if (otmp->otyp == SADDLE) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Place this saddle on a pet"); + else if (otmp->otyp == MAGIC_WHISTLE + || otmp->otyp == TIN_WHISTLE) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Blow this whistle"); + else if (otmp->otyp == EUCALYPTUS_LEAF) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Use this leaf as a whistle"); + else if (otmp->otyp == STETHOSCOPE) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Listen through the stethoscope"); + else if (otmp->otyp == MIRROR) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Show something its reflection"); + else if (otmp->otyp == BELL || otmp->otyp == BELL_OF_OPENING) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Ring the bell"); + else if (otmp->otyp == CANDELABRUM_OF_INVOCATION) { + Sprintf(buf, "%s the candelabrum", light); + ia_addmenu(win, IA_APPLY_OBJ, 'a', buf); + } else if (otmp->otyp == WAX_CANDLE || otmp->otyp == TALLOW_CANDLE) { + boolean multiple = (otmp->quan == 1L) ? FALSE : TRUE; + const char *s = multiple ? "these" : "this"; + struct obj *o = carrying(CANDELABRUM_OF_INVOCATION); + + if (o && o->spe < 7) + Sprintf(buf, "Attach %s to your candelabrum, or %s %s", s, + !otmp->lamplit ? "light" : "extinguish", /* [lowercase] */ + multiple ? "them" : "it"); + else + Sprintf(buf, "%s %s %s", light, s, simpleonames(otmp)); + ia_addmenu(win, IA_APPLY_OBJ, 'a', buf); + } else if (otmp->otyp == OIL_LAMP || otmp->otyp == MAGIC_LAMP + || otmp->otyp == BRASS_LANTERN) { + Sprintf(buf, "%s this light source", light); + ia_addmenu(win, IA_APPLY_OBJ, 'a', buf); + } else if (otmp->otyp == POT_OIL && objects[otmp->otyp].oc_name_known) { + Sprintf(buf, "%s this oil", light); + ia_addmenu(win, IA_APPLY_OBJ, 'a', buf); + } else if (otmp->oclass == POTION_CLASS) { + /* FIXME? this should probably be moved to 'D' rather than be 'a' */ + Sprintf(buf, "Dip something into %s potion%s", + is_plural(otmp) ? "one of these" : "this", plur(otmp->quan)); + ia_addmenu(win, IA_DIP_OBJ, 'a', buf); + } else if (otmp->otyp == EXPENSIVE_CAMERA) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Take a photograph"); + else if (otmp->otyp == TOWEL) + ia_addmenu(win, IA_APPLY_OBJ, 'a', + "Clean yourself off with this towel"); + else if (otmp->otyp == CRYSTAL_BALL) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Peer into this crystal ball"); + else if (otmp->otyp == MAGIC_MARKER) + ia_addmenu(win, IA_APPLY_OBJ, 'a', + "Write on something with this marker"); + else if (otmp->otyp == FIGURINE) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Make this figurine transform"); + else if (otmp->otyp == UNICORN_HORN) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Use this unicorn horn"); + else if (otmp->otyp == HORN_OF_PLENTY + && objects[otmp->otyp].oc_name_known) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Blow into the horn of plenty"); + else if (otmp->otyp >= WOODEN_FLUTE && otmp->otyp <= DRUM_OF_EARTHQUAKE) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Play this musical instrument"); + else if (otmp->otyp == LAND_MINE || otmp->otyp == BEARTRAP) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Arm this trap"); + else if (otmp->otyp == PICK_AXE || otmp->otyp == DWARVISH_MATTOCK) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Dig with this digging tool"); + else if (otmp->oclass == WAND_CLASS) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Break this wand"); + + /* 'c', 'C' - call an item or its type something */ + if (item_naming_classification(otmp, buf, buf2)) { + if (*buf) + ia_addmenu(win, IA_NAME_OBJ, 'c', buf); + if (*buf2) + ia_addmenu(win, IA_NAME_OTYP, 'C', buf2); + } + + /* d: drop item, works on everything except worn items; those will + always have a takeoff/remove choice so we don't have to worry + about the menu maybe being empty when 'd' is suppressed */ + if (!already_worn) { + Sprintf(buf, "Drop this %s", (otmp->quan > 1L) ? "stack" : "item"); + ia_addmenu(win, IA_DROP_OBJ, 'd', buf); + } + + /* e: eat item */ + if (otmp->otyp == TIN) { + Sprintf(buf, "Open %s%s and eat the contents", + (otmp->quan > 1L) ? "one of these tins" : "this tin", + (otmp->otyp == TIN && uwep && uwep->otyp == TIN_OPENER) + ? " with your tin opener" : ""); + ia_addmenu(win, IA_EAT_OBJ, 'e', buf); + } else if (is_edible(otmp)) { + Sprintf(buf, "Eat %s", (otmp->quan > 1L) ? "one of these" : "this"); + ia_addmenu(win, IA_EAT_OBJ, 'e', buf); + } + + /* E: engrave with item */ + if (otmp->otyp == TOWEL) { + ia_addmenu(win, IA_ENGRAVE_OBJ, 'E', + "Wipe the floor with this towel"); + } else if (otmp->otyp == MAGIC_MARKER) { + ia_addmenu(win, IA_ENGRAVE_OBJ, 'E', + "Scribble graffiti on the floor"); + } else if (otmp->oclass == WEAPON_CLASS || otmp->oclass == WAND_CLASS + || otmp->oclass == GEM_CLASS || otmp->oclass == RING_CLASS) { + Sprintf(buf, "%s on the %s with %s", + (is_blade(otmp) || otmp->oclass == WAND_CLASS + || ((otmp->oclass == GEM_CLASS || otmp->oclass == RING_CLASS) + && objects[otmp->otyp].oc_tough)) ? "Engrave" : "Write", + surface(u.ux, u.uy), + (otmp->quan > 1L) ? "one of these items" : "this item"); + ia_addmenu(win, IA_ENGRAVE_OBJ, 'E', buf); + } + + /* f: fire quivered ammo */ + if (otmp == uquiver) { + boolean shoot = ammo_and_launcher(otmp, uwep); + + /* FIXME: see the multi-shot FIXME about "one of" for 't: throw' */ + Sprintf(buf, "%s %s", shoot ? "Shoot" : "Throw", + (otmp->quan > 1L) ? "one of these" : "this"); + if (shoot) { + assert(uwep != NULL); + Sprintf(eos(buf), " with your wielded %s", simpleonames(uwep)); + } + ia_addmenu(win, IA_FIRE_OBJ, 'f', buf); + } + + /* i: #adjust inventory letter; gold can't be adjusted unless there + is some in a slot other than '$' (which shouldn't be possible) */ + if (otmp->oclass != COIN_CLASS || check_invent_gold("item-action")) + ia_addmenu(win, IA_ADJUST_OBJ, 'i', + "Adjust inventory by assigning new letter"); + /* I: #adjust inventory item by splitting its stack */ + if (otmp->quan > 1L && otmp->oclass != COIN_CLASS) + ia_addmenu(win, IA_ADJUST_STACK, 'I', + "Adjust inventory by splitting this stack"); + + /* O: offer sacrifice */ + if (IS_ALTAR(levl[u.ux][u.uy].typ) && !u.uswallow) { + /* FIXME: this doesn't match #offer's likely candidates, which don't + include corpses on Astral and don't include amulets off Astral */ + if (otmp->otyp == CORPSE) + ia_addmenu(win, IA_SACRIFICE, 'O', + "Offer this corpse as a sacrifice at this altar"); + else if (otmp->otyp == AMULET_OF_YENDOR + || otmp->otyp == FAKE_AMULET_OF_YENDOR) + ia_addmenu(win, IA_SACRIFICE, 'O', + "Offer this amulet as a sacrifice at this altar"); + } + + /* p: pay for unpaid utems */ + if (otmp->unpaid + /* FIXME: should also handle player owned container (so not + flagged 'unpaid') holding shop owned items */ + && (mtmp = shop_keeper(*in_rooms(u.ux, u.uy, SHOPBASE))) != 0 + && inhishop(mtmp)) { + Sprintf(buf, "Buy this unpaid %s", + (otmp->quan > 1L) ? "stack" : "item"); + ia_addmenu(win, IA_BUY_OBJ, 'p', buf); + } + + /* P: put on accessory */ + if (!already_worn) { + /* if 'otmp' is worn, we'll skip 'P' and show 'R' below; + if not worn, we show 'P - Put on this ' if + the slot is available, or 'P - '; for the latter, + 'P' will fail but we don't want to omit the choice because + item actions can be used to learn commands */ + *buf = '\0'; + if (otmp->oclass == AMULET_CLASS) { + Strcpy(buf, !uamul ? "Put this amulet on" + : "[already wearing an amulet]"); + } else if (otmp->oclass == RING_CLASS || otmp->otyp == MEAT_RING) { + if (!uleft || !uright) + Strcpy(buf, "Put this ring on"); + else + Sprintf(buf, "[both ring %s in use]", + makeplural(body_part(FINGER))); + } else if (otmp->otyp == BLINDFOLD || otmp->otyp == TOWEL + || otmp->otyp == LENSES) { + if (ublindf) + Strcpy(buf, "[already wearing eyewear]"); + else if (otmp->otyp == LENSES) + Strcpy(buf, "Put these lenses on"); + else + Sprintf(buf, "Put this on%s", + (otmp->otyp == TOWEL) ? " to blindfold yourself" : ""); + } + if (*buf) + ia_addmenu(win, IA_WEAR_OBJ, 'P', buf); + } + + /* q: drink item */ + if (otmp->oclass == POTION_CLASS) { + Sprintf(buf, "Quaff (drink) %s", + (otmp->quan > 1L) ? "one of these potions" : "this potion"); + ia_addmenu(win, IA_QUAFF_OBJ, 'q', buf); + } + + /* Q: quiver throwable item */ + if ((otmp->oclass == GEM_CLASS || otmp->oclass == WEAPON_CLASS) + && otmp != uquiver) { + Sprintf(buf, "Quiver this %s for easy %s with \'f\'ire", + (otmp->quan > 1L) ? "stack" : "item", + ammo_and_launcher(otmp, uwep) ? "shooting" : "throwing"); + ia_addmenu(win, IA_QUIVER_OBJ, 'Q', buf); + } + + /* r: read item */ + if (item_reading_classification(otmp, buf) == IA_READ_OBJ) + ia_addmenu(win, IA_READ_OBJ, 'r', buf); + + /* R: remove accessory or rub item */ + if (otmp->owornmask & W_ACCESSORY) { + Sprintf(buf, "Remove this %s", + (otmp->owornmask & W_AMUL) ? "amulet" + : (otmp->owornmask & W_RING) ? "ring" + : (otmp->owornmask & W_TOOL) ? "eyewear" + : "accessory"); /* catchall -- can't happen */ + ia_addmenu(win, IA_TAKEOFF_OBJ, 'R', buf); + } + if (otmp->otyp == OIL_LAMP || otmp->otyp == MAGIC_LAMP + || otmp->otyp == BRASS_LANTERN) { + Sprintf(buf, "Rub this %s", simpleonames(otmp)); + ia_addmenu(win, IA_RUB_OBJ, 'R', buf); + } else if (otmp->oclass == GEM_CLASS && is_graystone(otmp)) + ia_addmenu(win, IA_RUB_OBJ, 'R', "Rub something on this stone"); + + /* t: throw item */ + if (!already_worn) { + boolean shoot = ammo_and_launcher(otmp, uwep); + + /* + * FIXME: + * 'one of these' should be changed to 'some of these' when there + * is the possibility of a multi-shot volley but we don't have + * any way to determine that except by actually calculating the + * volley count and that could randomly yield 1 here and 2..N + * while throwing or vice versa. + */ + Sprintf(buf, "%s %s%s", shoot ? "Shoot" : "Throw", + (otmp->quan == 1L) ? "this item" + : (otmp->otyp == GOLD_PIECE) ? "them" + : "one of these", + /* if otmp is quivered, we've already listed + 'f - shoot|throw this item' as a choice; + if 't' is duplicating that, say so ('t' and 'f' + behavior differs for throwing a stack of gold) */ + (otmp == uquiver && (otmp->otyp != GOLD_PIECE + || otmp->quan == 1L)) + ? " (same as 'f')" : ""); + ia_addmenu(win, IA_THROW_OBJ, 't', buf); + } + + /* T: take off armor, tip carried container */ + if (otmp->owornmask & W_ARMOR) + ia_addmenu(win, IA_TAKEOFF_OBJ, 'T', "Take off this armor"); + if ((Is_container(otmp) && (Has_contents(otmp) || !otmp->cknown)) + || (otmp->otyp == HORN_OF_PLENTY && (otmp->spe > 0 || !otmp->known))) + ia_addmenu(win, IA_TIP_CONTAINER, 'T', + "Tip all the contents out of this container"); + + /* V: invoke */ + if ((otmp->otyp == FAKE_AMULET_OF_YENDOR && !otmp->known) + || otmp->oartifact || objects[otmp->otyp].oc_unique + /* non-artifact crystal balls don't have any unique power but + the #invoke command lists them as likely candidates */ + || otmp->otyp == CRYSTAL_BALL) + ia_addmenu(win, IA_INVOKE_OBJ, 'V', + "Try to invoke a unique power of this object"); + + /* w: wield, hold in hands, works on everything but with different + advice text; not mentioned for things that are already wielded */ + if (otmp == uwep || cantwield(gy.youmonst.data)) { + ; /* either already wielded or can't wield anything; skip 'w' */ + } else if (otmp->oclass == WEAPON_CLASS || is_weptool(otmp) + || is_wet_towel(otmp) || otmp->otyp == HEAVY_IRON_BALL) { + Sprintf(buf, "Wield this %s as your weapon", + (otmp->quan > 1L) ? "stack" : "item"); + ia_addmenu(win, IA_WIELD_OBJ, 'w', buf); + } else if (otmp->otyp == TIN_OPENER) { + ia_addmenu(win, IA_WIELD_OBJ, 'w', + "Wield the tin opener to easily open tins"); + } else if (!already_worn) { + /* originally this was using "hold this item in your hands" but + there's no concept of "holding an item", plus it unwields + whatever item you already have wielded so use "wield this item" */ + Sprintf(buf, "Wield this %s in your %s", + (otmp->quan > 1L) ? "stack" : "item", + /* only two-handed weapons and unicorn horns care about + pluralizing "hand" and they won't reach here, but plural + sounds better when poly'd into something with "claw" */ + makeplural(body_part(HAND))); + ia_addmenu(win, IA_WIELD_OBJ, 'w', buf); + } + + /* W: wear armor */ + if (!already_worn) { + if (otmp->oclass == ARMOR_CLASS) { + /* if 'otmp' is worn we skip 'W' (and show 'T' above instead); + if it isn't, we either show "W - wear this" if otmp's slot + isn't populated, or "W - [already wearing ]"; + for the latter, picking 'W' will fail but we don't want to + omit 'W' in this situation */ + long Wmask = armcat_to_wornmask(objects[otmp->otyp].oc_armcat); + struct obj *o = wearmask_to_obj(Wmask); + + if (!o) + Strcpy(buf, "Wear this armor"); + else + Sprintf(buf, "[already wearing %s]", an(armor_simple_name(o))); + + ia_addmenu(win, IA_WEAR_OBJ, 'W', buf); + } + } + + /* x: Swap main and readied weapon */ + if (otmp == uwep && uswapwep) + ia_addmenu(win, IA_SWAPWEAPON, 'x', + "Swap this with your alternate weapon"); + else if (otmp == uwep) + ia_addmenu(win, IA_SWAPWEAPON, 'x', + "Ready this as an alternate weapon"); + else if (otmp == uswapwep) + ia_addmenu(win, IA_SWAPWEAPON, 'x', + "Swap this with your main weapon"); + + /* this is based on TWOWEAPOK() in wield.c; we don't call can_two_weapon() + because it is very verbose; attempting to two-weapon might be rejected + but we screen out most reasons for rejection before offering it as a + choice */ +#define MAYBETWOWEAPON(obj) \ + ((((obj)->oclass == WEAPON_CLASS) \ + ? !(is_launcher(obj) || is_ammo(obj) || is_missile(obj)) \ + : is_weptool(obj)) \ + && !bimanual(obj)) + + /* X: Toggle two-weapon mode on or off */ + if ((otmp == uwep || otmp == uswapwep) + /* if already two-weaponing, no special checks needed to toggle off */ + && (u.twoweap + /* but if not, try to filter most "you can't do that" here */ + || (could_twoweap(gy.youmonst.data) && !uarms + && uwep && MAYBETWOWEAPON(uwep) + && uswapwep && MAYBETWOWEAPON(uswapwep)))) { + Sprintf(buf, "Toggle two-weapon combat %s", u.twoweap ? "off" : "on"); + ia_addmenu(win, IA_TWOWEAPON, 'X', buf); + } + +#undef MAYBETWOWEAPON + + /* z: Zap wand */ + if (otmp->oclass == WAND_CLASS) + ia_addmenu(win, IA_ZAP_OBJ, 'z', + "Zap this wand to release its magic"); + + /* ?: Look up an item in the game's database */ + if (ia_checkfile(otmp)) { + Sprintf(buf, "Look up information about %s", + (otmp->quan > 1L) ? "these" : "this"); + ia_addmenu(win, IA_WHATIS_OBJ, '/', buf); + } + + Sprintf(buf, "Do what with %s?", the(cxname(otmp))); + end_menu(win, buf); + + n = select_menu(win, PICK_ONE, &selected); + + if (n > 0) { + act = selected[0].item.a_int; + free((genericptr_t) selected); + + itemactions_pushkeys(otmp, act); + } + destroy_nhwindow(win); + + /* finish the 'i' command: no time elapses and cancelling without + selecting an action doesn't matter */ + return ECMD_OK; +} + +/*iactions.c*/ diff --git a/src/invent.c b/src/invent.c index 523250197..713bd7d7a 100644 --- a/src/invent.c +++ b/src/invent.c @@ -5,11 +5,6 @@ #include "hack.h" -/* fake inventory letters, not 'a'..'z' or 'A'..'Z' */ -#define NOINVSYM '#' /* overflow because all 52 letters are in use */ -#define CONTAINED_SYM '>' /* designator for inside a container */ -#define HANDS_SYM '-' /* hands|fingers|self depending on context */ - staticfn void inuse_classify(Loot *, struct obj *); staticfn char *loot_xname(struct obj *); staticfn int invletter_value(char); @@ -43,11 +38,6 @@ staticfn int adjust_ok(struct obj *); staticfn int adjust_gold_ok(struct obj *); staticfn int doorganize_core(struct obj *); staticfn char obj_to_let(struct obj *); -staticfn boolean item_naming_classification(struct obj *, char *, char *); -staticfn int item_reading_classification(struct obj *, char *); -staticfn void ia_addmenu(winid, int, char, const char *); -staticfn void itemactions_pushkeys(struct obj *, int); -staticfn int itemactions(struct obj *); staticfn int dispinv_with_action(char *, boolean, const char *); /* enum and structs are defined in wintype.h */ @@ -2965,708 +2955,6 @@ xprname( RESTORE_WARNING_FORMAT_NONLITERAL -enum item_action_actions { - IA_NONE = 0, - IA_UNWIELD, /* hack for 'w-' */ - IA_APPLY_OBJ, /* 'a' */ - IA_DIP_OBJ, /* 'a' on a potion == dip */ - IA_NAME_OBJ, /* 'c' name individual item */ - IA_NAME_OTYP, /* 'C' name item's type */ - IA_DROP_OBJ, /* 'd' */ - IA_EAT_OBJ, /* 'e' */ - IA_ENGRAVE_OBJ, /* 'E' */ - IA_FIRE_OBJ, /* 'f' */ - IA_ADJUST_OBJ, /* 'i' #adjust inventory letter */ - IA_ADJUST_STACK, /* 'I' #adjust with count to split stack */ - IA_SACRIFICE, /* 'O' offer sacrifice */ - IA_BUY_OBJ, /* 'p' pay shopkeeper */ - IA_QUAFF_OBJ, - IA_QUIVER_OBJ, - IA_READ_OBJ, - IA_RUB_OBJ, - IA_THROW_OBJ, - IA_TAKEOFF_OBJ, - IA_TIP_CONTAINER, - IA_INVOKE_OBJ, - IA_WIELD_OBJ, - IA_WEAR_OBJ, - IA_SWAPWEAPON, - IA_TWOWEAPON, - IA_ZAP_OBJ, - IA_WHATIS_OBJ, /* '/' specify inventory object */ -}; - -/* construct text for the menu entries for IA_NAME_OBJ and IA_NAME_OTYP */ -staticfn boolean -item_naming_classification( - struct obj *obj, - char *onamebuf, - char *ocallbuf) -{ - static const char - Name[] = "Name", - Rename[] = "Rename or un-name", - Call[] = "Call", - /* "re-call" seems a bit weird, but "recall" and - "rename" don't fit for changing a type name */ - Recall[] = "Re-call or un-call"; - - onamebuf[0] = ocallbuf[0] = '\0'; - if (name_ok(obj) == GETOBJ_SUGGEST) { - Sprintf(onamebuf, "%s %s %s", - (!has_oname(obj) || !*ONAME(obj)) ? Name : Rename, - the_unique_obj(obj) ? "the" - : !is_plural(obj) ? "this specific" - : "this stack of", /*"these",*/ - simpleonames(obj)); - } - if (call_ok(obj) == GETOBJ_SUGGEST) { - char *callname = simpleonames(obj); - - /* prefix known unique item with "the", make all other types plural */ - if (the_unique_obj(obj)) /* treats unID'd fake amulets as if real */ - callname = the(callname); - else if (!is_plural(obj)) - callname = makeplural(callname); - Sprintf(ocallbuf, "%s the type for %s", - (!objects[obj->otyp].oc_uname - || !*objects[obj->otyp].oc_uname) ? Call : Recall, - callname); - } - return (*onamebuf || *ocallbuf) ? TRUE : FALSE; -} - -/* construct text for the menu entries for IA_READ_OBJ */ -staticfn int -item_reading_classification(struct obj *obj, char *outbuf) -{ - int otyp = obj->otyp, res = IA_READ_OBJ; - - *outbuf = '\0'; - if (otyp == FORTUNE_COOKIE) { - Strcpy(outbuf, "Read the message inside this cookie"); - } else if (otyp == T_SHIRT) { - Strcpy(outbuf, "Read the slogan on the shirt"); - } else if (otyp == ALCHEMY_SMOCK) { - Strcpy(outbuf, "Read the slogan on the apron"); - } else if (otyp == HAWAIIAN_SHIRT) { - Strcpy(outbuf, "Look at the pattern on the shirt"); - } else if (obj->oclass == SCROLL_CLASS) { - const char *magic = ((obj->dknown -#ifdef MAIL_STRUCTURES - && otyp != SCR_MAIL -#endif - && (otyp != SCR_BLANK_PAPER - || !objects[otyp].oc_name_known)) - ? " to activate its magic" : ""); - - Sprintf(outbuf, "Read this scroll%s", magic); - } else if (obj->oclass == SPBOOK_CLASS) { - boolean novel = (otyp == SPE_NOVEL), - blank = (otyp == SPE_BLANK_PAPER - && objects[otyp].oc_name_known), - tome = (otyp == SPE_BOOK_OF_THE_DEAD - && objects[otyp].oc_name_known); - - Sprintf(outbuf, "%s this %s", - (novel || blank) ? "Read" : tome ? "Examine" : "Study", - novel ? simpleonames(obj) /* "novel" or "paperback book" */ - : tome ? "tome" : "spellbook"); - } else { - res = IA_NONE; - } - return res; -} - -staticfn void -ia_addmenu(winid win, int act, char let, const char *txt) -{ - anything any; - int clr = NO_COLOR; - - any = cg.zeroany; - any.a_int = act; - add_menu(win, &nul_glyphinfo, &any, let, 0, - ATR_NONE, clr, txt, MENU_ITEMFLAGS_NONE); -} - -/* set up a command to execute on a specific item next */ -staticfn void -itemactions_pushkeys(struct obj *otmp, int act) -{ - switch (act) { - default: - impossible("Unknown item action %d", act); - break; - case IA_NONE: - break; - case IA_UNWIELD: - cmdq_add_ec(CQ_CANNED, (otmp == uwep) ? dowield - : (otmp == uswapwep) ? remarm_swapwep - : (otmp == uquiver) ? dowieldquiver - : donull); /* can't happen */ - cmdq_add_key(CQ_CANNED, HANDS_SYM); - break; - case IA_APPLY_OBJ: - cmdq_add_ec(CQ_CANNED, doapply); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_DIP_OBJ: - /* #altdip instead of normal #dip - takes potion to dip into - first (the inventory item instigating this) and item to - be dipped second, also ignores floor features such as - fountain/sink so we don't need to force m-prefix here */ - cmdq_add_ec(CQ_CANNED, dip_into); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_NAME_OBJ: - case IA_NAME_OTYP: - cmdq_add_ec(CQ_CANNED, docallcmd); - cmdq_add_key(CQ_CANNED, (act == IA_NAME_OBJ) ? 'i' : 'o'); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_DROP_OBJ: - cmdq_add_ec(CQ_CANNED, dodrop); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_EAT_OBJ: - /* start with m-prefix; for #eat, it means ignore floor food - if present and eat food from invent */ - cmdq_add_ec(CQ_CANNED, do_reqmenu); - cmdq_add_ec(CQ_CANNED, doeat); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_ENGRAVE_OBJ: - cmdq_add_ec(CQ_CANNED, doengrave); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_FIRE_OBJ: - cmdq_add_ec(CQ_CANNED, dofire); - break; - case IA_ADJUST_OBJ: - cmdq_add_ec(CQ_CANNED, doorganize); /* #adjust */ - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_ADJUST_STACK: - cmdq_add_ec(CQ_CANNED, adjust_split); /* #altadjust */ - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_SACRIFICE: - cmdq_add_ec(CQ_CANNED, dosacrifice); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_BUY_OBJ: - cmdq_add_ec(CQ_CANNED, dopay); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_QUAFF_OBJ: - /* start with m-prefix; for #quaff, it means ignore fountain - or sink if present and drink a potion from invent */ - cmdq_add_ec(CQ_CANNED, do_reqmenu); - cmdq_add_ec(CQ_CANNED, dodrink); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_QUIVER_OBJ: - cmdq_add_ec(CQ_CANNED, dowieldquiver); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_READ_OBJ: - cmdq_add_ec(CQ_CANNED, doread); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_RUB_OBJ: - cmdq_add_ec(CQ_CANNED, dorub); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_THROW_OBJ: - cmdq_add_ec(CQ_CANNED, dothrow); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_TAKEOFF_OBJ: - cmdq_add_ec(CQ_CANNED, ia_dotakeoff); /* #altdotakeoff */ - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_TIP_CONTAINER: - /* start with m-prefix to skip floor containers; - for menustyle:Traditional when more than one floor container - is present, player will get a #tip menu and have to pick - the "tip something being carried" choice, then this item - will be already chosen from inventory; suboptimal but - possibly an acceptable tradeoff since combining item actions - with use of traditional ggetobj() is an unlikely scenario */ - cmdq_add_ec(CQ_CANNED, do_reqmenu); - cmdq_add_ec(CQ_CANNED, dotip); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_INVOKE_OBJ: - cmdq_add_ec(CQ_CANNED, doinvoke); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_WIELD_OBJ: - cmdq_add_ec(CQ_CANNED, dowield); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_WEAR_OBJ: - cmdq_add_ec(CQ_CANNED, dowear); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_SWAPWEAPON: - cmdq_add_ec(CQ_CANNED, doswapweapon); - break; - case IA_TWOWEAPON: - cmdq_add_ec(CQ_CANNED, dotwoweapon); - break; - case IA_ZAP_OBJ: - cmdq_add_ec(CQ_CANNED, dozap); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_WHATIS_OBJ: - cmdq_add_ec(CQ_CANNED, dowhatis); /* "/" command */ - cmdq_add_key(CQ_CANNED, 'i'); /* "i" == item from inventory */ - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - } -} - -/* Show menu of possible actions hero could do with item otmp */ -staticfn int -itemactions(struct obj *otmp) -{ - int n, act = IA_NONE; - winid win; - char buf[BUFSZ], buf2[BUFSZ]; - menu_item *selected; - struct monst *mtmp; - const char *light = otmp->lamplit ? "Extinguish" : "Light"; - boolean already_worn = (otmp->owornmask & (W_ARMOR | W_ACCESSORY)) != 0; - - win = create_nhwindow(NHW_MENU); - start_menu(win, MENU_BEHAVE_STANDARD); - - /* -: unwield; picking current weapon offers an opportunity for 'w-' - to wield bare/gloved hands; likewise for 'Q-' with quivered item(s) */ - if (otmp == uwep || otmp == uswapwep || otmp == uquiver) { - const char *verb = (otmp == uquiver) ? "Quiver" : "Wield", - *action = (otmp == uquiver) ? "un-ready" : "un-wield", - *which = is_plural(otmp) ? "these" : "this", - *what = ((otmp->oclass == WEAPON_CLASS || is_weptool(otmp)) - ? "weapon" : "item"); - /* - * TODO: if uwep is ammo, tell player that to shoot instead of toss, - * the corresponding launcher must be wielded; - */ - Sprintf(buf, "%s '%c' to %s %s %s", - verb, HANDS_SYM, action, which, - is_plural(otmp) ? makeplural(what) : what); - ia_addmenu(win, IA_UNWIELD, '-', buf); - } - - /* a: apply */ - if (otmp->oclass == COIN_CLASS) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Flip a coin"); - else if (otmp->otyp == CREAM_PIE) - ia_addmenu(win, IA_APPLY_OBJ, 'a', - "Hit yourself with this cream pie"); - else if (otmp->otyp == BULLWHIP) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Lash out with this whip"); - else if (otmp->otyp == GRAPPLING_HOOK) - ia_addmenu(win, IA_APPLY_OBJ, 'a', - "Grapple something with this hook"); - else if (otmp->otyp == BAG_OF_TRICKS && objects[otmp->otyp].oc_name_known) - /* bag of tricks skips this unless discovered */ - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Reach into this bag"); - else if (Is_container(otmp)) - /* bag of tricks gets here only if not yet discovered */ - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Open this container"); - else if (otmp->otyp == CAN_OF_GREASE) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Use the can to grease an item"); - else if (otmp->otyp == LOCK_PICK - || otmp->otyp == CREDIT_CARD - || otmp->otyp == SKELETON_KEY) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Use this tool to pick a lock"); - else if (otmp->otyp == TINNING_KIT) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Use this kit to tin a corpse"); - else if (otmp->otyp == LEASH) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Tie a pet to this leash"); - else if (otmp->otyp == SADDLE) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Place this saddle on a pet"); - else if (otmp->otyp == MAGIC_WHISTLE - || otmp->otyp == TIN_WHISTLE) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Blow this whistle"); - else if (otmp->otyp == EUCALYPTUS_LEAF) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Use this leaf as a whistle"); - else if (otmp->otyp == STETHOSCOPE) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Listen through the stethoscope"); - else if (otmp->otyp == MIRROR) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Show something its reflection"); - else if (otmp->otyp == BELL || otmp->otyp == BELL_OF_OPENING) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Ring the bell"); - else if (otmp->otyp == CANDELABRUM_OF_INVOCATION) { - Sprintf(buf, "%s the candelabrum", light); - ia_addmenu(win, IA_APPLY_OBJ, 'a', buf); - } else if (otmp->otyp == WAX_CANDLE || otmp->otyp == TALLOW_CANDLE) { - boolean multiple = (otmp->quan == 1L) ? FALSE : TRUE; - const char *s = multiple ? "these" : "this"; - struct obj *o = carrying(CANDELABRUM_OF_INVOCATION); - - if (o && o->spe < 7) - Sprintf(buf, "Attach %s to your candelabrum, or %s %s", s, - !otmp->lamplit ? "light" : "extinguish", /* [lowercase] */ - multiple ? "them" : "it"); - else - Sprintf(buf, "%s %s %s", light, s, simpleonames(otmp)); - ia_addmenu(win, IA_APPLY_OBJ, 'a', buf); - } else if (otmp->otyp == OIL_LAMP || otmp->otyp == MAGIC_LAMP - || otmp->otyp == BRASS_LANTERN) { - Sprintf(buf, "%s this light source", light); - ia_addmenu(win, IA_APPLY_OBJ, 'a', buf); - } else if (otmp->otyp == POT_OIL && objects[otmp->otyp].oc_name_known) { - Sprintf(buf, "%s this oil", light); - ia_addmenu(win, IA_APPLY_OBJ, 'a', buf); - } else if (otmp->oclass == POTION_CLASS) { - /* FIXME? this should probably be moved to 'D' rather than be 'a' */ - Sprintf(buf, "Dip something into %s potion%s", - is_plural(otmp) ? "one of these" : "this", plur(otmp->quan)); - ia_addmenu(win, IA_DIP_OBJ, 'a', buf); - } else if (otmp->otyp == EXPENSIVE_CAMERA) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Take a photograph"); - else if (otmp->otyp == TOWEL) - ia_addmenu(win, IA_APPLY_OBJ, 'a', - "Clean yourself off with this towel"); - else if (otmp->otyp == CRYSTAL_BALL) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Peer into this crystal ball"); - else if (otmp->otyp == MAGIC_MARKER) - ia_addmenu(win, IA_APPLY_OBJ, 'a', - "Write on something with this marker"); - else if (otmp->otyp == FIGURINE) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Make this figurine transform"); - else if (otmp->otyp == UNICORN_HORN) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Use this unicorn horn"); - else if (otmp->otyp == HORN_OF_PLENTY - && objects[otmp->otyp].oc_name_known) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Blow into the horn of plenty"); - else if (otmp->otyp >= WOODEN_FLUTE && otmp->otyp <= DRUM_OF_EARTHQUAKE) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Play this musical instrument"); - else if (otmp->otyp == LAND_MINE || otmp->otyp == BEARTRAP) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Arm this trap"); - else if (otmp->otyp == PICK_AXE || otmp->otyp == DWARVISH_MATTOCK) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Dig with this digging tool"); - else if (otmp->oclass == WAND_CLASS) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Break this wand"); - - /* 'c', 'C' - call an item or its type something */ - if (item_naming_classification(otmp, buf, buf2)) { - if (*buf) - ia_addmenu(win, IA_NAME_OBJ, 'c', buf); - if (*buf2) - ia_addmenu(win, IA_NAME_OTYP, 'C', buf2); - } - - /* d: drop item, works on everything except worn items; those will - always have a takeoff/remove choice so we don't have to worry - about the menu maybe being empty when 'd' is suppressed */ - if (!already_worn) { - Sprintf(buf, "Drop this %s", (otmp->quan > 1L) ? "stack" : "item"); - ia_addmenu(win, IA_DROP_OBJ, 'd', buf); - } - - /* e: eat item */ - if (otmp->otyp == TIN) { - Sprintf(buf, "Open %s%s and eat the contents", - (otmp->quan > 1L) ? "one of these tins" : "this tin", - (otmp->otyp == TIN && uwep && uwep->otyp == TIN_OPENER) - ? " with your tin opener" : ""); - ia_addmenu(win, IA_EAT_OBJ, 'e', buf); - } else if (is_edible(otmp)) { - Sprintf(buf, "Eat %s", (otmp->quan > 1L) ? "one of these" : "this"); - ia_addmenu(win, IA_EAT_OBJ, 'e', buf); - } - - /* E: engrave with item */ - if (otmp->otyp == TOWEL) { - ia_addmenu(win, IA_ENGRAVE_OBJ, 'E', - "Wipe the floor with this towel"); - } else if (otmp->otyp == MAGIC_MARKER) { - ia_addmenu(win, IA_ENGRAVE_OBJ, 'E', - "Scribble graffiti on the floor"); - } else if (otmp->oclass == WEAPON_CLASS || otmp->oclass == WAND_CLASS - || otmp->oclass == GEM_CLASS || otmp->oclass == RING_CLASS) { - Sprintf(buf, "%s on the %s with %s", - (is_blade(otmp) || otmp->oclass == WAND_CLASS - || ((otmp->oclass == GEM_CLASS || otmp->oclass == RING_CLASS) - && objects[otmp->otyp].oc_tough)) ? "Engrave" : "Write", - surface(u.ux, u.uy), - (otmp->quan > 1L) ? "one of these items" : "this item"); - ia_addmenu(win, IA_ENGRAVE_OBJ, 'E', buf); - } - - /* f: fire quivered ammo */ - if (otmp == uquiver) { - boolean shoot = ammo_and_launcher(otmp, uwep); - - /* FIXME: see the multi-shot FIXME about "one of" for 't: throw' */ - Sprintf(buf, "%s %s", shoot ? "Shoot" : "Throw", - (otmp->quan > 1L) ? "one of these" : "this"); - if (shoot) { - assert(uwep != NULL); - Sprintf(eos(buf), " with your wielded %s", simpleonames(uwep)); - } - ia_addmenu(win, IA_FIRE_OBJ, 'f', buf); - } - - /* i: #adjust inventory letter; gold can't be adjusted unless there - is some in a slot other than '$' (which shouldn't be possible) */ - if (otmp->oclass != COIN_CLASS || check_invent_gold("item-action")) - ia_addmenu(win, IA_ADJUST_OBJ, 'i', - "Adjust inventory by assigning new letter"); - /* I: #adjust inventory item by splitting its stack */ - if (otmp->quan > 1L && otmp->oclass != COIN_CLASS) - ia_addmenu(win, IA_ADJUST_STACK, 'I', - "Adjust inventory by splitting this stack"); - - /* O: offer sacrifice */ - if (IS_ALTAR(levl[u.ux][u.uy].typ) && !u.uswallow) { - /* FIXME: this doesn't match #offer's likely candidates, which don't - include corpses on Astral and don't include amulets off Astral */ - if (otmp->otyp == CORPSE) - ia_addmenu(win, IA_SACRIFICE, 'O', - "Offer this corpse as a sacrifice at this altar"); - else if (otmp->otyp == AMULET_OF_YENDOR - || otmp->otyp == FAKE_AMULET_OF_YENDOR) - ia_addmenu(win, IA_SACRIFICE, 'O', - "Offer this amulet as a sacrifice at this altar"); - } - - /* p: pay for unpaid utems */ - if (otmp->unpaid - /* FIXME: should also handle player owned container (so not - flagged 'unpaid') holding shop owned items */ - && (mtmp = shop_keeper(*in_rooms(u.ux, u.uy, SHOPBASE))) != 0 - && inhishop(mtmp)) { - Sprintf(buf, "Buy this unpaid %s", - (otmp->quan > 1L) ? "stack" : "item"); - ia_addmenu(win, IA_BUY_OBJ, 'p', buf); - } - - /* P: put on accessory */ - if (!already_worn) { - /* if 'otmp' is worn, we'll skip 'P' and show 'R' below; - if not worn, we show 'P - Put on this ' if - the slot is available, or 'P - '; for the latter, - 'P' will fail but we don't want to omit the choice because - item actions can be used to learn commands */ - *buf = '\0'; - if (otmp->oclass == AMULET_CLASS) { - Strcpy(buf, !uamul ? "Put this amulet on" - : "[already wearing an amulet]"); - } else if (otmp->oclass == RING_CLASS || otmp->otyp == MEAT_RING) { - if (!uleft || !uright) - Strcpy(buf, "Put this ring on"); - else - Sprintf(buf, "[both ring %s in use]", - makeplural(body_part(FINGER))); - } else if (otmp->otyp == BLINDFOLD || otmp->otyp == TOWEL - || otmp->otyp == LENSES) { - if (ublindf) - Strcpy(buf, "[already wearing eyewear]"); - else if (otmp->otyp == LENSES) - Strcpy(buf, "Put these lenses on"); - else - Sprintf(buf, "Put this on%s", - (otmp->otyp == TOWEL) ? " to blindfold yourself" : ""); - } - if (*buf) - ia_addmenu(win, IA_WEAR_OBJ, 'P', buf); - } - - /* q: drink item */ - if (otmp->oclass == POTION_CLASS) { - Sprintf(buf, "Quaff (drink) %s", - (otmp->quan > 1L) ? "one of these potions" : "this potion"); - ia_addmenu(win, IA_QUAFF_OBJ, 'q', buf); - } - - /* Q: quiver throwable item */ - if ((otmp->oclass == GEM_CLASS || otmp->oclass == WEAPON_CLASS) - && otmp != uquiver) { - Sprintf(buf, "Quiver this %s for easy %s with \'f\'ire", - (otmp->quan > 1L) ? "stack" : "item", - ammo_and_launcher(otmp, uwep) ? "shooting" : "throwing"); - ia_addmenu(win, IA_QUIVER_OBJ, 'Q', buf); - } - - /* r: read item */ - if (item_reading_classification(otmp, buf) == IA_READ_OBJ) - ia_addmenu(win, IA_READ_OBJ, 'r', buf); - - /* R: remove accessory or rub item */ - if (otmp->owornmask & W_ACCESSORY) { - Sprintf(buf, "Remove this %s", - (otmp->owornmask & W_AMUL) ? "amulet" - : (otmp->owornmask & W_RING) ? "ring" - : (otmp->owornmask & W_TOOL) ? "eyewear" - : "accessory"); /* catchall -- can't happen */ - ia_addmenu(win, IA_TAKEOFF_OBJ, 'R', buf); - } - if (otmp->otyp == OIL_LAMP || otmp->otyp == MAGIC_LAMP - || otmp->otyp == BRASS_LANTERN) { - Sprintf(buf, "Rub this %s", simpleonames(otmp)); - ia_addmenu(win, IA_RUB_OBJ, 'R', buf); - } else if (otmp->oclass == GEM_CLASS && is_graystone(otmp)) - ia_addmenu(win, IA_RUB_OBJ, 'R', "Rub something on this stone"); - - /* t: throw item */ - if (!already_worn) { - boolean shoot = ammo_and_launcher(otmp, uwep); - - /* - * FIXME: - * 'one of these' should be changed to 'some of these' when there - * is the possibility of a multi-shot volley but we don't have - * any way to determine that except by actually calculating the - * volley count and that could randomly yield 1 here and 2..N - * while throwing or vice versa. - */ - Sprintf(buf, "%s %s%s", shoot ? "Shoot" : "Throw", - (otmp->quan == 1L) ? "this item" - : (otmp->otyp == GOLD_PIECE) ? "them" - : "one of these", - /* if otmp is quivered, we've already listed - 'f - shoot|throw this item' as a choice; - if 't' is duplicating that, say so ('t' and 'f' - behavior differs for throwing a stack of gold) */ - (otmp == uquiver && (otmp->otyp != GOLD_PIECE - || otmp->quan == 1L)) - ? " (same as 'f')" : ""); - ia_addmenu(win, IA_THROW_OBJ, 't', buf); - } - - /* T: take off armor, tip carried container */ - if (otmp->owornmask & W_ARMOR) - ia_addmenu(win, IA_TAKEOFF_OBJ, 'T', "Take off this armor"); - if ((Is_container(otmp) && (Has_contents(otmp) || !otmp->cknown)) - || (otmp->otyp == HORN_OF_PLENTY && (otmp->spe > 0 || !otmp->known))) - ia_addmenu(win, IA_TIP_CONTAINER, 'T', - "Tip all the contents out of this container"); - - /* V: invoke */ - if ((otmp->otyp == FAKE_AMULET_OF_YENDOR && !otmp->known) - || otmp->oartifact || objects[otmp->otyp].oc_unique - /* non-artifact crystal balls don't have any unique power but - the #invoke command lists them as likely candidates */ - || otmp->otyp == CRYSTAL_BALL) - ia_addmenu(win, IA_INVOKE_OBJ, 'V', - "Try to invoke a unique power of this object"); - - /* w: wield, hold in hands, works on everything but with different - advice text; not mentioned for things that are already wielded */ - if (otmp == uwep || cantwield(gy.youmonst.data)) { - ; /* either already wielded or can't wield anything; skip 'w' */ - } else if (otmp->oclass == WEAPON_CLASS || is_weptool(otmp) - || is_wet_towel(otmp) || otmp->otyp == HEAVY_IRON_BALL) { - Sprintf(buf, "Wield this %s as your weapon", - (otmp->quan > 1L) ? "stack" : "item"); - ia_addmenu(win, IA_WIELD_OBJ, 'w', buf); - } else if (otmp->otyp == TIN_OPENER) { - ia_addmenu(win, IA_WIELD_OBJ, 'w', - "Wield the tin opener to easily open tins"); - } else if (!already_worn) { - /* originally this was using "hold this item in your hands" but - there's no concept of "holding an item", plus it unwields - whatever item you already have wielded so use "wield this item" */ - Sprintf(buf, "Wield this %s in your %s", - (otmp->quan > 1L) ? "stack" : "item", - /* only two-handed weapons and unicorn horns care about - pluralizing "hand" and they won't reach here, but plural - sounds better when poly'd into something with "claw" */ - makeplural(body_part(HAND))); - ia_addmenu(win, IA_WIELD_OBJ, 'w', buf); - } - - /* W: wear armor */ - if (!already_worn) { - if (otmp->oclass == ARMOR_CLASS) { - /* if 'otmp' is worn we skip 'W' (and show 'T' above instead); - if it isn't, we either show "W - wear this" if otmp's slot - isn't populated, or "W - [already wearing ]"; - for the latter, picking 'W' will fail but we don't want to - omit 'W' in this situation */ - long Wmask = armcat_to_wornmask(objects[otmp->otyp].oc_armcat); - struct obj *o = wearmask_to_obj(Wmask); - - if (!o) - Strcpy(buf, "Wear this armor"); - else - Sprintf(buf, "[already wearing %s]", an(armor_simple_name(o))); - - ia_addmenu(win, IA_WEAR_OBJ, 'W', buf); - } - } - - /* x: Swap main and readied weapon */ - if (otmp == uwep && uswapwep) - ia_addmenu(win, IA_SWAPWEAPON, 'x', - "Swap this with your alternate weapon"); - else if (otmp == uwep) - ia_addmenu(win, IA_SWAPWEAPON, 'x', - "Ready this as an alternate weapon"); - else if (otmp == uswapwep) - ia_addmenu(win, IA_SWAPWEAPON, 'x', - "Swap this with your main weapon"); - - /* this is based on TWOWEAPOK() in wield.c; we don't call can_two_weapon() - because it is very verbose; attempting to two-weapon might be rejected - but we screen out most reasons for rejection before offering it as a - choice */ -#define MAYBETWOWEAPON(obj) \ - ((((obj)->oclass == WEAPON_CLASS) \ - ? !(is_launcher(obj) || is_ammo(obj) || is_missile(obj)) \ - : is_weptool(obj)) \ - && !bimanual(obj)) - - /* X: Toggle two-weapon mode on or off */ - if ((otmp == uwep || otmp == uswapwep) - /* if already two-weaponing, no special checks needed to toggle off */ - && (u.twoweap - /* but if not, try to filter most "you can't do that" here */ - || (could_twoweap(gy.youmonst.data) && !uarms - && uwep && MAYBETWOWEAPON(uwep) - && uswapwep && MAYBETWOWEAPON(uswapwep)))) { - Sprintf(buf, "Toggle two-weapon combat %s", u.twoweap ? "off" : "on"); - ia_addmenu(win, IA_TWOWEAPON, 'X', buf); - } - -#undef MAYBETWOWEAPON - - /* z: Zap wand */ - if (otmp->oclass == WAND_CLASS) - ia_addmenu(win, IA_ZAP_OBJ, 'z', - "Zap this wand to release its magic"); - - /* ?: Look up an item in the game's database */ - if (ia_checkfile(otmp)) { - Sprintf(buf, "Look up information about %s", - (otmp->quan > 1L) ? "these" : "this"); - ia_addmenu(win, IA_WHATIS_OBJ, '/', buf); - } - - Sprintf(buf, "Do what with %s?", the(cxname(otmp))); - end_menu(win, buf); - - n = select_menu(win, PICK_ONE, &selected); - - if (n > 0) { - act = selected[0].item.a_int; - free((genericptr_t) selected); - - itemactions_pushkeys(otmp, act); - } - destroy_nhwindow(win); - - /* finish the 'i' command: no time elapses and cancelling without - selecting an action doesn't matter */ - return ECMD_OK; -} /* show some or all of inventory while allowing the picking of an item in order to preform context-sensitive item action on it; always returns 'ok'; diff --git a/sys/msdos/Makefile.GCC b/sys/msdos/Makefile.GCC index 8ae2be4dc..bdf4391e3 100644 --- a/sys/msdos/Makefile.GCC +++ b/sys/msdos/Makefile.GCC @@ -296,7 +296,7 @@ VOBJ22 = $(O)topl.o $(O)topten.o $(O)trap.o $(O)u_init.o $(O)uhitm.o VOBJ23 = $(O)utf8map.o $(O)vault.o $(O)track.o $(O)vision.o $(O)weapon.o VOBJ24 = $(O)were.o $(O)wield.o $(O)windows.o $(O)wintty.o $(O)wizard.o VOBJ25 = $(O)wizcmds.o $(O)worm.o $(O)worn.o $(O)write.o $(O)zap.o -VOBJ26 = $(O)light.o $(O)dlb.o $(REGEX) +VOBJ26 = $(O)light.o $(O)dlb.o $(O)iactions.o $(REGEX) SOBJ = $(O)msdos.o $(O)pcsys.o $(O)tty.o $(O)unix.o \ $(O)video.o $(O)vidtxt.o $(O)pckeys.o @@ -1399,6 +1399,7 @@ $(TARGETPFX)files.o: files.c $(HACK_H) ../include/dlb.h ../include/wintty.h \ $(TARGETPFX)fountain.o: fountain.c $(HACK_H) $(TARGETPFX)hack.o: hack.c $(HACK_H) $(TARGETPFX)hacklib.o: hacklib.c $(HACK_H) +$(TARGETPFX)iactions.o: iactions.c $(HACK_H) $(TARGETPFX)insight.o: insight.c $(HACK_H) $(TARGETPFX)invent.o: invent.c $(HACK_H) $(TARGETPFX)isaac64.o: isaac64.c $(CONFIG_H) ../include/isaac64.h diff --git a/sys/unix/Makefile.src b/sys/unix/Makefile.src index e5a30e3c1..81a6b72a6 100644 --- a/sys/unix/Makefile.src +++ b/sys/unix/Makefile.src @@ -519,7 +519,7 @@ HACKCSRC = allmain.c alloc.c apply.c artifact.c attrib.c ball.c bones.c \ dlb.c do.c do_name.c do_wear.c dog.c dogmove.c dokick.c dothrow.c \ drawing.c dungeon.c eat.c end.c engrave.c exper.c explode.c \ extralev.c files.c fountain.c hack.c hacklib.c \ - getpos.c glyphs.c insight.c invent.c isaac64.c light.c \ + getpos.c glyphs.c iactions.c insight.c invent.c isaac64.c light.c \ lock.c mail.c makemon.c mcastu.c mdlib.c mhitm.c \ mhitu.c minion.c mklev.c mkmap.c mkmaze.c mkobj.c mkroom.c mon.c \ mondata.c monmove.c monst.c mplayer.c mthrowu.c muse.c music.c \ @@ -601,6 +601,7 @@ HOBJ = $(TARGETPFX)allmain.o $(TARGETPFX)alloc.o \ $(TARGETPFX)engrave.o $(TARGETPFX)exper.o $(TARGETPFX)explode.o \ $(TARGETPFX)extralev.o $(TARGETPFX)files.o $(TARGETPFX)fountain.o \ $(TARGETPFX)getpos.o $(TARGETPFX)glyphs.o $(TARGETPFX)hack.o \ + $(TARGETPFX)iactions.o \ $(TARGETPFX)insight.o $(TARGETPFX)invent.o $(TARGETPFX)isaac64.o \ $(TARGETPFX)light.o $(TARGETPFX)lock.o $(TARGETPFX)mail.o \ $(TARGETPFX)makemon.o $(TARGETPFX)mcastu.o $(TARGETPFX)mdlib.o \ @@ -1176,6 +1177,7 @@ $(TARGETPFX)getpos.o: getpos.c $(HACK_H) $(TARGETPFX)glyphs.o: glyphs.c $(HACK_H) $(TARGETPFX)hack.o: hack.c $(HACK_H) $(TARGETPFX)hacklib.o: hacklib.c $(HACK_H) +$(TARGETPFX)iactions.o: iactions.c $(HACK_H) $(TARGETPFX)insight.o: insight.c $(HACK_H) $(TARGETPFX)invent.o: invent.c $(HACK_H) $(TARGETPFX)isaac64.o: isaac64.c $(CONFIG_H) ../include/isaac64.h diff --git a/sys/vms/Makefile.src b/sys/vms/Makefile.src index dee166f8e..9cf3f465f 100644 --- a/sys/vms/Makefile.src +++ b/sys/vms/Makefile.src @@ -151,7 +151,7 @@ HACKCSRC = allmain.c alloc.c apply.c artifact.c attrib.c ball.c bones.c \ dig.c display.c dlb.c do.c do_name.c do_wear.c dog.c dogmove.c dokick.c \ dothrow.c drawing.c dungeon.c eat.c end.c engrave.c exper.c \ explode.c extralev.c files.c fountain.c getpos.c glyphs.c hack.c \ - hacklib.c insight.c invent.c light.c lock.c \ + hacklib.c iactions.c insight.c invent.c light.c lock.c \ mail.c makemon.c mcastu.c mhitm.c mhitu.c minion.c \ mklev.c mkmap.c mkmaze.c mkobj.c mkroom.c mon.c mondata.c \ monmove.c monst.c mplayer.c mthrowu.c muse.c music.c o_init.c \ @@ -200,7 +200,7 @@ HOBJ1 = allmain.obj,alloc.obj,apply.obj,artifact.obj,attrib.obj, \ HOBJ2 = dog.obj,dogmove.obj,dokick.obj,dothrow.obj,drawing.obj, \ dungeon.obj,eat.obj,end.obj,engrave.obj,exper.obj,explode.obj, \ extralev.obj,files.obj,fountain.obj,getpos.obj,glyphs.obj,hack.obj, \ - hacklib.obj,insight.obj,invent.obj + hacklib.obj,iactions.obj,insight.obj,invent.obj HOBJ3 = light.obj,lock.obj,mail.obj,makemon.obj,mcastu.obj, \ mhitm.obj,mhitu.obj,minion.obj,mklev.obj,mkmap.obj,mkmaze.obj, \ mkobj.obj,mkroom.obj,mon.obj,mondata.obj,monmove.obj @@ -529,6 +529,7 @@ files.obj : files.c $(HACK_H) $(INC)dlb.h $(INC)wintty.h #zlib.h fountain.obj : fountain.c $(HACK_H) hack.obj : hack.c $(HACK_H) hacklib.obj : hacklib.c $(HACK_H) +iactions.obj : iactions.c $(HACK_H) insight.obj : insight.c $(HACK_H) invent.obj : invent.c $(HACK_H) light.obj : light.c $(HACK_H) diff --git a/sys/vms/Makefile_src.vms b/sys/vms/Makefile_src.vms index 937200a20..56b35aa58 100644 --- a/sys/vms/Makefile_src.vms +++ b/sys/vms/Makefile_src.vms @@ -130,7 +130,7 @@ HACKFILES := allmain alloc apply artifact attrib ball bones botl \ sp_lev spell stairs steal steed strutil symbols sys teleport \ timeout topten track trap u_init utf8map \ uhitm vault version vision weapon were wield \ - windows wizard wizcmds worm worn write zap + windows wizard wizcmds worm worn write zap iactions # the date file DATEFILES = date @@ -735,6 +735,7 @@ $(TARGETPFX)getpos.obj: getpos.c $(HACK_H) $(TARGETPFX)glyphs.obj: glyphs.c $(HACK_H) $(TARGETPFX)hack.obj: hack.c $(HACK_H) $(TARGETPFX)hacklib.obj: hacklib.c $(HACK_H) +$(TARGETPFX)iactions.obj: iactions.c $(HACK_H) $(TARGETPFX)insight.obj: insight.c $(HACK_H) $(TARGETPFX)invent.obj: invent.c $(HACK_H) $(TARGETPFX)isaac64.obj: isaac64.c $(CONFIG_H) $(INCL)isaac64.h diff --git a/sys/vms/vmsbuild.com b/sys/vms/vmsbuild.com index e34309fce..3749ffe04 100755 --- a/sys/vms/vmsbuild.com +++ b/sys/vms/vmsbuild.com @@ -425,7 +425,7 @@ $ c_list = "allmain,apply,artifact,attrib,ball,bones,botl,calendar,cmd" - + ",do_wear,dog,dogmove,dokick,dungeon,eat,end,engrave,exper,explode" - + ",extralev,files,fountain,getpos,glyphs" $ gosub compile_list -$ c_list = "hack,hacklib,insight,invent,light,lock,mail,makemon" - +$ c_list = "hack,hacklib,iactions,insight,invent,light,lock,mail,makemon" - + ",mcastu,mdlib,mhitm,mhitu,minion,mklev,mkmap,mkmaze" - + ",mkobj,mkroom,mon,mondata,monmove,mplayer,mthrowu,muse" - + ",music" diff --git a/sys/windows/GNUmakefile b/sys/windows/GNUmakefile index c964d780c..6f282c632 100644 --- a/sys/windows/GNUmakefile +++ b/sys/windows/GNUmakefile @@ -1232,7 +1232,7 @@ COREOBJS = $(addsuffix .o, allmain alloc apply artifact attrib ball bones botl \ dbridge decl detect dig display dlb do do_name do_wear \ dog dogmove dokick dothrow drawing dungeon \ eat end engrave exper explode extralev files fountain \ - getpos glyphs hack insight invent isaac64 light lock \ + getpos glyphs hack iactions insight invent isaac64 light lock \ mail makemon mcastu mdlib mhitm mhitu minion mklev mkmap mkmaze mkobj mkroom \ mon mondata monmove monst mplayer mthrowu muse music \ nhlobj nhlsel nhlua windsound o_init objects objnam options \ diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index e50f9fb21..faa637986 100644 --- a/sys/windows/Makefile.nmake +++ b/sys/windows/Makefile.nmake @@ -659,6 +659,7 @@ HACKCSRC = \ $(SRC)drawing.c $(SRC)dungeon.c $(SRC)eat.c $(SRC)end.c \ $(SRC)engrave.c $(SRC)exper.c $(SRC)explode.c $(SRC)files.c \ $(SRC)fountain.c $(SRC)getpos.c $(SRC)glyphs.c $(SRC)hack.c \ + $(SRC)iactions.c \ $(SRC)insight.c $(SRC)invent.c $(SRC)isaac64.c $(SRC)light.c \ $(SRC)lock.c $(SRC)mail.c $(SRC)makemon.c $(SRC)mcastu.c \ $(SRC)mdlib.c $(SRC)mhitm.c $(SRC)mhitu.c $(SRC)minion.c \ @@ -862,6 +863,7 @@ COREOBJTTY = \ $(OTTY)drawing.o $(OTTY)dungeon.o $(OTTY)eat.o $(OTTY)end.o \ $(OTTY)engrave.o $(OTTY)exper.o $(OTTY)explode.o $(OTTY)extralev.o \ $(OTTY)files.o $(OTTY)fountain.o $(OTTY)getpos.o $(OTTY)glyphs.o \ + $(OTTY)iactions.o \ $(OTTY)hack.o $(OTTY)insight.o $(OTTY)invent.o $(OTTY)isaac64.o \ $(OTTY)light.o $(OTTY)lock.o $(OTTY)mail.o $(OTTY)makemon.o \ $(OTTY)mcastu.o $(OTTY)mhitm.o $(OTTY)mhitu.o $(OTTY)minion.o \ @@ -925,6 +927,7 @@ COREOBJGUI = \ $(OGUI)drawing.o $(OGUI)dungeon.o $(OGUI)eat.o $(OGUI)end.o \ $(OGUI)engrave.o $(OGUI)exper.o $(OGUI)explode.o $(OGUI)extralev.o \ $(OGUI)files.o $(OGUI)fountain.o $(OGUI)getpos.o $(OGUI)glyphs.o \ + $(OGUI)iactions.o \ $(OGUI)hack.o $(OGUI)insight.o $(OGUI)invent.o $(OGUI)isaac64.o \ $(OGUI)light.o $(OGUI)lock.o $(OGUI)mail.o $(OGUI)makemon.o \ $(OGUI)mcastu.o $(OGUI)mhitm.o $(OGUI)mhitu.o $(OGUI)minion.o \ @@ -3257,6 +3260,7 @@ $(OTTY)getpos.o: getpos.c $(HACK_H) $(OTTY)glyphs.o: glyphs.c $(HACK_H) $(OTTY)hack.o: hack.c $(HACK_H) $(OTTY)hacklib.o: hacklib.c $(HACK_H) +$(OTTY)iactions.o: iactions.c $(HACK_H) $(OTTY)insight.o: insight.c $(HACK_H) $(OTTY)invent.o: invent.c $(HACK_H) $(OTTY)isaac64.o: isaac64.c $(CONFIG_H) $(INCL)isaac64.h @@ -3636,6 +3640,7 @@ $(OGUI)getpos.o: getpos.c $(HACK_H) $(OGUI)glyphs.o: glyphs.c $(HACK_H) $(OGUI)hack.o: hack.c $(HACK_H) $(OGUI)hacklib.o: hacklib.c $(HACK_H) +$(OGUI)iactions.o: iactions.c $(HACK_H) $(OGUI)insight.o: insight.c $(HACK_H) $(OGUI)invent.o: invent.c $(HACK_H) $(OGUI)isaac64.o: isaac64.c $(CONFIG_H) $(INCL)isaac64.h diff --git a/sys/windows/vs/NetHack/NetHack.vcxproj b/sys/windows/vs/NetHack/NetHack.vcxproj index d2ca44c61..5df071e6c 100644 --- a/sys/windows/vs/NetHack/NetHack.vcxproj +++ b/sys/windows/vs/NetHack/NetHack.vcxproj @@ -125,6 +125,7 @@ + diff --git a/sys/windows/vs/NetHackW/NetHackW.vcxproj b/sys/windows/vs/NetHackW/NetHackW.vcxproj index a3d74baa4..df43cdd0c 100644 --- a/sys/windows/vs/NetHackW/NetHackW.vcxproj +++ b/sys/windows/vs/NetHackW/NetHackW.vcxproj @@ -146,6 +146,7 @@ + From c016367d8c95daeff4e1aa41c1704711503541ab Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 11 Jan 2026 10:11:30 -0500 Subject: [PATCH 192/442] an old comment typo --- include/artilist.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/artilist.h b/include/artilist.h index b5dce04f8..0dd2181a8 100644 --- a/include/artilist.h +++ b/include/artilist.h @@ -197,7 +197,7 @@ static NEARDATA struct artifact artilist[] = { * or the shriek that shrieked he, * As I gnashed my teeth, and from my sheath * I drew my Snickersnee! - * --Koko, Lord high executioner of Titipu + * --Ko-Ko, Lord high executioner of Titipu * (From Sir W.S. Gilbert's "The Mikado") */ A("Snickersnee", KATANA, SPFX_RESTR, 0, 0, PHYS(0, 8), NO_DFNS, NO_CARY, From 6459d44461b5c23f379bb466fb3fe28c0b219c85 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 11 Jan 2026 11:31:34 -0500 Subject: [PATCH 193/442] some spelling and inconsistency corrections; comments and elsewhere --- doc/Guidebook.mn | 6 +++--- doc/Guidebook.tex | 6 +++--- include/config.h | 9 +++++---- include/dgn_file.h | 2 +- include/dungeon.h | 2 +- include/flag.h | 2 +- include/hack.h | 4 ++-- include/mfndpos.h | 4 ++-- include/monattk.h | 4 ++-- include/mondata.h | 2 +- include/monflag.h | 4 ++-- include/objclass.h | 2 +- include/quest.h | 2 +- include/region.h | 2 +- include/rm.h | 4 ++-- include/trap.h | 2 +- src/hacklib.c | 4 ++-- 17 files changed, 31 insertions(+), 30 deletions(-) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index aa3c5dc2e..212f90d9f 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -47,7 +47,7 @@ .ds f0 \*(vr .ds f1 \" empty .\"DO NOT REMOVE NH_DATESUB .ds f2 Date(%B %-d, %Y) -.ds f2 December 03, 2025 +.ds f2 December 11, 2025 . .\" A note on some special characters: .\" \(lq = left double quote @@ -3537,7 +3537,7 @@ is a section marker (the closing \(oq\f(CR]\fP\(cq can be followed by whitespace and then an arbitrary comment beginning with \(oq\f(CR#\fP\(cq). The text between the square brackets is the section name. Section markers are only valid after a CHOOSE directive and their names -are case insensitive. +are case-insensitive. Lines after a section marker belong to that section up until another section starts or a marker without a name is encountered or the file ends. Lines within sections are ignored unless a CHOOSE directive has selected @@ -4671,7 +4671,7 @@ or you are making screenshots or streaming video. Using the .op statuslines:3 option is recommended so that there will be more room available for -status information, unless you're using nethack's \fIQt\fP interface +status information, unless you're using NetHack's \fIQt\fP interface or your terminal emulator window displays fewer than 25 lines. Persistent. .lp "silent " diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index bdc21ecc6..6c83cd3fd 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -48,7 +48,7 @@ \author{Original version - Eric S. Raymond\\ (Edited and expanded for 3.7.0 by Mike Stephenson and others)} %DO NOT REMOVE NH_DATESUB \date{Date(%B %-d, %Y)} -\date{December 03, 2025} +\date{December 11, 2025} \maketitle @@ -3855,7 +3855,7 @@ is a section marker (the closing `{\tt \verb+]+}' can be followed by whitespace and then an arbitrary comment beginning with `{\tt \#}'). The text between the square brackets is the section name. Section markers are only valid after a CHOOSE directive and their names -are case insensitive. +are case-insensitive. Lines after a section marker belong to that section up until another section starts or a marker without a name is encountered or the file ends. Lines within sections are ignored unless a CHOOSE directive has selected @@ -5114,7 +5114,7 @@ or you are making screenshots or streaming video. Using the {\it statuslines:3\/} option is recommended so that there will be more room available for -status information, unless you're using nethack's {\it Qt\/} interface +status information, unless you're using NetHack's {\it Qt\/} interface or your terminal emulator window displays fewer than 25 lines. Persistent. %.lp diff --git a/include/config.h b/include/config.h index 7069b268d..5016c6702 100644 --- a/include/config.h +++ b/include/config.h @@ -149,10 +149,11 @@ #ifdef X11_GRAPHICS /* - * There are two ways that X11 tiles may be defined. (1) using a custom - * format loaded by NetHack code, or (2) using the XPM format loaded by - * the free XPM library. The second option allows you to then use other - * programs to generate tiles files. For example, the PBMPlus tools + * There are two ways that X11 tiles may be defined: + * (1) using a custom format loaded by NetHack code. + * (2) using the XPM format loaded by the free XPM library. + * The second option allows you to then use other programs to + * generate tiles files. For example, the PBMPlus tools * would allow: * xpmtoppm x11tiles_big.xpm diff --git a/include/dgn_file.h b/include/dgn_file.h index 106de5655..4a94e0cdc 100644 --- a/include/dgn_file.h +++ b/include/dgn_file.h @@ -43,7 +43,7 @@ struct tmpbranch { }; /* - * Values for type for tmpbranch structure. + * Values for type in tmpbranch structure. */ #define TBR_STAIR 0 /* connection with both ends having a staircase */ #define TBR_NO_UP 1 /* connection with no up staircase */ diff --git a/include/dungeon.h b/include/dungeon.h index d4a1dcf62..f364bcd8e 100644 --- a/include/dungeon.h +++ b/include/dungeon.h @@ -169,7 +169,7 @@ struct linfo { /* 0x02 was FORGOTTEN, when amnesia made you forget maps */ #define LFILE_EXISTS 0x04 /* a level file exists for this level */ /* Note: VISITED and LFILE_EXISTS are currently almost always - * set at the same time. However they _mean_ different things. + * set at the same time. However, they _mean_ different things. */ }; diff --git a/include/flag.h b/include/flag.h index a566c04de..59ef02c04 100644 --- a/include/flag.h +++ b/include/flag.h @@ -482,7 +482,7 @@ struct instance_flags { }; /* - * Old deprecated names + * Old, deprecated names */ #ifdef TTY_GRAPHICS #define eight_bit_tty wc_eight_bit_input diff --git a/include/hack.h b/include/hack.h index 7163bf6a4..a52258a4e 100644 --- a/include/hack.h +++ b/include/hack.h @@ -781,7 +781,7 @@ struct selectionvar { /* structure for 'program_state'; not saved and restored */ struct sinfo { - int gameover; /* self explanatory? */ + int gameover; /* self-explanatory? */ int stopprint; /* inhibit further end of game disclosure */ #ifdef HANGUPHANDLING volatile int done_hup; /* SIGHUP or moral equivalent received @@ -1350,7 +1350,7 @@ typedef uint32_t mmflags_nht; /* makemon MM_ flags */ /* Macros for launching objects */ #define ROLL 0x01 /* the object is rolling */ -#define FLING 0x02 /* the object is flying thru the air */ +#define FLING 0x02 /* the object is flying through the air */ #define LAUNCH_UNSEEN 0x40 /* hero neither caused nor saw it */ #define LAUNCH_KNOWN 0x80 /* the hero caused this by explicit action */ diff --git a/include/mfndpos.h b/include/mfndpos.h index 9e560a311..a1ec75461 100644 --- a/include/mfndpos.h +++ b/include/mfndpos.h @@ -18,9 +18,9 @@ #define UNLOCKDOOR 0x00800000L /* unlocks locked doors */ #define BUSTDOOR 0x01000000L /* breaks any doors */ #define ALLOW_ROCK 0x02000000L /* pushes rocks */ -#define ALLOW_WALL 0x04000000L /* walks thru walls */ +#define ALLOW_WALL 0x04000000L /* walks through walls */ #define ALLOW_DIG 0x08000000L /* digs */ -#define ALLOW_BARS 0x10000000L /* may pass thru iron bars */ +#define ALLOW_BARS 0x10000000L /* may pass through iron bars */ #define ALLOW_SANCT 0x20000000L /* enters temples */ #define ALLOW_SSM 0x40000000L /* ignores scare monster */ #ifdef NHSTDC diff --git a/include/monattk.h b/include/monattk.h index 04fbef021..912d93284 100644 --- a/include/monattk.h +++ b/include/monattk.h @@ -53,7 +53,7 @@ #define AD_BLND 11 /* blinds (yellow light) */ #define AD_STUN 12 /* stuns */ #define AD_SLOW 13 /* slows */ -#define AD_PLYS 14 /* paralyses */ +#define AD_PLYS 14 /* paralyzes */ #define AD_DRLI 15 /* drains life levels (Vampire) */ #define AD_DREN 16 /* drains magic energy */ #define AD_LEGS 17 /* damages legs (xan) */ @@ -101,7 +101,7 @@ struct mhitm_data { }; /* - * Monster to monster attacks. When a monster attacks another (mattackm), + * Monster-to-monster attacks. When a monster attacks another (mattackm), * any or all of the following can be returned. See mattackm() for more * details. */ diff --git a/include/mondata.h b/include/mondata.h index 80a177f76..430b5e83d 100644 --- a/include/mondata.h +++ b/include/mondata.h @@ -173,7 +173,7 @@ as unique even though they really aren't; that's ok here */ #define unique_corpstat(ptr) (((ptr)->geno & G_UNIQ) != 0) -/* this returns the light's range, or 0 if none; if we add more light emitting +/* this returns the light's range, or 0 if none; if we add more light-emitting monsters, we'll likely have to add a new light range field to mons[] */ #define emits_light(ptr) \ (((ptr)->mlet == S_LIGHT || (ptr) == &mons[PM_FLAMING_SPHERE] \ diff --git a/include/monflag.h b/include/monflag.h index 43400fea9..18c2e4256 100644 --- a/include/monflag.h +++ b/include/monflag.h @@ -85,9 +85,9 @@ enum ms_sounds { #define M1_FLY 0x00000001L /* can fly or float */ #define M1_SWIM 0x00000002L /* can traverse water */ #define M1_AMORPHOUS 0x00000004L /* can flow under doors */ -#define M1_WALLWALK 0x00000008L /* can phase thru rock */ +#define M1_WALLWALK 0x00000008L /* can phase through rock */ #define M1_CLING 0x00000010L /* can cling to ceiling */ -#define M1_TUNNEL 0x00000020L /* can tunnel thru rock */ +#define M1_TUNNEL 0x00000020L /* can tunnel through rock */ #define M1_NEEDPICK 0x00000040L /* needs pick to tunnel */ #define M1_CONCEAL 0x00000080L /* hides under objects */ #define M1_HIDE 0x00000100L /* mimics, blends in with ceiling */ diff --git a/include/objclass.h b/include/objclass.h index b3da30b49..b04fa496a 100644 --- a/include/objclass.h +++ b/include/objclass.h @@ -146,7 +146,7 @@ enum objclass_syms { /* for mkobj() use ONLY! odd '-SPBOOK_CLASS' is in case of unsigned enums */ #define SPBOOK_no_NOVEL (0 - (int) SPBOOK_CLASS) -#define BURNING_OIL (MAXOCLASSES + 1) /* Can be used as input to explode. */ +#define BURNING_OIL (MAXOCLASSES + 1) /* Can be used as input to explode */ #define MON_EXPLODE (MAXOCLASSES + 2) /* Exploding monster (e.g. gas spore) */ #define TRAP_EXPLODE (MAXOCLASSES + 3) diff --git a/include/quest.h b/include/quest.h index 28d5ebeb1..77af3049f 100644 --- a/include/quest.h +++ b/include/quest.h @@ -14,7 +14,7 @@ struct q_score { /* Quest "scorecard" */ Bitfield(killed_leader, 1); /* killed the quest leader */ Bitfield(first_locate, 1); /* only set the first time */ - Bitfield(met_intermed, 1); /* used if the locate is a person. */ + Bitfield(met_intermed, 1); /* used if the locate is a person */ Bitfield(got_final, 1); /* got the final quest assignment */ Bitfield(made_goal, 3); /* # of times on goal level */ diff --git a/include/region.h b/include/region.h index c712173c1..b9b7ced43 100644 --- a/include/region.h +++ b/include/region.h @@ -56,7 +56,7 @@ typedef struct { /* Should probably do the same thing about objects */ - boolean visible; /* Is the region visible ? */ + boolean visible; /* Is the region visible? */ int glyph; /* Which glyph to use if visible */ anything arg; /* Optional user argument (Ex: strength of * force field, damage of a fire zone, ...*/ diff --git a/include/rm.h b/include/rm.h index 8ff3c3cea..1c9f49f14 100644 --- a/include/rm.h +++ b/include/rm.h @@ -12,8 +12,8 @@ * building on Don G Kneller's MS-DOS implementation. See drawing.c for * the code that permits the user to set the contents of the symbol structure. * - * The door representation was changed by Ari - * Huttunen(ahuttune@niksula.hut.fi) + * The door representation was changed by + * Ari Huttunen(ahuttune@niksula.hut.fi). */ /* diff --git a/include/trap.h b/include/trap.h index a0592f7e1..206951baa 100644 --- a/include/trap.h +++ b/include/trap.h @@ -26,7 +26,7 @@ struct trap { Bitfield(once, 1); Bitfield(madeby_u, 1); /* So monsters may take offence when you trap * them. Recognizing who made the trap isn't - * completely unreasonable, everybody has + * completely unreasonable; everybody has * their own style. This flag is also needed * when you untrap a monster. It would be too * easy to make a monster peaceful if you could diff --git a/src/hacklib.c b/src/hacklib.c index 209982751..50be4169b 100644 --- a/src/hacklib.c +++ b/src/hacklib.c @@ -711,7 +711,7 @@ online2(coordxy x0, coordxy y0, coordxy x1, coordxy y1) } #ifndef STRNCMPI -/* case insensitive counted string comparison */ +/* case-insensitive counted string comparison */ /*{ aka strncasecmp }*/ int strncmpi( @@ -735,7 +735,7 @@ strncmpi( #endif /* STRNCMPI */ #ifndef STRSTRI -/* case insensitive substring search */ +/* case-insensitive substring search */ char * strstri(const char *str, const char *sub) { From fd9f8cfd6050d0b8304917e0a88ab7f4913e7332 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 11 Jan 2026 12:08:32 -0500 Subject: [PATCH 194/442] Update submodule lua to v5.4.8 --- submodules/lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/lua b/submodules/lua index 644318516..6e22fedb7 160000 --- a/submodules/lua +++ b/submodules/lua @@ -1 +1 @@ -Subproject commit 6443185167c77adcc8552a3fee7edab7895db1a9 +Subproject commit 6e22fedb74cf0c9b6656e9fce8b7331db847c605 From 6de59927a2c8e979d6b08cfe4245a9583620f569 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Sun, 11 Jan 2026 22:12:35 +0000 Subject: [PATCH 195/442] Vlad's throne rebalance This removes the container-cursing effect (which is too specific and too easy to work around) and replaces it with an effect that greases everything (largely but not entirely positive). --- src/apply.c | 2 ++ src/sit.c | 31 +++++++++++++++---------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/apply.c b/src/apply.c index e8f1a339b..c33b5d929 100644 --- a/src/apply.c +++ b/src/apply.c @@ -2583,6 +2583,8 @@ grease_ok(struct obj *obj) if (!obj) return GETOBJ_SUGGEST; + /* note: if changing the list of ungreasable objects, also change + special_throne_effect in sit.c */ if (obj->oclass == COIN_CLASS) return GETOBJ_EXCLUDE; diff --git a/src/sit.c b/src/sit.c index 62fc1f017..6ae51fdec 100644 --- a/src/sit.c +++ b/src/sit.c @@ -7,7 +7,6 @@ #include "artifact.h" staticfn void throne_sit_effect(void); -staticfn void rndcurse_inner(boolean); staticfn int lay_an_egg(void); /* take away the hero's money */ @@ -265,9 +264,20 @@ special_throne_effect(int effect) { } break; case 6: - /* containers become cursed */ - rndcurse_inner(TRUE); + { + /* grease hands and inventory + + Same rules for which items can be affected as grease_ok in apply.c */ + struct obj *otmp; + + pline("A greasy liquid sprays all over you!"); + for (otmp = gi.invent; otmp; otmp = otmp->nobj) + if (otmp->oclass != COIN_CLASS) + otmp->greased = 1; + make_glib(rn1(101, 100)); + update_inventory(); break; + } case 7: /* lose an intrinsic */ attrcurse(); @@ -313,7 +323,8 @@ special_throne_effect(int effect) { break; } case 11: - /* polymorph effect (not blocked by magic resistance) */ + /* polymorph effect (not blocked by magic resistance, but other things + that protect from polymorphs work) */ pline("This throne was not meant for those such as you!"); You_feel("a change coming over you."); polyself(POLY_NOFLAGS); @@ -552,12 +563,6 @@ dosit(void) /* curse a few inventory items at random! */ void rndcurse(void) -{ - rndcurse_inner(FALSE); -} - -staticfn void -rndcurse_inner(boolean prefer_containers) { int nobj = 0; int cnt, onum; @@ -579,13 +584,9 @@ rndcurse_inner(boolean prefer_containers) /* gold isn't subject to being cursed or blessed */ if (otmp->oclass == COIN_CLASS) continue; - if (prefer_containers && !otmp->cobj) - continue; nobj++; } cnt = rnd(6 / ((!!Antimagic) + (!!Half_spell_damage) + 1)); - if (prefer_containers) - cnt = nobj * 2; if (nobj) { for (; cnt > 0; cnt--) { onum = rnd(nobj); @@ -593,8 +594,6 @@ rndcurse_inner(boolean prefer_containers) /* as above */ if (otmp->oclass == COIN_CLASS) continue; - if (prefer_containers && !otmp->cobj) - continue; if (--onum == 0) break; /* found the target */ } From ceaffcbd47845ce933ad2a83e370f249fbd8761c Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Sun, 11 Jan 2026 22:19:43 +0000 Subject: [PATCH 196/442] Large polyforms unequip cloaks rather than destroying them This commit is intended to achieve two things: a) making Vlad's throne a little less dangerous, b) making polymorph traps less dangerous for players who don't have magic resistance. (At present, uncontrolled polymorph is too dangerous in the late game for most players to consider risking it, so they take care to avoid things that have even a small chance of it, cutting out a lot of potential strategies. Toning down polymorphs should make them happen more often by increasing the chance that players will be willing to go without precautions.) Body armor is still destroyed (with the existing exception of dragon armor), in order to mostly preserve the balance effect of polymorph traps in the early game. --- doc/fixes3-7-0.txt | 1 + src/polyself.c | 12 +++--------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index a85c925f7..c86f77a40 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1565,6 +1565,7 @@ scroll of enchant armor formula has changed (in particular, magical armor now uncursed scrolls can sometimes enchant by more than one point) dragonhide can rot throwing ammo without a launcher produces a message +polymorphing into a large polyform unequips rather than destroying cloaks Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/polyself.c b/src/polyself.c index 14fe3f1cc..a990a4d5f 100644 --- a/src/polyself.c +++ b/src/polyself.c @@ -1172,15 +1172,9 @@ break_armor(void) if ((otmp = uarmc) != 0 /* mummy wrapping adapts to small and very big sizes */ && (otmp->otyp != MUMMY_WRAPPING || !WrappingAllowed(uptr))) { - if (otmp->oartifact) { - Your("%s falls off!", cloak_simple_name(otmp)); - (void) Cloak_off(); - dropp(otmp); - } else { - Your("%s tears apart!", cloak_simple_name(otmp)); - (void) Cloak_off(); - useup(otmp); - } + pline_The("clasp on your %s breaks open!", cloak_simple_name(otmp)); + (void) Cloak_off(); + dropp(otmp); } if (uarmu) { Your("shirt rips to shreds!"); From 904e3ffe67c29568153223ccbf6983c13e1796b8 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Mon, 12 Jan 2026 00:52:24 +0000 Subject: [PATCH 197/442] Clarify in the #quit prompt that it doesn't save the game --- doc/fixes3-7-0.txt | 1 + src/end.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index c86f77a40..0745178c1 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1566,6 +1566,7 @@ scroll of enchant armor formula has changed (in particular, magical armor now dragonhide can rot throwing ammo without a launcher produces a message polymorphing into a large polyform unequips rather than destroying cloaks +clarify in the #quit message that it doesn't save the game Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/end.c b/src/end.c index f7f61020e..a4ed7fc0d 100644 --- a/src/end.c +++ b/src/end.c @@ -98,7 +98,8 @@ done2(void) && y_n("Switch from the tutorial back to regular play?") == 'y') abandon_tutorial = TRUE; - if (abandon_tutorial || !paranoid_query(ParanoidQuit, "Really quit?")) { + if (abandon_tutorial || !paranoid_query( + ParanoidQuit, "Really quit without saving?")) { #ifndef NO_SIGNAL (void) signal(SIGINT, (SIG_RET_TYPE) done1); #endif From c62d76b776d0a037c24ae2a09c51a87861efa827 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 11 Jan 2026 21:12:25 -0500 Subject: [PATCH 198/442] more spelling and inconsistency corrections; comments and elsewhere --- src/spell.c | 16 ++++++++-------- src/steal.c | 2 +- src/steed.c | 4 ++-- src/teleport.c | 4 ++-- src/timeout.c | 16 ++++++++-------- src/topten.c | 2 +- src/trap.c | 11 ++++++----- src/u_init.c | 6 +++--- src/uhitm.c | 6 +++--- src/vision.c | 15 +++++++-------- src/weapon.c | 6 +++--- src/wield.c | 4 ++-- src/wizard.c | 6 +++--- src/worm.c | 16 ++++++++-------- src/worn.c | 4 ++-- src/write.c | 4 ++-- src/zap.c | 14 +++++++------- 17 files changed, 68 insertions(+), 68 deletions(-) diff --git a/src/spell.c b/src/spell.c index 2626de50c..889f624ba 100644 --- a/src/spell.c +++ b/src/spell.c @@ -100,7 +100,7 @@ staticfn void propagate_chain_lightning(struct chain_lightning_queue *, * Fighters find body armour & shield a little less limiting. * Headgear, Gauntlets and Footwear are not role-specific (but * still have an effect, except helm of brilliance, which is designed - * to permit magic-use). + * to permit magic use). */ #define uarmhbon 4 /* Metal helmets interfere with the mind */ @@ -191,7 +191,7 @@ confused_book(struct obj *spellbook) boolean gone = FALSE; if (!rn2(3) && spellbook->otyp != SPE_BOOK_OF_THE_DEAD) { - spellbook->in_use = TRUE; /* in case called from learn */ + spellbook->in_use = TRUE; /* in case called from learn() */ pline( "Being confused you have difficulties in controlling your actions."); display_nhwindow(WIN_MESSAGE, FALSE); @@ -225,7 +225,7 @@ deadbook_pacify_undead(struct monst *mtmp) } } -/* special effects for The Book of the Dead; reading it while blind is +/* special effects for the Book of the Dead; reading it while blind is allowed so that needs to be taken into account too */ staticfn void deadbook(struct obj *book2) @@ -2084,8 +2084,8 @@ dospellmenu( * The correct spacing of the columns when not using * tab separation depends on the following: * (1) that the font is monospaced, and - * (2) that selection letters are pre-pended to the - * given string and are of the form "a - ". + * (2) that selection letters are prepended to the + * given string and are of the form "a - ". * For SPELLMENU_DUMP, (2) is untrue, so four spaces * need to be subtracted. */ @@ -2163,7 +2163,7 @@ staticfn int percent_success(int spell) { /* Intrinsic and learned ability are combined to calculate - * the probability of player's success at cast a given spell. + * the probability of player's success at casting a given spell. */ int chance, splcaster, special, statused; int difficulty; @@ -2214,13 +2214,13 @@ percent_success(int spell) /* Calculate learned ability */ - /* Players basic likelihood of being able to cast any spell + /* The player's basic likelihood of being able to cast any spell * is based of their `magic' statistic. (Int or Wis) */ chance = 11 * statused / 2; /* - * High level spells are harder. Easier for higher level casters. + * High-level spells are harder. Easier for higher-level casters. * The difficulty is based on the hero's level and their skill level * in that spell type. */ diff --git a/src/steal.c b/src/steal.c index a5f771291..fd53ff1b7 100644 --- a/src/steal.c +++ b/src/steal.c @@ -643,7 +643,7 @@ mpickobj(struct monst *mtmp, struct obj *otmp) } /* don't want hidden light source inside the monster; assumes that engulfers won't have external inventories; whirly monsters cause - the light to be extinguished rather than letting it shine thru */ + the light to be extinguished rather than letting it shine through */ if (obj_sheds_light(otmp) && attacktype(mtmp->data, AT_ENGL)) { /* this is probably a burning object that you dropped or threw */ if (engulfing_u(mtmp) && !Blind) diff --git a/src/steed.c b/src/steed.c index 7b28794ae..274fd779d 100644 --- a/src/steed.c +++ b/src/steed.c @@ -213,8 +213,8 @@ mount_steed( pline("Maybe you should find a designated driver."); return (FALSE); } - /* While riding Wounded_legs refers to the steed's, - * not the hero's legs. + /* While riding, Wounded_legs refers to the steed's + * legs, not the hero's legs. * That opens up a potential abuse where the player * can mount a steed, then dismount immediately to * heal leg damage, because leg damage is always diff --git a/src/teleport.c b/src/teleport.c index 3c3d6df3e..535970dce 100644 --- a/src/teleport.c +++ b/src/teleport.c @@ -462,7 +462,7 @@ teleds(coordxy nux, coordxy nuy, int teleds_flags) * otherwise they are teleporting, so unplacebc(). * If they don't have to move the ball, then always "drag" whether or * not allow_drag is true, because we are calling that function, not - * to drag, but to move the chain. *However* there are some dumb + * to drag, but to move the chain. *However*, there are some dumb * special cases: * 0 0 * _X move east -----> X_ @@ -1353,7 +1353,7 @@ level_tele(void) d_level lsav; /* set specific death location; this also suppresses bones */ - lsav = u.uz; /* save current level, see below */ + lsav = u.uz; /* save current level; see below */ u.uz.dnum = 0; /* main dungeon */ u.uz.dlevel = (newlev <= -10) ? -10 : 0; /* heaven or surface */ done(DIED); diff --git a/src/timeout.c b/src/timeout.c index 1b85d5398..a8e5606b4 100644 --- a/src/timeout.c +++ b/src/timeout.c @@ -1078,7 +1078,7 @@ hatch_egg(anything *arg, long timeout) * mind are: * + Create the hatched monster then place it on the migrating * mons list. This is tough because all makemon() is made - * to place the monster as well. Makemon() also doesn't lend + * to place the monster as well. Makemon() also doesn't lend * itself well to splitting off a "not yet placed" subroutine. * + Mark the egg as hatched, then place the monster when we * place the migrating objects. @@ -1232,7 +1232,7 @@ slip_or_trip(void) if (otmp && on_foot) { /* trip over something in particular */ /* If there is only one item, it will have just been named - during the move, so refer to by via pronoun; otherwise, + during the move, so refer to it by pronoun; otherwise, if the top item has been or can be seen, refer to it by name; if not, look for rocks to trip over; trip over anonymous "something" if there aren't any rocks. @@ -1532,7 +1532,7 @@ burn_object(anything *arg, long timeout) default: /* * Someone added fuel to the lamp while it was - * lit. Just fall through and let begin burn + * lit. Just fall through and let begin_burn() * handle the new age. */ break; @@ -1659,7 +1659,7 @@ burn_object(anything *arg, long timeout) default: /* * Someone added fuel (candles) to the menorah while - * it was lit. Just fall through and let begin burn + * it was lit. Just fall through and let begin_burn() * handle the new age. */ break; @@ -1875,7 +1875,7 @@ do_storms(void) } if (levl[u.ux][u.uy].typ == CLOUD) { - /* Inside a cloud during a thunder storm is deafening. */ + /* Inside a cloud during a thunderstorm is deafening. */ /* Even if already deaf, we sense the thunder's vibrations. */ Soundeffect(se_kaboom_boom_boom, 80); pline("Kaboom!!! Boom!! Boom!!"); @@ -1925,7 +1925,7 @@ do_storms(void) * Save all timers of range 'range'. Range is either global * or local. Global timers follow game play, local timers * are saved with a level. Object and monster timers are - * saved using their respective id's instead of pointers. + * saved using their respective ids instead of pointers. * * void restore_timers(NHFILE *, int range, long adjust) * Restore timers of range 'range'. If from a ghost pile, @@ -2217,7 +2217,7 @@ run_timers(void) /* * Always use the first element. Elements may be added or deleted at - * any time. The list is ordered, we are done when the first element + * any time. The list is ordered; we are done when the first element * is in the future. */ while (gt.timer_base && gt.timer_base->timeout <= svm.moves) { @@ -2653,7 +2653,7 @@ maybe_write_timer(NHFILE *nhfp, int range, boolean write_it) * + timeouts that follow obj & monst that are migrating * * Level range: - * + timeouts that are level specific (e.g. storms) + * + timeouts that are level-specific (e.g. storms) * + timeouts that stay with the level (obj & monst) */ void diff --git a/src/topten.c b/src/topten.c index d6567ea08..f7770791a 100644 --- a/src/topten.c +++ b/src/topten.c @@ -16,7 +16,7 @@ /* * Updating in place can leave junk at the end of the file in some - * circumstances (if it shrinks and the O.S. doesn't have a straightforward + * circumstances (if it shrinks and the OS doesn't have a straightforward * way to truncate it). The trailing junk is harmless and the code * which reads the scores will ignore it. */ diff --git a/src/trap.c b/src/trap.c index 41f2cad94..d69ad9fc6 100644 --- a/src/trap.c +++ b/src/trap.c @@ -548,7 +548,7 @@ maketrap(coordxy x, coordxy y, int typ) /* * some cases which can happen when digging - * down while phazing thru solid areas + * down while phasing thru solid areas */ } else if (lev->typ == STONE || lev->typ == SCORR) { (void) set_levltyp(x, y, CORR); @@ -2125,7 +2125,7 @@ trapeffect_web( /* time will be adjusted below */ set_utrap(1, TT_WEB); - /* Time stuck in the web depends on your/steed strength. */ + /* Time stuck in the web depends on your/steed's strength. */ { int tim, str = ACURR(A_STR); @@ -2491,8 +2491,8 @@ trapeffect_landmine( already_seen ? " land mine" : "it"); } else { /* prevent landmine from killing steed, throwing you to - * the ground, and you being affected again by the same - * mine because it hasn't been deleted yet + * the ground, and then that same landmine affecting you + * again because it hasn't been deleted yet */ static boolean recursive_mine = FALSE; @@ -3228,7 +3228,8 @@ launch_obj( newsym(x1, y1); /* in case you're using a pick-axe to chop the boulder that's being launched (perhaps a monster triggered it), destroy context so that - next dig attempt never thinks you're resuming previous effort */ + the next dig attempt never thinks that you're resuming + the previous effort */ if ((otyp == BOULDER || otyp == STATUE) && singleobj->ox == svc.context.digging.pos.x && singleobj->oy == svc.context.digging.pos.y) diff --git a/src/u_init.c b/src/u_init.c index 2ce7461bb..1e6ac0ff3 100644 --- a/src/u_init.c +++ b/src/u_init.c @@ -1012,7 +1012,7 @@ u_init_misc(void) /* * For now, everyone starts out with a night vision range of 1 and - * their xray range disabled. + * their xray_range disabled. */ u.nv_range = 1; u.xray_range = -1; @@ -1314,8 +1314,8 @@ ini_inv(const struct trobj *trop) } else { /* UNDEF_TYP */ obj = ini_inv_mkobj_filter(trop->trclass, got_sp1); otyp = obj->otyp; - /* Heavily relies on the fact that 1) we create wands - * before rings, 2) that we create rings before + /* Heavily relies on the facts that 1) we create wands + * before rings, that 2) we create rings before * spellbooks, and that 3) not more than 1 object of a * particular symbol is to be prohibited. (For more * objects, we need more nocreate variables...) diff --git a/src/uhitm.c b/src/uhitm.c index 954a0f1ea..8b00bcdc0 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -198,7 +198,7 @@ attack_checks( return FALSE; if (svc.context.forcefight) { - /* Do this in the caller, after we checked that the monster + /* Do this in the caller, after we have checked that the monster * didn't die from the blow. Reason: putting the 'I' there * causes the hero to forget the square's contents since * both 'I' and remembered contents are stored in .glyph. @@ -1027,7 +1027,7 @@ hmon_hitmon_weapon_melee( && (is_ammo(obj) || is_missile(obj))) { if (ammo_and_launcher(obj, uwep)) { /* elves and samurai do extra damage using their own - bows with own arrows; they're highly trained */ + bows with their own arrows; they're highly trained */ if (Role_if(PM_SAMURAI) && obj->otyp == YA && uwep->otyp == YUMI) hmd->dmg++; @@ -1823,7 +1823,7 @@ hmon_hitmon( mon->mhp -= hmd.dmg; } /* adjustments might have made tmp become less than what - a level draining artifact has already done to max HP */ + a level-draining artifact has already done to max HP */ if (mon->mhp > mon->mhpmax) mon->mhp = mon->mhpmax; if (mon->mx == 0) { diff --git a/src/vision.c b/src/vision.c index 3f33d726c..709f54d02 100644 --- a/src/vision.c +++ b/src/vision.c @@ -399,7 +399,7 @@ staticfn int new_angle(struct rm *, unsigned char *, int, int); * this is good enough. * * + When this function is called we don't have all of the seen - * information (we're doing a top down scan in vision_recalc). + * information (we're doing a top-down scan in vision_recalc). * We would need to scan once to set all IN_SIGHT and COULD_SEE * bits, then again to correctly set the seenv bits. * + I'm trying to make this as cheap as possible. The display @@ -561,7 +561,7 @@ vision_recalc(int control) /* * Our own version of the update loop below. We know we can't see - * anything, so we only need update positions we used to be able + * anything, so we only need to update positions we used to be able * to see. */ temp_array = gv.viz_array; /* set gv.viz_array so newsym() will work */ @@ -588,7 +588,7 @@ vision_recalc(int control) if (Underwater && !Is_waterlevel(&u.uz)) { /* - * The hero is under water. Only see surrounding locations if + * The hero is underwater. Only see surrounding locations if * they are also underwater. This overrides night vision but * does not override x-ray vision. */ @@ -714,7 +714,7 @@ vision_recalc(int control) * + Set the IN_SIGHT bit for places that we could see and are lit. * + Reset changed places. * - * There is one thing that make deciding what the hero can see + * There is one thing that makes deciding what the hero can see * difficult: * * 1. Directional lighting. Items that block light create problems. @@ -761,10 +761,9 @@ vision_recalc(int control) || IS_WALL(lev->typ)) && !viz_clear[row][col]) { /* * Make sure doors, walls, boulders or mimics don't show - * up - * at the end of dark hallways. We do this by checking + * up at the end of dark hallways. We do this by checking * the adjacent position. If it is lit, then we can see - * the door or wall, otherwise we can't. + * the door or wall; otherwise we can't. */ dx = u.ux - col; dx = sign(dx); @@ -1786,7 +1785,7 @@ right_side( * * Otherwise, we know we can see the right edge of the current row. * - * This must be a strict less than so that we can always see a + * This must be a strict "less than" so that we can always see a * horizontal wall, even if it is adjacent to us. */ if (right_mark < right_edge) { diff --git a/src/weapon.c b/src/weapon.c index 317fd7c08..bd21b93b2 100644 --- a/src/weapon.c +++ b/src/weapon.c @@ -155,7 +155,7 @@ hitval(struct obj *otmp, struct monst *mon) if (Is_weapon) tmp += otmp->spe; - /* Put weapon specific "to hit" bonuses in below: */ + /* Put weapon-specific "to hit" bonuses in below: */ tmp += objects[otmp->otyp].oc_hitbon; /* Put weapon vs. monster type "to hit" bonuses in below: */ @@ -302,7 +302,7 @@ dmgval(struct obj *otmp, struct monst *mon) } if (objects[otyp].oc_material <= LEATHER && thick_skinned(ptr)) - /* thick skinned/scaled creatures don't feel it */ + /* thick-skinned or scaled creatures don't feel it */ tmp = 0; if (ptr == &mons[PM_SHADE] && !shade_glare(otmp)) tmp = 0; @@ -1387,7 +1387,7 @@ enhance_weapon_skill(void) n = selected[0].item.a_int - 1; /* get item selected */ free((genericptr_t) selected); skill_advance(n); - /* check for more skills able to advance, if so then .. */ + /* check for more skills able to advance; if so, then... */ for (n = i = 0; i < P_NUM_SKILLS; i++) { if (can_advance(i, speedy)) { if (!speedy) diff --git a/src/wield.c b/src/wield.c index f24ae8e1e..7b24b3df7 100644 --- a/src/wield.c +++ b/src/wield.c @@ -86,7 +86,7 @@ static const char * 1. Initializing the slot during character generation or a * restore. * 2. Setting the slot due to a player's actions. - * 3. If one of the objects in the slot are split off, these + * 3. If one of the objects in the slot is split off, these * functions can be used to put the remainder back in the slot. * 4. Putting an item that was thrown and returned back into the slot. * 5. Emptying the slot, by passing a null object. NEVER pass @@ -1021,7 +1021,7 @@ chwepon(struct obj *otmp, int amount) /* * Enchantment, which normally improves a weapon, has an - * addition adverse reaction on Magicbane whose effects are + * additional adverse reaction on Magicbane whose effects are * spe dependent. Give an obscure clue here. */ if (u_wield_art(ART_MAGICBANE) && uwep->spe >= 0) { diff --git a/src/wizard.c b/src/wizard.c index 2f4b0bdb9..674b4db6e 100644 --- a/src/wizard.c +++ b/src/wizard.c @@ -631,7 +631,7 @@ nasty(struct monst *summoner) * randomized so it won't always do so. */ for (j = 0; j < 20; j++) { - /* Don't create more spellcasters of the monsters' level or + /* Don't create more spellcasters of the monster's level or * higher--avoids chain summoners filling up the level. */ trylimit = 10 + 1; /* 10 tries */ @@ -704,7 +704,7 @@ nasty(struct monst *summoner) return count; } -/* Let's resurrect the wizard, for some unexpected fun. */ +/* Let's resurrect the Wizard, for some unexpected fun. */ void resurrect(void) { @@ -744,7 +744,7 @@ resurrect(void) if (!mtmp->mx) mtmp = 0; /* note: there might be a second Wizard; if so, - he'll have to wait til the next resurrection */ + he'll have to wait until the next resurrection */ break; } } diff --git a/src/worm.c b/src/worm.c index e2bb61c3b..d687389da 100644 --- a/src/worm.c +++ b/src/worm.c @@ -31,7 +31,7 @@ staticfn struct wseg *create_worm_tail(int); /* may return NULL */ * If wormno == 0 this does not mean that the monster is not a worm, * it just means that the monster does not have a long worm tail. * - * The actual segments of a worm are not full blown monst structs. + * The actual segments of a worm are not full0blown monst structs. * They are small wseg structs, and their position in the levels.monsters[][] * array is held by the monst struct of the head of the worm. This makes * things like probing and hit point bookkeeping much easier. @@ -46,7 +46,7 @@ staticfn struct wseg *create_worm_tail(int); /* may return NULL */ * wheads: The last (end) of a linked list of segments. This points to * the segment that is at the same position as the real monster * (the head). Note that the segment that wheads[wormno] points - * to, is not displayed. It is simply there to keep track of + * to is not displayed. It is simply there to keep track of * where the head came from, so that worm movement and display * are simplified later. * Keeping the head segment of the worm at the end of the list @@ -113,8 +113,8 @@ get_wormno(void) * Initialize the worm entry. This will set up the worm grow time, and * create and initialize the dummy segment for wheads[] and wtails[]. * - * If the worm has no tail (ie get_wormno() fails) then this function need - * not be called. + * If the worm has no tail (ie get_wormno() fails) then this function + * need not be called. */ void initworm(struct monst *worm, int wseg_count) @@ -282,7 +282,7 @@ worm_move(struct monst *worm) * * Check for mon->wormno before calling this function! * - * The worm don't move so it should shrink. + * The worm doesn't move, so it should shrink. */ void worm_nomove(struct monst *worm) @@ -379,7 +379,7 @@ cutworm(struct monst *worm, coordxy x, coordxy y, int cut_chance, new_wnum; if (!wnum) - return; /* bullet proofing */ + return; /* bullet-proofing */ if (x == worm->mx && y == worm->my) return; /* hit on head */ @@ -420,7 +420,7 @@ cutworm(struct monst *worm, coordxy x, coordxy y, /* * At this point, the old worm is correct. Any new worm will have - * it's head at "curr" and its tail at "new_tail". The old worm + * its head at "curr" and its tail at "new_tail". The old worm * must be at least level 3 in order to produce a new worm. */ new_worm = 0; @@ -804,7 +804,7 @@ random_dir(int x, int y, int *nx, int *ny) { *nx = x + (x > 1 /* extreme left ? */ ? (x < COLNO - 1 /* extreme right ? */ - ? (rn2(3) - 1) /* neither so +1, 0, or -1 */ + ? (rn2(3) - 1) /* neither, so +1, 0, or -1 */ : -rn2(2)) /* right edge, use -1 or 0 */ : rn2(2)); /* left edge, use 0 or 1 */ if (*nx != x) /* if x has changed, do same thing with y */ diff --git a/src/worn.c b/src/worn.c index e3464d04a..176a7793c 100644 --- a/src/worn.c +++ b/src/worn.c @@ -487,7 +487,7 @@ mon_adjust_speed( switch (adjust) { case 2: mon->permspeed = MFAST; - give_msg = FALSE; /* special case monster creation */ + give_msg = FALSE; /* special-case monster creation */ break; case 1: if (mon->permspeed == MSLOW) @@ -980,7 +980,7 @@ m_dowear_type( } } update_mon_extrinsics(mon, best, TRUE, creation); - /* if couldn't see it but now can, or vice versa, */ + /* if couldn't see it but now can, or vice versa */ if (!creation && (sawmon ^ canseemon(mon))) { if (mon->minvis && !See_invisible) { pline("Suddenly you cannot see %s.", nambuf); diff --git a/src/write.c b/src/write.c index f90c13b06..d7a1eddb4 100644 --- a/src/write.c +++ b/src/write.c @@ -8,7 +8,7 @@ staticfn int write_ok(struct obj *) NO_NNARGS; staticfn char *new_book_description(int, char *) NONNULL NONNULLPTRS; /* - * returns basecost of a scroll or a spellbook + * returns base cost of a scroll or a spellbook */ staticfn int cost(struct obj *otmp) @@ -351,7 +351,7 @@ dowrite(struct obj *pen) return ECMD_TIME; } - /* useup old scroll / spellbook */ + /* use up old scroll / spellbook */ useup(paper); /* success */ diff --git a/src/zap.c b/src/zap.c index 779d8b32e..d3fdf72eb 100644 --- a/src/zap.c +++ b/src/zap.c @@ -1643,7 +1643,7 @@ do_osshock(struct obj *obj) go.obj_zapped = TRUE; if (gp.poly_zapped < 0) { - /* some may metamorphosize */ + /* some may metamorphose */ for (i = obj->quan; i; i--) if (!rn2(Luck + 45)) { gp.poly_zapped = objects[obj->otyp].oc_material; @@ -1714,7 +1714,7 @@ poly_obj(struct obj *obj, int id) if (obj->otyp == UNICORN_HORN && obj->degraded_horn) magic_obj = 0; /* Try up to 3 times to make the magic-or-not status of - the new item be the same as it was for the old one. */ + the new item the same as the old item. */ otmp = (struct obj *) 0; do { if (otmp) @@ -1736,7 +1736,7 @@ poly_obj(struct obj *obj, int id) /* preserve quantity */ otmp->quan = obj->quan; - /* preserve the shopkeepers (lack of) interest */ + /* preserve the shopkeeper's (lack of) interest */ otmp->no_charge = obj->no_charge; /* preserve inventory letter if in inventory */ if (obj_location == OBJ_INVENT) @@ -2005,7 +2005,7 @@ stone_to_flesh_obj(struct obj *obj) /* nonnull */ return 0; (void) get_obj_location(obj, &oox, &ooy, 0); - /* add more if stone objects are added.. */ + /* add more if stone objects are added... */ switch (objects[obj->otyp].oc_class) { case ROCK_CLASS: /* boulders and statues */ case TOOL_CLASS: /* figurines */ @@ -3490,7 +3490,7 @@ spell_damage_bonus( } /* - * Generate the to hit bonus for a spell. Based on the hero's skill in + * Generate the to-hit bonus for a spell. Based on the hero's skill in * spell class and dexterity. */ staticfn int @@ -3986,7 +3986,7 @@ bhit( through instead of stop so we call flash_hits_mon() directly rather than returning mtmp back to caller. That allows the flash to keep on going. Note that we - use mtmp->minvis not canspotmon() because it makes no + use mtmp->minvis, not canspotmon(), because it makes no difference whether hero can see the monster or not. */ if (mtmp->minvis) { obj->ox = u.ux, obj->oy = u.uy; @@ -4731,7 +4731,7 @@ dobuzz( int spell_type; int hdmgtype = Hallucination ? rn2(6) : damgtype; - /* if it's a Hero Spell then get its SPE_TYPE */ + /* if it's a hero spell then get its SPE_TYPE */ spell_type = is_hero_spell(type) ? SPE_MAGIC_MISSILE + damgtype : 0; if (u.uswallow) { From 8b280d6108ced3e676639025e131d0fcfe996768 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 11 Jan 2026 21:23:37 -0500 Subject: [PATCH 199/442] whitespace follow-up --- src/trap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/trap.c b/src/trap.c index d69ad9fc6..b68df92c6 100644 --- a/src/trap.c +++ b/src/trap.c @@ -2492,7 +2492,7 @@ trapeffect_landmine( } else { /* prevent landmine from killing steed, throwing you to * the ground, and then that same landmine affecting you - * again because it hasn't been deleted yet + * again because it hasn't been deleted yet */ static boolean recursive_mine = FALSE; From 9a26f9d37d9ac057accb8a8283c2681a2c9f5f5d Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 11 Jan 2026 21:28:15 -0500 Subject: [PATCH 200/442] new typo fix --- src/worm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/worm.c b/src/worm.c index d687389da..a74b81ac8 100644 --- a/src/worm.c +++ b/src/worm.c @@ -31,7 +31,7 @@ staticfn struct wseg *create_worm_tail(int); /* may return NULL */ * If wormno == 0 this does not mean that the monster is not a worm, * it just means that the monster does not have a long worm tail. * - * The actual segments of a worm are not full0blown monst structs. + * The actual segments of a worm are not full-blown monst structs. * They are small wseg structs, and their position in the levels.monsters[][] * array is held by the monst struct of the head of the worm. This makes * things like probing and hit point bookkeeping much easier. From 4546603eb5dd35a2ef96b5c8d11caaa550a64afa Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 12 Jan 2026 07:17:21 -0500 Subject: [PATCH 201/442] fixes3-7-0.txt catch-up Related to: 96394394b81967b0464133c11c9ba55258b70973 Fix comment typo c016367d8c95daeff4e1aa41c1704711503541ab an old comment typo 6459d44461b5c23f379bb466fb3fe28c0b219c85 some spelling and inconsistency corrections; comments and elsewhere c62d76b776d0a037c24ae2a09c51a87861efa827 more spelling and inconsistency corrections; comments and elsewhere 8b280d6108ced3e676639025e131d0fcfe996768 whitespace follow-up 9a26f9d37d9ac057accb8a8283c2681a2c9f5f5d new typo fix Reference: https://groups.google.com/g/rec.games.roguelike.nethack/c/F28kIo9HOsA/m/6IYW7YRXSJsJ --- doc/fixes3-7-0.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 0745178c1..055bf4054 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -3073,6 +3073,9 @@ split making pits on do_earthquake() into a separate function and update the WASM cross-compile to work with the current code (pr #1331 by guillaumebrunerie) add silver maces (pr #1405 by disperse) +applied several source comment spelling or grammar fixes that were linked + to an old rec.games.roguelike.nethack post from May 2006 + by Arthur J. O'Dwyer Code Cleanup and Reorganization From 9bd8fcf82abc5f0dcaa55e462894def35a374d42 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Mon, 12 Jan 2026 18:08:17 +0000 Subject: [PATCH 202/442] Cursed potions of invisibility remove intrinsic invisibility They still have their other current effects (aggravating monsters and granting temporary invisibility). This is mostly meant as a way of counteracting the "turn permanently invisible" effect of magic traps, for players who would rather their characters remained visible. Thanks to paxed for helping with this commit. --- doc/fixes3-7-0.txt | 1 + src/potion.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 055bf4054..1d7427bf7 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1567,6 +1567,7 @@ dragonhide can rot throwing ammo without a launcher produces a message polymorphing into a large polyform unequips rather than destroying cloaks clarify in the #quit message that it doesn't save the game +cursed potion of invisibility removes intrinsic invisibility Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/potion.c b/src/potion.c index ca7a2eaa5..072d33b29 100644 --- a/src/potion.c +++ b/src/potion.c @@ -827,6 +827,10 @@ peffect_invisibility(struct obj *otmp) if (otmp->cursed) { pline("For some reason, you feel your presence is known."); aggravate(); + + /* doing this gives temporary invisibility, but removes permanent + invisibility */ + HInvis &= ~FROMOUTSIDE; } } From c5f0e7642b97ae4cf18e637fb059df01c6179527 Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 12 Jan 2026 20:35:01 -0500 Subject: [PATCH 203/442] generated files updated --- Files | 62 +- doc/Guidebook.txt | 2556 ++++++++++++++++++++++----------------------- 2 files changed, 1309 insertions(+), 1309 deletions(-) diff --git a/Files b/Files index be6ec970a..7154e17c2 100644 --- a/Files +++ b/Files @@ -47,21 +47,21 @@ Val-strt.lua Wiz-fila.lua Wiz-filb.lua Wiz-goal.lua Wiz-loca.lua Wiz-strt.lua air.lua asmodeus.lua astral.lua baalz.lua bigrm-1.lua bigrm-2.lua bigrm-3.lua bigrm-4.lua bigrm-5.lua bigrm-6.lua bigrm-7.lua bigrm-8.lua bigrm-9.lua bigrm-10.lua -bigrm-11.lua bigrm-12.lua bogusmon.txt castle.lua cmdhelp -data.base dungeon.lua earth.lua engrave.txt epitaph.txt -fakewiz1.lua fakewiz2.lua fire.lua hellfill.lua help -hh history juiblex.lua keyhelp knox.lua -license luahelper medusa-1.lua medusa-2.lua medusa-3.lua -medusa-4.lua minefill.lua minend-1.lua minend-2.lua minend-3.lua -minetn-1.lua minetn-2.lua minetn-3.lua minetn-4.lua minetn-5.lua -minetn-6.lua minetn-7.lua nhcore.lua nhlib.lua opthelp -optmenu oracle.lua oracles.txt orcus.lua quest.lua -rumors.fal rumors.tru sanctum.lua soko1-1.lua soko1-2.lua -soko2-1.lua soko2-2.lua soko3-1.lua soko3-2.lua soko4-1.lua -soko4-2.lua symbols themerms.lua tower1.lua tower2.lua -tower3.lua tribute tut-1.lua tut-2.lua usagehlp -valley.lua water.lua wizard1.lua wizard2.lua wizard3.lua -wizhelp +bigrm-11.lua bigrm-12.lua bigrm-13.lua bogusmon.txt castle.lua +cmdhelp data.base dungeon.lua earth.lua engrave.txt +epitaph.txt fakewiz1.lua fakewiz2.lua fire.lua hellfill.lua +help hh history juiblex.lua keyhelp +knox.lua license luahelper medusa-1.lua medusa-2.lua +medusa-3.lua medusa-4.lua minefill.lua minend-1.lua minend-2.lua +minend-3.lua minetn-1.lua minetn-2.lua minetn-3.lua minetn-4.lua +minetn-5.lua minetn-6.lua minetn-7.lua nhcore.lua nhlib.lua +opthelp optmenu oracle.lua oracles.txt orcus.lua +quest.lua rumors.fal rumors.tru sanctum.lua soko1-1.lua +soko1-2.lua soko2-1.lua soko2-2.lua soko3-1.lua soko3-2.lua +soko4-1.lua soko4-2.lua symbols themerms.lua tower1.lua +tower2.lua tower3.lua tribute tut-1.lua tut-2.lua +usagehlp valley.lua water.lua wizard1.lua wizard2.lua +wizard3.lua wizhelp doc: (files for all versions) @@ -298,22 +298,22 @@ date.c dbridge.c decl.c detect.c dig.c display.c dlb.c do.c do_name.c do_wear.c dog.c dogmove.c dokick.c dothrow.c drawing.c dungeon.c eat.c end.c engrave.c exper.c explode.c extralev.c files.c fountain.c -getpos.c glyphs.c hack.c hacklib.c insight.c invent.c -isaac64.c light.c lock.c mail.c makemon.c mcastu.c -mdlib.c mhitm.c mhitu.c minion.c mklev.c mkmap.c -mkmaze.c mkobj.c mkroom.c mon.c mondata.c monmove.c -monst.c mplayer.c mthrowu.c muse.c music.c nhlobj.c -nhlsel.c nhlua.c nhmd4.c o_init.c objects.c objnam.c -options.c pager.c pickup.c pline.c polyself.c potion.c -pray.c priest.c quest.c questpgr.c read.c rect.c -region.c report.c restore.c rip.c rnd.c role.c -rumors.c save.c selvar.c sfbase.c sfstruct.c shk.c -shknam.c sit.c sounds.c sp_lev.c spell.c stairs.c -steal.c steed.c strutil.c symbols.c sys.c teleport.c -timeout.c topten.c track.c trap.c u_init.c uhitm.c -utf8map.c vault.c version.c vision.c weapon.c were.c -wield.c windows.c wizard.c wizcmds.c worm.c worn.c -write.c zap.c +getpos.c glyphs.c hack.c hacklib.c iactions.c insight.c +invent.c isaac64.c light.c lock.c mail.c makemon.c +mcastu.c mdlib.c mhitm.c mhitu.c minion.c mklev.c +mkmap.c mkmaze.c mkobj.c mkroom.c mon.c mondata.c +monmove.c monst.c mplayer.c mthrowu.c muse.c music.c +nhlobj.c nhlsel.c nhlua.c nhmd4.c o_init.c objects.c +objnam.c options.c pager.c pickup.c pline.c polyself.c +potion.c pray.c priest.c quest.c questpgr.c read.c +rect.c region.c report.c restore.c rip.c rnd.c +role.c rumors.c save.c selvar.c sfbase.c sfstruct.c +shk.c shknam.c sit.c sounds.c sp_lev.c spell.c +stairs.c steal.c steed.c strutil.c symbols.c sys.c +teleport.c timeout.c topten.c track.c trap.c u_init.c +uhitm.c utf8map.c vault.c version.c vision.c weapon.c +were.c wield.c windows.c wizard.c wizcmds.c worm.c +worn.c write.c zap.c submodules: (files in top directory) diff --git a/doc/Guidebook.txt b/doc/Guidebook.txt index d4a90d378..9c25c65e2 100644 --- a/doc/Guidebook.txt +++ b/doc/Guidebook.txt @@ -15,25 +15,25 @@ Original version - Eric S. Raymond (Edited and expanded for NetHack 3.7.0 by Mike Stephenson and others) - December 05, 2025 + December 11, 2025 1. Introduction Recently, you have begun to find yourself unfulfilled and distant - in your daily occupation. Strange dreams of prospecting, stealing, - crusading, and combat have haunted you in your sleep for many months, - but you aren't sure of the reason. You wonder whether you have in - fact been having those dreams all your life, and somehow managed to - forget about them until now. Some nights you awaken suddenly and cry - out, terrified at the vivid recollection of the strange and powerful - creatures that seem to be lurking behind every corner of the dungeon - in your dream. Could these details haunting your dreams be real? As + in your daily occupation. Strange dreams of prospecting, stealing, + crusading, and combat have haunted you in your sleep for many months, + but you aren't sure of the reason. You wonder whether you have in + fact been having those dreams all your life, and somehow managed to + forget about them until now. Some nights you awaken suddenly and cry + out, terrified at the vivid recollection of the strange and powerful + creatures that seem to be lurking behind every corner of the dungeon + in your dream. Could these details haunting your dreams be real? As each night passes, you feel the desire to enter the mysterious caverns near the ruins grow stronger. Each morning, however, you quickly put the idea out of your head as you recall the tales of those who entered - the caverns before you and did not return. Eventually you can resist + the caverns before you and did not return. Eventually you can resist the yearning to seek out the fantastic place in your dreams no longer. After all, when other adventurers came back this way after spending time in the caverns, they usually seemed better off than when they @@ -41,15 +41,15 @@ who did not return had not just kept going? Asking around, you hear about a bauble, called the Amulet of Yen- - dor by some, which, if you can find it, will bring you great wealth. - One legend you were told even mentioned that the one who finds the + dor by some, which, if you can find it, will bring you great wealth. + One legend you were told even mentioned that the one who finds the amulet will be granted immortality by the gods. The amulet is rumored to be somewhere beyond the Valley of Gehennom, deep within the Mazes of Menace. Upon hearing the legends, you immediately realize that there is some profound and undiscovered reason that you are to descend - into the caverns and seek out that amulet of which they spoke. Even - if the rumors of the amulet's powers are untrue, you decide that you - should at least be able to sell the tales of your adventures to the + into the caverns and seek out that amulet of which they spoke. Even + if the rumors of the amulet's powers are untrue, you decide that you + should at least be able to sell the tales of your adventures to the local minstrels for a tidy sum, especially if you encounter any of the terrifying and magical creatures of your dreams along the way. You spend one last night fortifying yourself at the local inn, becoming @@ -70,36 +70,36 @@ - ancient ruins that mark the entrance to the Mazes of Menace. It is - late at night, so you make camp at the entrance and spend the night - sleeping under the open skies. In the morning, you gather your gear, + ancient ruins that mark the entrance to the Mazes of Menace. It is + late at night, so you make camp at the entrance and spend the night + sleeping under the open skies. In the morning, you gather your gear, eat what may be your last meal outside, and enter the dungeon.... 2. What is going on here? - You have just begun a game of NetHack. Your goal is to grab as - much treasure as you can, retrieve the Amulet of Yendor, and escape + You have just begun a game of NetHack. Your goal is to grab as + much treasure as you can, retrieve the Amulet of Yendor, and escape the Mazes of Menace alive. - Your abilities and strengths for dealing with the hazards of + Your abilities and strengths for dealing with the hazards of adventure will vary with your background and training: - Archeologists understand dungeons pretty well; this enables them - to move quickly and sneak up on the local nasties. They start - equipped with the tools for a proper scientific expedition, and are + Archeologists understand dungeons pretty well; this enables them + to move quickly and sneak up on the local nasties. They start + equipped with the tools for a proper scientific expedition, and are able to read ancient languages. - Barbarians are warriors out of the hinterland, hardened to bat- - tle. They begin their quests with naught but uncommon strength, a + Barbarians are warriors out of the hinterland, hardened to bat- + tle. They begin their quests with naught but uncommon strength, a trusty hauberk, and a great two-handed sword. Cavemen and Cavewomen start with exceptional strength but, unfor- tunately, with neolithic weapons. Healers are wise in medicine and apothecary. They know the herbs - and simples that can restore vitality, ease pain, anesthetize, and - neutralize poisons; and with their instruments, they can divine a - being's state of health or sickness. Their medical practice earns + and simples that can restore vitality, ease pain, anesthetize, and + neutralize poisons; and with their instruments, they can divine a + being's state of health or sickness. Their medical practice earns them quite reasonable amounts of money, with which they enter the dun- geon. @@ -113,20 +113,20 @@ mobility. Priests and Priestesses are clerics militant, crusaders advancing - the cause of righteousness with arms, armor, and arts thaumaturgic. - Their ability to commune with deities via prayer occasionally extri- + the cause of righteousness with arms, armor, and arts thaumaturgic. + Their ability to commune with deities via prayer occasionally extri- cates them from peril, but can also put them in it. - Rangers are most at home in the woods, and some say slightly out - of place in a dungeon. They are, however, experts in archery as well + Rangers are most at home in the woods, and some say slightly out + of place in a dungeon. They are, however, experts in archery as well as tracking and stealthy movement. - Rogues are agile and stealthy thieves, with knowledge of locks, - traps, and poisons. Their advantage lies in surprise, which they + Rogues are agile and stealthy thieves, with knowledge of locks, + traps, and poisons. Their advantage lies in surprise, which they employ to great advantage. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -136,12 +136,12 @@ - Samurai are the elite warriors of feudal Nippon. They are - lightly armored and quick, and wear the dai-sho, two swords of the + Samurai are the elite warriors of feudal Nippon. They are + lightly armored and quick, and wear the dai-sho, two swords of the deadliest keenness. - Tourists start out with lots of gold (suitable for shopping - with), a credit card, lots of food, some maps, and an expensive cam- + Tourists start out with lots of gold (suitable for shopping + with), a credit card, lots of food, some maps, and an expensive cam- era. Most monsters don't like being photographed. Valkyries are hardy warrior women. Their upbringing in the harsh @@ -170,11 +170,11 @@ ground mine complex built by this race exists within the Mazes of Men- ace, filled with both riches and danger. - Humans are by far the most common race of the surface world, and - are thus the norm to which other races are often compared. Although + Humans are by far the most common race of the surface world, and + are thus the norm to which other races are often compared. Although they have no special abilities, they can succeed in any role. - Orcs are a cruel and barbaric race that hate every living thing + Orcs are a cruel and barbaric race that hate every living thing (including other orcs). Above all others, Orcs hate Elves with a pas- sion unequalled, and will go out of their way to kill one at any opportunity. The armor and weapons fashioned by the Orcs are typi- @@ -187,12 +187,12 @@ level, it appears on the screen in front of you. When NetHack's ancestor rogue first appeared, its screen orienta- - tion was almost unique among computer fantasy games. Since then, - screen orientation has become the norm rather than the exception; - NetHack continues this fine tradition. Unlike text adventure games + tion was almost unique among computer fantasy games. Since then, + screen orientation has become the norm rather than the exception; + NetHack continues this fine tradition. Unlike text adventure games - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -202,8 +202,8 @@ - that accept commands in pseudo-English sentences and explain the - results in words, NetHack commands are all one or two keystrokes and + that accept commands in pseudo-English sentences and explain the + results in words, NetHack commands are all one or two keystrokes and the results are displayed graphically on the screen. A minimum screen size of 24 lines by 80 columns is recommended; if the screen is larger, only a 21x80 section will be used for the map. @@ -212,21 +212,21 @@ of Braille readers or speech synthesisers. Instructions for configur- ing NetHack for the blind are included later in this document. - NetHack generates a new dungeon every time you play it; even the + NetHack generates a new dungeon every time you play it; even the authors still find it an entertaining and exciting game despite having won several times. NetHack offers a variety of display options. The options avail- able to you will vary from port to port, depending on the capabilities - of your hardware and software, and whether various compile-time + of your hardware and software, and whether various compile-time options were enabled when your executable was created. The three pos- sible display options are: a monochrome character interface, a color character interface, and a graphical interface using small pictures called tiles. The two character interfaces allow fonts with other characters to be substituted, but the default assignments use standard - ASCII characters to represent everything. There is no difference - between the various display options with respect to game play. - Because we cannot reproduce the tiles or colors in the Guidebook, and + ASCII characters to represent everything. There is no difference + between the various display options with respect to game play. + Because we cannot reproduce the tiles or colors in the Guidebook, and because it is common to all ports, we will use the default ASCII char- acters from the monochrome character display when referring to things you might see on the screen during your game. @@ -258,7 +258,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -268,22 +268,21 @@ - +----------------------------------------------------------------+ - | The bat bites! | - | | - | ------ | - | |....| ---------- | - | |.<..|####...@...$.| | - | |....-# |...B....+ | - | |....| |.d......| | - | ------ -------|-- | - | | - | | - | | - | Player the Rambler St:12 Dx:7 Co:18 In:11 Wi:9 Ch:15 Neutral | - | Dlvl:1 $:993 HP:9(12) Pw:3(3) AC:10 Exp:1/19 T:752 Hungry Conf | - +----------------------------------------------------------------+ - Figure 1 + +---------------------------------------------------------------+ + |The bat bites! | + | | + | ------ | + | |....| ---------- | + | |.<..|####...@...$.| | + | |....-# |...B....+ | + | |....| |.d......| | + | ------ -------|-- | + | | + | | + | | + |Player the Rambler St:12 Dx:7 Co:18 In:11 Wi:9 Ch:15 Neutral | + |Dlvl:1 $:993 HP:9(12) Pw:3(3) AC:10 Exp:1/19 T:752 Hungry Conf | + +---------------------------Figure-1----------------------------+ @@ -324,7 +323,8 @@ - NetHack 3.7.0 December 05, 2025 + + NetHack 3.7.0 December 11, 2025 @@ -334,18 +334,17 @@ - +----------------------------------------------------------------+ - | Player the Rambler St:12 Dx:7 Co:18 In:11 Wi:9 Ch:15 | - | Neutral $:993 HP:9(12) Pw:3(3) AC:10 Exp:1/19 Hungry | - | Dlvl:1 T:752 Conf | - +----------------------------------------------------------------+ - Figure 2 + +---------------------------------------------------------------+ + |Player the Rambler St:12 Dx:7 Co:18 In:11 Wi:9 Ch:15 | + |Neutral $:993 HP:9(12) Pw:3(3) AC:10 Exp:1/19 Hungry | + |Dlvl:1 T:752 Conf | + +---------------------------Figure-2----------------------------+ 3.1. The status lines (bottom) - The bottom two (or three) lines of the screen contain several - cryptic pieces of information describing your current status. Figure - 1 shows the traditional two-line status area below the map. Figure 2 + The bottom two (or three) lines of the screen contain several + cryptic pieces of information describing your current status. Figure + 1 shows the traditional two-line status area below the map. Figure 2 shows just the status area, when the statuslines:3 option has been set (not all interfaces support this option). If any status line becomes wider than the screen, you might not see all of it due to truncation. @@ -361,9 +360,9 @@ experience level, see below). Strength - A measure of your character's strength; one of your six basic - attributes. A human character's attributes can range from 3 to - 18 inclusive; non-humans may exceed these limits (occasionally + A measure of your character's strength; one of your six basic + attributes. A human character's attributes can range from 3 to + 18 inclusive; non-humans may exceed these limits (occasionally you may get super-strengths of the form 18/xx, and magic can also cause attributes to exceed the normal limits). The higher your strength, the stronger you are. Strength affects how success- @@ -382,15 +381,16 @@ your constitution no longer matters. Intelligence - Intelligence affects your ability to cast spells and read spell- + Intelligence affects your ability to cast spells and read spell- books. Wisdom - Wisdom comes from your practical experience (especially when + Wisdom comes from your practical experience (especially when dealing with magic). It affects your magical energy. - NetHack 3.7.0 December 05, 2025 + + NetHack 3.7.0 December 11, 2025 @@ -420,11 +420,11 @@ level. Gold - The number of gold pieces you are openly carrying. Gold which + The number of gold pieces you are openly carrying. Gold which you have concealed in containers is not counted. Hit Points - Your current and maximum hit points. Hit points indicate how + Your current and maximum hit points. Hit points indicate how much damage you can take before you die. The more you get hit in a fight, the lower they get. You can regain hit points by rest- ing, or by using certain magical items or spells. The number in @@ -437,26 +437,26 @@ Armor Class A measure of how effectively your armor stops blows from - unfriendly creatures. The lower this number is, the more effec- - tive the armor; it is quite possible to have negative armor + unfriendly creatures. The lower this number is, the more effec- + tive the armor; it is quite possible to have negative armor class. See the Armor subsection of Objects for more information. Experience - Your current experience level. If the showexp option is set, it + Your current experience level. If the showexp option is set, it will be followed by a slash and experience points. As you adven- ture, you gain experience points. At certain experience point totals, you gain an experience level. The more experienced you are, the better you fight and withstand magical attacks. (By the - time your level reaches double digits, the usefulness of showing - the points with it has dropped significantly. You can use the - `O' command to turn showexp off to avoid using up the limited + time your level reaches double digits, the usefulness of showing + the points with it has dropped significantly. You can use the + `O' command to turn showexp off to avoid using up the limited status line space.) Time - The number of turns elapsed so far, displayed if you have the + The number of turns elapsed so far, displayed if you have the - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -469,13 +469,13 @@ time option set. Status - Hunger: your current hunger status. Values are Satiated, Not - Hungry (or Normal), Hungry, Weak, and Fainting. Not shown when + Hunger: your current hunger status. Values are Satiated, Not + Hungry (or Normal), Hungry, Weak, and Fainting. Not shown when Normal. - Encumbrance: an indication of how what you are carrying affects - your ability to move. Values are Unencumbered, Burdened, - Stressed, Strained, Overtaxed, and Overloaded. Not shown when + Encumbrance: an indication of how what you are carrying affects + your ability to move. Values are Unencumbered, Burdened, + Stressed, Strained, Overtaxed, and Overloaded. Not shown when Unencumbered. Fatal conditions: Stone (aka Petrifying, turning to stone), Slime @@ -492,8 +492,8 @@ Other conditions and modifiers exist, but there isn't enough room to display them with the other status fields. - The #attributes command (default key ^X) will show all current status - information in unabbreviated format. It also shows other information + The #attributes command (default key ^X) will show all current status + information in unabbreviated format. It also shows other information which might be included on the status lines if those had more room. 3.2. The message line (top) @@ -513,16 +513,16 @@ The rest of the screen is the map of the level as you have explored it so far. Each symbol on the screen represents something. You can set various graphics options to change some of the symbols the - game uses; otherwise, the game will use default symbols. Here is a + game uses; otherwise, the game will use default symbols. Here is a list of what the default symbols mean: - - The horizontal or corner walls of a room, or an open east/west + - The horizontal or corner walls of a room, or an open east/west door. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -532,13 +532,13 @@ - | The vertical walls of a room, or an open north/south door, or a + | The vertical walls of a room, or an open north/south door, or a grave. - . The floor of a room, or ice, or a doorless doorway, or the span + . The floor of a room, or ice, or a doorless doorway, or the span of an open drawbridge. - # A corridor, or iron bars, or a tree, or the portcullis of a + # A corridor, or iron bars, or a tree, or the portcullis of a closed drawbridge. Note: engravings in corridors also appear as # but are shown in a @@ -588,7 +588,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -610,8 +610,8 @@ A-HJ-Z and @&':; - Letters and certain other symbols represent the various inhabi- - tants of the Mazes of Menace. Watch out, they can be nasty and + Letters and certain other symbols represent the various inhabi- + tants of the Mazes of Menace. Watch out, they can be nasty and vicious. Sometimes, however, they can be helpful. I Rather than a specific type of monster, this marks the last known @@ -619,18 +619,18 @@ the monster could have moved. The `s', `F', and `m' commands may be useful here. - 1-5 The digits 1 through 5 may be displayed, marking unseen monsters - sensed via the Warning attribute. Less dangerous monsters are + 1-5 The digits 1 through 5 may be displayed, marking unseen monsters + sensed via the Warning attribute. Less dangerous monsters are indicated by lower values, more dangerous by higher values. - You need not memorize all these symbols; you can ask the game - what any symbol represents with the `/' command (see the next section + You need not memorize all these symbols; you can ask the game + what any symbol represents with the `/' command (see the next section for more info). 4. Commands - Commands can be initiated by typing one or two characters to - which the command is bound to, or typing the command name in the + Commands can be initiated by typing one or two characters to + which the command is bound to, or typing the command name in the extended commands entry. Some commands, like "search", do not require that any more information be collected by NetHack. Other commands might require additional information, for example a direction, or an @@ -647,14 +647,14 @@ indicating that you may choose an object not on the list, if you wanted to use something unexpected. Typing a `*' lists your entire inventory, so you can see the inventory letters of every object you're - carrying. Finally, if you change your mind and decide you don't want - to do this command after all, you can press the ESC key to abort the + carrying. Finally, if you change your mind and decide you don't want + to do this command after all, you can press the ESC key to abort the command. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -664,7 +664,7 @@ - You can put a number before some commands to repeat them that + You can put a number before some commands to repeat them that many times; for example, "10s" will search ten times. If you have the number_pad option set, you must type `n' to prefix a count, so the example above would be typed "n10s" instead. Commands for which @@ -678,18 +678,18 @@ ? Help menu: display one of several help texts available. - / The "whatis" command, to tell what a symbol represents. You may - choose to specify a location or type a symbol (or even a whole - word) to explain. Specifying a location is done by moving the - cursor to a particular spot on the map and then pressing one of + / The "whatis" command, to tell what a symbol represents. You may + choose to specify a location or type a symbol (or even a whole + word) to explain. Specifying a location is done by moving the + cursor to a particular spot on the map and then pressing one of `.', `,', `;', or `:'. `.' will explain the symbol at the chosen location, conditionally check for "More info?" depending upon whether the help option is on, and then you will be asked to pick - another location; `,' will explain the symbol but skip any addi- - tional information, then let you pick another location; `;' will - skip additional info and also not bother asking you to choose - another location to examine; `:' will show additional info, if - any, without asking for confirmation. When picking a location, + another location; `,' will explain the symbol but skip any addi- + tional information, then let you pick another location; `;' will + skip additional info and also not bother asking you to choose + another location to examine; `:' will show additional info, if + any, without asking for confirmation. When picking a location, pressing the ESC key will terminate this command, or pressing `?' will give a brief reminder about how it works. @@ -720,7 +720,7 @@ instead. Only these one-step movement commands cause you to - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -731,15 +731,14 @@ fight monsters; the others (below) are "safe." - +-----------------------------------------------------+ - | y k u 7 8 9 | - | \ | / \ | / | - | h- . -l 4- . -6 | - | / | \ / | \ | - | b j n 1 2 3 | - | (number_pad off) (number_pad on) | - +-----------------------------------------------------+ - Figure 3 + +----------------------------------------------------------------+ + | y k u 7 8 9 | + | \ | / \ | / | + | h- . -l 4- . -6 | + | / | \ / | \ | + | b j n 1 2 3 | + | (number_pad off) (number_pad on) | + +---------------------------Figure-3-----------------------------+ [YUHJKLBN] Go in that direction until you hit a wall or run into something. @@ -756,20 +755,20 @@ that prompt). The prefix will make "#travel" command show a menu of interesting - targets in sight. It can also be used with the `\' (known, show + targets in sight. It can also be used with the `\' (known, show a list of all discovered objects) and the ``' (knownclass, show a list of discovered objects in a particular class) commands to offer a menu of several sorting alternatives (which sets a new value for the sortdiscoveries option); also for "#vanquished" and "#genocided" commands to offer a sorting menu. - A few other commands (eat food, offer sacrifice, apply tinning- - kit, drink/quaff, dip, tip container) use the `m' prefix to skip - checking for applicable objects on the floor and go straight to - checking inventory, or (for "#loot" to remove a saddle), skip + A few other commands (eat food, offer sacrifice, apply tinning- + kit, drink/quaff, dip, tip container) use the `m' prefix to skip + checking for applicable objects on the floor and go straight to + checking inventory, or (for "#loot" to remove a saddle), skip containers and go straight to adjacent monsters. - In debug mode (aka "wizard mode"), the `m' prefix may also be + In debug mode (aka "wizard mode"), the `m' prefix may also be used with the "#teleport" and "#wizlevelport" commands. F[yuhjklbn] @@ -779,14 +778,15 @@ Prefix: move until something interesting is found. G[yuhjklbn] or +[yuhjklbn] - Prefix: similar to `g', but forking of corridors is not consid- + Prefix: similar to `g', but forking of corridors is not consid- ered interesting. - Note: + means holding the or key - down like while typing and releasing , then releas- + Note: + means holding the or key + down like while typing and releasing , then releas- + ing . ^ is used as shorthand elsewhere in the - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -796,13 +796,12 @@ - ing . ^ is used as shorthand elsewhere in the - Guidebook to mean the same thing. Control characters are case- + Guidebook to mean the same thing. Control characters are case- insensitive so ^x and ^X are the same. M[yuhjklbn] - Old versions supported `M' as a movement prefix which combined - the effect of `m' with +. That is no longer + Old versions supported `M' as a movement prefix which combined + the effect of `m' with +. That is no longer supported as a prefix but similar effect can be achieved by using `m' and G in combination. m can also be used in com- bination with g, +, or @@ -819,17 +818,17 @@ location other than the current position. . Wait or rest, do nothing for one turn. Precede with the `m' pre- - fix to wait for a turn even next to a hostile monster, if + fix to wait for a turn even next to a hostile monster, if safe_wait is on. a Apply (use) a tool (pick-axe, key, lamp...). - If used on a wand, that wand will be broken, releasing its magic + If used on a wand, that wand will be broken, releasing its magic in the process. Confirmation is required. A Remove one or more worn items, such as armor. - Use `T' (take off) to take off only one piece of armor or `R' + Use `T' (take off) to take off only one piece of armor or `R' (remove) to take off only one accessory. ^A Repeat the previous command. @@ -852,7 +851,8 @@ - NetHack 3.7.0 December 05, 2025 + + NetHack 3.7.0 December 11, 2025 @@ -864,8 +864,8 @@ "What kinds of things do you want to drop? [!%= BUCXPaium]" - you should type zero or more object symbols possibly followed by - `a' and/or `i' and/or `u' and/or `m'. In addition, one or more + you should type zero or more object symbols possibly followed by + `a' and/or `i' and/or `u' and/or `m'. In addition, one or more of the blessed/uncursed/cursed groups may be typed. DB - drop all objects known to be blessed. @@ -879,16 +879,16 @@ Dm - use a menu to pick which object(s) to drop. D%u - drop only unpaid food. - The last example shows a combination. There are four categories + The last example shows a combination. There are four categories of object filtering: class (`!' for potions, `?' for scrolls, and so on), shop status (`u' for unpaid, in other words, owned by the shop), bless/curse state (`B', `U', `C', and `X' as shown above), and novelty (`P', recently picked up items; controlled by picking up or dropping things rather than by any time factor). - If you specify more than one value in a category (such as "!?" - for potions and scrolls or "BU" for blessed and uncursed), an - inventory object will meet the criteria if it matches any of the + If you specify more than one value in a category (such as "!?" + for potions and scrolls or "BU" for blessed and uncursed), an + inventory object will meet the criteria if it matches any of the specified values (so "!?" means `!' or `?'). If you specify more than one category, an inventory object must meet each of the cat- egory criteria (so "%u" means class `%' and unpaid `u'). Lastly, @@ -918,7 +918,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -932,14 +932,14 @@ attack you hand-to-hand (but if you attack, you will rub it out); this is often useful to give yourself a breather. - f Fire (shoot or throw) one of the objects placed in your quiver - (or quiver sack, or that you have at the ready). You may select - ammunition with a previous `Q' command, or let the computer pick - something appropriate if autoquiver is true. If your wielded - weapon has the throw-and-return property, your quiver is empty, - and autoquiver is false, you will throw that wielded weapon - instead of filling the quiver. This will also automatically use - a polearm if wielded. If fireassist is true, firing will auto- + f Fire (shoot or throw) one of the objects placed in your quiver + (or quiver sack, or that you have at the ready). You may select + ammunition with a previous `Q' command, or let the computer pick + something appropriate if autoquiver is true. If your wielded + weapon has the throw-and-return property, your quiver is empty, + and autoquiver is false, you will throw that wielded weapon + instead of filling the quiver. This will also automatically use + a polearm if wielded. If fireassist is true, firing will auto- matically try to wield a launcher (for example, a bow or a sling) matching the ammo in the quiver; this might take multiple turns, and get interrupted by a monster. Remember to swap back to your @@ -969,22 +969,22 @@ A menu showing the current option values will be displayed. You can change most values simply by selecting the menu entry for the - given option (ie, by typing its letter or clicking upon it, - depending on your user interface). For the non-boolean choices, - a further menu or prompt will appear once you've closed this - menu. The available options are listed later in this Guidebook. - Options are usually set before the game rather than with the `O' - command; see the section on options below. Precede `O' with the + given option (ie, by typing its letter or clicking upon it, + depending on your user interface). For the non-boolean choices, + a further menu or prompt will appear once you've closed this + menu. The available options are listed later in this Guidebook. + Options are usually set before the game rather than with the `O' + command; see the section on options below. Precede `O' with the `m' prefix to show advanced options. ^O Show overview. - Shortcut for "#overview": list interesting dungeon levels vis- + Shortcut for "#overview": list interesting dungeon levels vis- ited. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -994,16 +994,16 @@ - (Prior to 3.6.0, `^O' was a debug mode command which listed the - placement of all special levels. Use "#wizwhere" to run that + (Prior to 3.6.0, `^O' was a debug mode command which listed the + placement of all special levels. Use "#wizwhere" to run that command.) p Pay your shopping bill. P Put on an accessory (ring, amulet, or blindfold). - This command may also be used to wear armor. The prompt for - which inventory item to use will only list accessories, but + This command may also be used to wear armor. The prompt for + which inventory item to use will only list accessories, but choosing an unlisted item of armor will attempt to wear it. (See the `W' command below. It lists armor as the inventory choices but will accept an accessory and attempt to put that on.) @@ -1017,10 +1017,10 @@ When there is a fountain or sink present, it asks whether to drink from that. If that is declined, then it offers a chance to - choose a potion from inventory. Precede `q' with the `m' prefix + choose a potion from inventory. Precede `q' with the `m' prefix to skip asking about drinking from a fountain or sink. - Q Select an object for your quiver, quiver sack, or just generally + Q Select an object for your quiver, quiver sack, or just generally at the ready (only one of these is available at a time). You can then throw this (or one of these) using the `f' command. @@ -1033,16 +1033,16 @@ be removed without asking, but you can set the paranoid_confirma- tion:Remove option to require a prompt. - This command may also be used to take off armor. The prompt for - which inventory item to remove only lists worn accessories, but + This command may also be used to take off armor. The prompt for + which inventory item to remove only lists worn accessories, but an item of worn armor can be chosen. (See the `T' command below. It lists armor as the inventory choices but will accept an acces- sory and attempt to remove it.) ^R Redraw the screen. - s Search for secret doors and traps around you. It usually takes - several tries to find something. Precede with the `m' prefix to + s Search for secret doors and traps around you. It usually takes + several tries to find something. Precede with the `m' prefix to search for a turn even next to a hostile monster, if safe_wait is on. @@ -1050,7 +1050,7 @@ at an adjacent "remembered, unseen monster" marker. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -1077,9 +1077,9 @@ t Throw an object or shoot a projectile. There's no separate "shoot" command. If you throw an arrow while - wielding a bow, you are shooting that arrow and any weapon skill - bonus or penalty for bow applies. If you throw an arrow while - not wielding a bow, you are throwing it by hand and it will gen- + wielding a bow, you are shooting that arrow and any weapon skill + bonus or penalty for bow applies. If you throw an arrow while + not wielding a bow, you are throwing it by hand and it will gen- erally be less effective than when shot. See also `f' (fire) for throwing or shooting an item pre-selected @@ -1096,8 +1096,8 @@ This command may also be used to remove accessories. The prompt for which inventory item to take off only lists worn armor, but a - worn accessory can be chosen. (See the `R' command above. It - lists accessories as the inventory choices but will accept an + worn accessory can be chosen. (See the `R' command above. It + lists accessories as the inventory choices but will accept an item of armor and attempt to take it off.) ^T Teleport, if you have the ability. @@ -1110,13 +1110,13 @@ w- - wield nothing, use your bare (or gloved) hands. - Some characters can wield two weapons at once; use the `X' com- + Some characters can wield two weapons at once; use the `X' com- mand (or the "#twoweapon" extended command) to do so. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -1128,8 +1128,8 @@ W Wear armor. - This command may also be used to put on an accessory (ring, - amulet, or blindfold). The prompt for which inventory item to + This command may also be used to put on an accessory (ring, + amulet, or blindfold). The prompt for which inventory item to use will only list armor, but choosing an unlisted accessory will attempt to put it on. (See the `P' command above. It lists accessories as the inventory choices but will accept an item of @@ -1139,13 +1139,13 @@ weapon slot. The latter is used as your secondary weapon when engaging in two- - weapon combat. Note that if one of these slots is empty, the + weapon combat. Note that if one of these slots is empty, the exchange still takes place. - X Toggle two-weapon combat, if your character can do it. Also + X Toggle two-weapon combat, if your character can do it. Also available via the "#twoweapon" extended command. - (In versions prior to 3.6 this keystroke ran the command to + (In versions prior to 3.6 this keystroke ran the command to switch from normal play to "explore mode", also known as "discov- ery mode", which has now been moved to "#exploremode" and M-X.) @@ -1159,7 +1159,7 @@ don't appear in the normal status display due to space considera- tions. - In normal play, that's all that `^X' displays. In explore mode, + In normal play, that's all that `^X' displays. In explore mode, the role and status feedback is augmented by the information pro- vided by enlightenment magic. @@ -1182,7 +1182,7 @@ (R)UNIX is a registered trademark of The Open Group. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -1221,15 +1221,15 @@ + List the spells you know. - Using this command, you can also rearrange the order in which - your spells are listed, either by sorting the entire list or by - picking one spell from the menu then picking another to swap - places with it. Swapping pairs of spells changes their casting - letters, so the change lasts after the current `+' command fin- - ishes. Sorting the whole list is temporary. To make the most - recent sort order persist beyond the current `+' command, choose - the sort option again and then pick "reassign casting letters". - (Any spells learned after that will be added to the end of the + Using this command, you can also rearrange the order in which + your spells are listed, either by sorting the entire list or by + picking one spell from the menu then picking another to swap + places with it. Swapping pairs of spells changes their casting + letters, so the change lasts after the current `+' command fin- + ishes. Sorting the whole list is temporary. To make the most + recent sort order persist beyond the current `+' command, choose + the sort option again and then pick "reassign casting letters". + (Any spells learned after that will be added to the end of the list rather than be inserted into the sorted ordering.) \ Show what types of objects have been discovered. @@ -1240,15 +1240,15 @@ May be preceded by `m' to select preferred display order. - | If persistent inventory display is supported and enabled (with - the perm_invent option), interact with it instead of with the + | If persistent inventory display is supported and enabled (with + the perm_invent option), interact with it instead of with the map. - Allows scrolling with the menu_first_page, menu_previous_page, - menu_next_page, and menu_last_page keys (`^', `<', `>', `|' by + Allows scrolling with the menu_first_page, menu_previous_page, + menu_next_page, and menu_last_page keys (`^', `<', `>', `|' by - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -1259,7 +1259,7 @@ default). Some interfaces also support menu_shift_left and - menu_shift_right keys (`{' and `}' by default). Use the Return + menu_shift_right keys (`{' and `}' by default). Use the Return (aka Enter) or Escape key to resume play. ! Escape to a shell. See "#shell" below for more details. @@ -1268,20 +1268,20 @@ of the current level's map without monsters; without monsters and objects; or without monsters, objects, and traps. - The key is also shown as on some keyboards or - on others. It is sometimes displayed as ^? even though + The key is also shown as on some keyboards or + on others. It is sometimes displayed as ^? even though that is not an actual control character. - Many terminals have an option to swap the and keys, so typing the key might not execute this com- - mand. If that happens, you can use the extended command "#ter- - rain" instead. + Many terminals have an option to swap the and + keys, so typing the key might not execute this + command. If that happens, you can use the extended command + "#terrain" instead. # Perform an extended command. - As you can see, the authors of NetHack used up all the letters, + As you can see, the authors of NetHack used up all the letters, so this is a way to introduce the less frequently used commands. What extended commands are available depends on what features the game was compiled with. @@ -1300,21 +1300,21 @@ when choosing the item to adjust, enter a count prior to its let- ter. - Adjusting without a count used to collect all compatible stacks - when moving to the destination. That behavior has been changed; - to gather compatible stacks, "#adjust" a stack into its own + Adjusting without a count used to collect all compatible stacks + when moving to the destination. That behavior has been changed; + to gather compatible stacks, "#adjust" a stack into its own inventory slot. If it has a name assigned, other stacks with the same name or with no name will merge provided that all their other attributes match. If it does not have a name, only other stacks with no name are eligible. In either case, otherwise com- - patible stacks with a different name will not be merged. This + patible stacks with a different name will not be merged. This contrasts with using "#adjust" to move from one slot to a differ- ent slot. In that situation, moving (no count given) a compati- ble stack will merge if either stack has a name when the other doesn't and give that name to the result, while splitting (count - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -1380,7 +1380,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -1397,7 +1397,7 @@ Dip an object into something. Autocompletes. Default key is `M- d'. - The `m' prefix skips dipping into a fountain or pool if there is + The `m' prefix skips dipping into a fountain or pool if there is one at your location. #down @@ -1410,14 +1410,14 @@ Drop specific item types. Default key is `D'. #eat - Eat something. Default key is `e'. The `m' prefix skips eating + Eat something. Default key is `e'. The `m' prefix skips eating items on the floor. #engrave Engrave writing on the floor. Default key is `E'. #enhance - Advance or check weapon and spell skills. Autocompletes. + Advance or check weapon and spell skills. Autocompletes. Default key is `M-e'. #exploremode @@ -1446,7 +1446,7 @@ extinct. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -1457,13 +1457,13 @@ The display order is the same as is used by #vanquished. The `m' - prefix brings up a menu of available sorting orders, and doing - that for either #genocided or #vanquished changes the order for + prefix brings up a menu of available sorting orders, and doing + that for either #genocided or #vanquished changes the order for both. - If the sorting order is "count high to low" or "count low to - high" (which are applicable for #vanquished), that will be - ignored for #genocided and alphabetical will be used instead. + If the sorting order is "count high to low" or "count low to + high" (which are applicable for #vanquished), that will be + ignored for #genocided and alphabetical will be used instead. The menu omits those two choices when used for #genocide. Autocompletes. Default key is `M-g'. @@ -1512,7 +1512,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -1527,7 +1527,7 @@ played. #knownclass - Show discovered types for one class of objects. Default key is + Show discovered types for one class of objects. Default key is ``'. The `m' prefix operates the same as for "#known". @@ -1545,7 +1545,7 @@ Describe what you can see, or remember, of your surroundings. #loot - Loot a box or bag on the floor beneath you, or the saddle from a + Loot a box or bag on the floor beneath you, or the saddle from a steed standing next to you. Autocompletes. Precede with the `m' prefix to skip containers at your location and go directly to removing a saddle. Default key is `M-l', and also `l' if num- @@ -1561,10 +1561,10 @@ N'. #offer - Offer a sacrifice to the gods. Autocompletes. Default key is + Offer a sacrifice to the gods. Autocompletes. Default key is `M-o'. - You'll need to find an altar to have any chance at success. + You'll need to find an altar to have any chance at success. Corpses of recently killed monsters are the fodder of choice. The `m' prefix skips offering any items which are on the altar. @@ -1573,12 +1573,12 @@ Open a door. Default key is `o'. #options - Show and change option settings. Default key is `O'. Precede + Show and change option settings. Default key is `O'. Precede with the `m' prefix to show advanced options. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -1589,32 +1589,32 @@ #optionsfull - Show advanced game option settings. No default key. Precede - with the `m' prefix to execute the simpler options command. - (Mainly useful if you use BINDING=O:optionsfull to switch `O' + Show advanced game option settings. No default key. Precede + with the `m' prefix to execute the simpler options command. + (Mainly useful if you use BINDING=O:optionsfull to switch `O' from simple options back to traditional advanced options.) #overview - Display information you've discovered about the dungeon. Any - visited level with an annotation is included, and many things - (altars, thrones, fountains, and so on; extra stairs leading to + Display information you've discovered about the dungeon. Any + visited level with an annotation is included, and many things + (altars, thrones, fountains, and so on; extra stairs leading to another dungeon branch) trigger an automatic annotation. If dun- geon overview is chosen during end-of-game disclosure, every vis- ited level will be included regardless of annotations. - Precede #overview with the `m' prefix to display the dungeon - overview as a menu where you can select any visited level to add - or remove an annotation without needing to return to that level. - This will also force all visited levels to be displayed rather + Precede #overview with the `m' prefix to display the dungeon + overview as a menu where you can select any visited level to add + or remove an annotation without needing to return to that level. + This will also force all visited levels to be displayed rather than just the "interesting" subset. Autocompletes. Default keys are `^O', and `M-O'. #panic - Test the panic routine. Terminates the current game. Autocom- + Test the panic routine. Terminates the current game. Autocom- pletes. Debug mode only. - Asks for confirmation; default is n (no); continue playing. To + Asks for confirmation; default is n (no); continue playing. To really panic, respond with y. You can set the paranoid_confirma- tion:quit option to require a response of yes instead. @@ -1638,13 +1638,13 @@ #pray Pray to the gods for help. Autocompletes. Default key is `M-p'. - Praying too soon after receiving prior help is a bad idea. - (Hint: entering the dungeon alive is treated as having received - help. You probably shouldn't start off a new game by praying - right away.) Since using this command by accident can cause + Praying too soon after receiving prior help is a bad idea. + (Hint: entering the dungeon alive is treated as having received + help. You probably shouldn't start off a new game by praying + right away.) Since using this command by accident can cause - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -1654,8 +1654,8 @@ - trouble, there is an option to make you confirm your intent - before praying. It is enabled by default, and you can reset the + trouble, there is an option to make you confirm your intent + before praying. It is enabled by default, and you can reset the paranoid_confirmation option to disable it. #prevmsg @@ -1674,20 +1674,20 @@ Quit the program without saving your game. Autocompletes. Since using this command by accident would throw away the current - game, you are asked to confirm your intent before quitting. - Default response is n (no); continue playing. To really quit, - respond with y. You can set the paranoid_confirmation:quit + game, you are asked to confirm your intent before quitting. + Default response is n (no); continue playing. To really quit, + respond with y. You can set the paranoid_confirmation:quit option to require a response of yes instead. #quiver Select ammunition for quiver. Default key is `Q'. #read - Read a scroll, a spellbook, or something else. Default key is + Read a scroll, a spellbook, or something else. Default key is `r'. #redraw - Redraw the screen. Default key is `^R', and also `^L' if num- + Redraw the screen. Default key is `^R', and also `^L' if num- ber_pad is on. #remove @@ -1697,20 +1697,20 @@ Repeat the previous command. Default key is `^A'. #reqmenu - Prefix key to modify the behavior or request menu from some com- - mands. Prevents autopickup when used with movement commands. + Prefix key to modify the behavior or request menu from some com- + mands. Prevents autopickup when used with movement commands. Default key is `m'. #retravel - Travel to a previously selected travel destination. Default key + Travel to a previously selected travel destination. Default key is `C-_'. See also #travel. #ride - Ride (or stop riding) a saddled creature. Autocompletes. + Ride (or stop riding) a saddled creature. Autocompletes. Default key is `M-R'. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -1724,8 +1724,8 @@ Rub a lamp or a stone. Autocompletes. Default key is `M-r'. #run - Prefix key to run towards a direction. Default key is `G' when - number_pad is off, `5' when number_pad is set to 1 or 3, other- + Prefix key to run towards a direction. Default key is `G' when + number_pad is off, `5' when number_pad is set to 1 or 3, other- wise `M-5' when it is set to 2 or 4. #rush @@ -1737,12 +1737,12 @@ Save the game and exit the program. Default key is `S'. #saveoptions - Save configuration options to the config file. This will over- - write the file, removing all comments, so if you have manually + Save configuration options to the config file. This will over- + write the file, removing all comments, so if you have manually edited the config file, don't use this. #search - Search for traps and secret doors around you. Default key is + Search for traps and secret doors around you. Default key is `s'. #seeall @@ -1776,7 +1776,7 @@ (worn blindfold or towel or lenses, lit lamp(s) and/or candle(s), - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -1795,12 +1795,12 @@ If dual-wielding, a separate message about the secondary weapon will be given. Using the `m' prefix will force a menu and it will include primary weapon, alternate weapon even when not dual- - wielding, and also whatever is currently assigned to the quiver + wielding, and also whatever is currently assigned to the quiver slot. #shell - Do a shell escape, switching from NetHack to a subprocess. Can - be disabled at the time the program is built. When enabled, + Do a shell escape, switching from NetHack to a subprocess. Can + be disabled at the time the program is built. When enabled, access for specific users can be controlled by the system config- uration file. Use the shell command `exit' to return to the game. Default key is `!'. @@ -1842,7 +1842,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -1865,19 +1865,19 @@ traps. If there are visible clouds of gas in view, they are treated like - traps when deciding whether to show them or the floor underneath + traps when deciding whether to show them or the floor underneath them. - In explore mode, you can choose to view the full map rather than - just its explored portion. In debug mode there are additional + In explore mode, you can choose to view the full map rather than + just its explored portion. In debug mode there are additional choices. - Autocompletes. Default key is `' or `' (see Del + Autocompletes. Default key is `' or `' (see Del above). #therecmdmenu - Show a menu of possible actions directed at a location next to - you. The menu is limited to a subset of the likeliest actions, + Show a menu of possible actions directed at a location next to + you. The menu is limited to a subset of the likeliest actions, not an exhaustive set of all possibilities. Autocompletes. #throw @@ -1894,21 +1894,21 @@ tory to tip. The `m' prefix makes the command skip containers on the floor and - pick one from inventory, except for the special case of - menustyle:traditional with two or more containers present; that + pick one from inventory, except for the special case of + menustyle:traditional with two or more containers present; that situation will start with the floor container menu. Autocompletes. Default key is `M-T'. #travel - Travel to a specific location on the map. Default key is `_'. - Using the "request menu" prefix shows a menu of interesting tar- - gets in sight without asking to move the cursor. When picking a - target with cursor and the autodescribe option is on, the top + Travel to a specific location on the map. Default key is `_'. + Using the "request menu" prefix shows a menu of interesting tar- + gets in sight without asking to move the cursor. When picking a + target with cursor and the autodescribe option is on, the top line will show "(no travel path)" if your character does not know - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -1951,13 +1951,13 @@ Using the "request menu" prefix prior to #vanquished brings up a menu of sorting orders available (provided that the vanquished - monsters list contains at least two types of monsters). - Whichever ordering is picked gets assigned to the sortvanquished + monsters list contains at least two types of monsters). Which- + ever ordering is picked gets assigned to the sortvanquished option so is remembered for subsequent #vanquished requests. The "#genocided" command shares this sorting order. - During end-of-game disclosure, when asked whether to show van- - quished monsters answering `a' will let you choose from the sort + During end-of-game disclosure, when asked whether to show van- + quished monsters answering `a' will let you choose from the sort menu. Autocompletes. Default key is `M-V'. @@ -1974,7 +1974,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -2033,14 +2033,14 @@ one. Autocompletes. Debug mode only. Default key is `^G'. #wizidentify - Identify all items in inventory. Autocompletes. Debug mode + Identify all items in inventory. Autocompletes. Debug mode only. Default key is `^I'. #wizintrinsic Set one or more intrinsic attributes. Autocompletes. Debug mode - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -2106,7 +2106,7 @@ "high"] bit), you can invoke many extended commands by meta-ing the - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -2119,17 +2119,17 @@ first letter of the command. On Windows and MS-DOS, the "Alt" key can be used in this fashion. - On other systems, if typing "Alt" plus another key transmits a two - character sequence consisting of an Escape followed by the other key, - you may set the altmeta option to have NetHack combine them into - meta+. (This combining action only takes place when NetHack is + On other systems, if typing "Alt" plus another key transmits a two + character sequence consisting of an Escape followed by the other key, + you may set the altmeta option to have NetHack combine them into + meta+. (This combining action only takes place when NetHack is expecting a command to execute, not when accepting input to name some- thing or to make a wish.) Unlike control characters, where ^x and ^X denote the same thing, - meta characters are case-sensitive: M-x and M-X represent different - things. Some commands which can be run via a meta character require - that the letter be capitalized because the lower-case equivalent is + meta characters are case-sensitive: M-x and M-X represent different + things. Some commands which can be run via a meta character require + that the letter be capitalized because the lower-case equivalent is used for another command, so the three key combination meta+Shift+ is needed. @@ -2172,7 +2172,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -2238,7 +2238,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -2252,7 +2252,7 @@ - If the number_pad option is on, some additional letter commands + If the number_pad option is on, some additional letter commands are available: h #help @@ -2304,7 +2304,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -2316,61 +2316,61 @@ 5. Rooms and corridors - Rooms and corridors in the dungeon are either lit or dark. Any - lit areas within your line of sight will be displayed; dark areas are - only displayed if they are within one space of you. Walls and corri- + Rooms and corridors in the dungeon are either lit or dark. Any + lit areas within your line of sight will be displayed; dark areas are + only displayed if they are within one space of you. Walls and corri- dors remain on the map as you explore them. Secret corridors are hidden and appear to be solid rock. You can find them with the `s' (search) command when adjacent to them. Multi- - ple search attempts may be needed. When searching is successful, - secret corridors become ordinary open corridor locations. Mapping - magic reveals secret corridors, so converts them into ordinary corri- + ple search attempts may be needed. When searching is successful, + secret corridors become ordinary open corridor locations. Mapping + magic reveals secret corridors, so converts them into ordinary corri- dors and shows them as such. 5.1. Doorways - Doorways connect rooms and corridors. Some doorways have no - doors; you can walk right through. Others have doors in them, which - may be open, closed, or locked. To open a closed door, use the `o' - (open) command; to close it again, use the `c' (close) command. By - default the autoopen option is enabled, so simply attempting to walk - onto a closed door's location will attempt to open it without needing - `o'. Opening via autoopen will not work if you are confused or + Doorways connect rooms and corridors. Some doorways have no + doors; you can walk right through. Others have doors in them, which + may be open, closed, or locked. To open a closed door, use the `o' + (open) command; to close it again, use the `c' (close) command. By + default the autoopen option is enabled, so simply attempting to walk + onto a closed door's location will attempt to open it without needing + `o'. Opening via autoopen will not work if you are confused or stunned or suffer from the fumbling attribute. - Open doors cannot be entered diagonally; you must approach them - straight on, horizontally or vertically. Doorways without doors are + Open doors cannot be entered diagonally; you must approach them + straight on, horizontally or vertically. Doorways without doors are not restricted in this fashion except on one particular level (described by "#overview" as "a primitive area"). - Unlocking magic exists but usually won't be available early on. - You can get through a locked door without magic by first using an - unlocking tool with the `a' (apply) command, and then opening it. By - default the autounlock option is also enabled, so if you attempt to - open (via `o' or autoopen) a locked door while carrying an unlocking - tool, you'll be asked whether to use it on the door's lock. Alterna- - tively, you can break a closed door (whether locked or not) down by - kicking it via the `^D' (kick) command. Kicking down a door destroys + Unlocking magic exists but usually won't be available early on. + You can get through a locked door without magic by first using an + unlocking tool with the `a' (apply) command, and then opening it. By + default the autounlock option is also enabled, so if you attempt to + open (via `o' or autoopen) a locked door while carrying an unlocking + tool, you'll be asked whether to use it on the door's lock. Alterna- + tively, you can break a closed door (whether locked or not) down by + kicking it via the `^D' (kick) command. Kicking down a door destroys it and makes a lot of noise which might wake sleeping monsters. - Some closed doors are booby-trapped and will explode if an - attempt is made to open (when unlocked) or unlock (when locked) or - kick down. Like kicking, an explosion destroys the door and makes a - lot of noise. The "#untrap" command can be used to search a door for - traps but might take multiple attempts to find one. When one is - found, you'll be asked whether to try to disarm it. If you accede, - success will eliminate the trap but failure will set off the trap's - explosion. (If you decline, you effectively forget that a trap was + Some closed doors are booby-trapped and will explode if an + attempt is made to open (when unlocked) or unlock (when locked) or + kick down. Like kicking, an explosion destroys the door and makes a + lot of noise. The "#untrap" command can be used to search a door for + traps but might take multiple attempts to find one. When one is + found, you'll be asked whether to try to disarm it. If you accede, + success will eliminate the trap but failure will set off the trap's + explosion. (If you decline, you effectively forget that a trap was found there.) - Closed doors can be useful for shutting out monsters. Most mon- - sters cannot open closed doors, although a few don't need to (for - example, ghosts can walk through doors and fog clouds can flow under + Closed doors can be useful for shutting out monsters. Most mon- + sters cannot open closed doors, although a few don't need to (for + example, ghosts can walk through doors and fog clouds can flow under them). Some monsters who can open doors can also use unlocking tools. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -2394,7 +2394,7 @@ There are traps throughout the dungeon to snare the unwary intruder. For example, you may suddenly fall into a pit and be stuck for a few turns trying to climb out (see below). A trap usually won't - appear on your map until you trigger it by moving onto it, you see + appear on your map until you trigger it by moving onto it, you see someone else trigger it, or you discover it with the `s' (search) com- mand (multiple attempts are often needed; if your luck is poor, many attempts might be needed). Wands of secret door detection and spell @@ -2402,9 +2402,9 @@ the trap is also within line-of-sight (whether you can see at the time or not). There is also other magic which can reveal traps. - Monsters can fall prey to traps, too, which can potentially be - used as a defensive strategy. Unfortunately traps can be harmful to - your pet(s) as well. Monsters, including pets, usually will avoid + Monsters can fall prey to traps, too, which can potentially be + used as a defensive strategy. Unfortunately traps can be harmful to + your pet(s) as well. Monsters, including pets, usually will avoid moving onto a trap which is shown on your map if they have encountered that type of trap before. @@ -2420,23 +2420,23 @@ to another level, but one which is always below the current level. Usually that will be the next level down but it can be farther. Unlike (level) teleporters, the destination level of a particular trap - door or hole is persistent, so falling into one will bring you to the - same level each time--though not necessarily the same spot on the + door or hole is persistent, so falling into one will bring you to the + same level each time--though not necessarily the same spot on the level. Magic portals behave similarly, but with some additional vari- ation. Some portals are two-way and their remote destination is always the same: another portal which can take you back. Others are one-way and send you to a specific destination level but not necessar- ily to a specific location there. - There is a special multi-level branch of the dungeon with pre- - mapped levels based on the classic computer game "Sokoban." In that - game, you operate as a warehouse worker who pushes crates around - obstacles to position them at designated locations. In NetHack, the + There is a special multi-level branch of the dungeon with pre- + mapped levels based on the classic computer game "Sokoban." In that + game, you operate as a warehouse worker who pushes crates around + obstacles to position them at designated locations. In NetHack, the goal is to push boulders into pits or holes until those traps have all been nullified, giving access to whatever is beyond them. In the - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -2470,19 +2470,19 @@ 5.3. Stairs and ladders (`<', `>') In general, each level in the dungeon will have a staircase going - up (`<') to the previous level and another going down (`>') to the - next level. There are some exceptions though. For instance, fairly - early in the dungeon you will find a level with two down staircases, - one continuing into the dungeon and the other branching into an area + up (`<') to the previous level and another going down (`>') to the + next level. There are some exceptions though. For instance, fairly + early in the dungeon you will find a level with two down staircases, + one continuing into the dungeon and the other branching into an area known as the Gnomish Mines. Those mines eventually hit a dead end, so after exploring them (if you choose to do so), you'll need to climb back up to the main dungeon. When you traverse a set of stairs, or trigger a trap which sends you to another level, the level you're leaving will be deactivated and - stored in a file on disk. If you're moving to a previously visited - level, it will be loaded from its file on disk and reactivated. If - you're moving to a level which has not yet been visited, it will be + stored in a file on disk. If you're moving to a previously visited + level, it will be loaded from its file on disk and reactivated. If + you're moving to a level which has not yet been visited, it will be created (from scratch for most random levels, from a template for some "special" levels, or loaded from the remains of an earlier game for a "bones" level as briefly described below). Monsters are only active @@ -2490,19 +2490,19 @@ into stasis. Ordinarily when you climb a set of stairs, you will arrive on the - corresponding staircase at your destination. However, pets (see - below) and some other monsters will follow along if they're close - enough when you travel up or down stairs, and occasionally one of + corresponding staircase at your destination. However, pets (see + below) and some other monsters will follow along if they're close + enough when you travel up or down stairs, and occasionally one of these creatures will displace you during the climb. When that occurs, the pet or other monster will arrive on the staircase and you will end up nearby. - Ladders serve the same purpose as staircases, and the two types - of inter-level connections are nearly indistinguishable during game + Ladders serve the same purpose as staircases, and the two types + of inter-level connections are nearly indistinguishable during game play. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -2514,8 +2514,8 @@ 5.4. Shops and shopping - Occasionally you will run across a room with a shopkeeper near - the door and many items lying on the floor. You can buy items by + Occasionally you will run across a room with a shopkeeper near + the door and many items lying on the floor. You can buy items by picking them up and then using the `p' command. You can inquire about the price of an item prior to picking it up by using the "#chat" com- mand while standing on it. Using an item prior to paying for it will @@ -2533,8 +2533,8 @@ have to buy it back if you want to reclaim it. Shopkeepers sometime run out of money. When that happens, you'll - be offered credit instead of gold when you try to sell something. - Credit can be used to pay for purchases, but it is only good in the + be offered credit instead of gold when you try to sell something. + Credit can be used to pay for purchases, but it is only good in the shop where it was obtained; other shopkeepers won't honor it. (If you happen to find a "credit card" in the dungeon, don't bother trying to use it in shops; shopkeepers will not accept it.) @@ -2568,7 +2568,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -2602,18 +2602,18 @@ and without giving feedback about them. The mention_walls option controls whether you get feedback if you - try to walk into a wall or solid stone or off the edge of the map. - Normally nothing happens (unless the hero is blind and no wall is - shown, then the wall that is being bumped into will be drawn on the - map). This option also gives feedback when rushing or running stops + try to walk into a wall or solid stone or off the edge of the map. + Normally nothing happens (unless the hero is blind and no wall is + shown, then the wall that is being bumped into will be drawn on the + map). This option also gives feedback when rushing or running stops for some non-obvious reason. - The mention_decor option controls whether you get feedback when - walking on "furniture." Normally stepping onto stairs or a fountain - or an altar or various other things doesn't elicit anything unless it - is covered by one or more objects so is obscured on the map. Setting - this option to true will describe such things even when they aren't - obscured. Doorless doorways and open doors aren't considered worthy + The mention_decor option controls whether you get feedback when + walking on "furniture." Normally stepping onto stairs or a fountain + or an altar or various other things doesn't elicit anything unless it + is covered by one or more objects so is obscured on the map. Setting + this option to true will describe such things even when they aren't + obscured. Doorless doorways and open doors aren't considered worthy of mention; closed doors (if you can move onto their spots) and broken doors are. Assuming that you're able to do so, moving onto water or lava or ice will give feedback if not yet on that type of terrain but @@ -2628,13 +2628,13 @@ The "nopickup" command prefix (default `m') is also the move- without-attacking prefix and can be used to try to step onto a visible - monster's spot without the move being considered an attack (see the - Fighting subsection of Monsters below). The "fight" command prefix - (default `F'; also `-' if number_pad is on) can be used to force an - attack, when guessing where an unseen monster is or when deliberately + monster's spot without the move being considered an attack (see the + Fighting subsection of Monsters below). The "fight" command prefix + (default `F'; also `-' if number_pad is on) can be used to force an + attack, when guessing where an unseen monster is or when deliberately - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -2646,22 +2646,22 @@ attacking a peaceful or tame creature. - The run_mode option controls how frequently the map gets redrawn - when moving more than one step in a single command (so when rushing, + The run_mode option controls how frequently the map gets redrawn + when moving more than one step in a single command (so when rushing, running, or traveling). 5.6. Rogue level - One dungeon level (occurring in mid to late teens of the main + One dungeon level (occurring in mid to late teens of the main dungeon) is a tribute to the ancestor game hack's inspiration rogue. - It is usually displayed differently from other levels: possibly - in characters instead of tiles, or without line-drawing symbols if - already in characters; also, gold is shown as * rather than $ and - stairs are shown as % rather than < and >. There are some minor dif- - ferences in actual game play: doorways lack doors; a scroll, wand, or - spell of light used in a room lights up the whole room rather than - within a radius around your character. And monsters represented by + It is usually displayed differently from other levels: possibly + in characters instead of tiles, or without line-drawing symbols if + already in characters; also, gold is shown as * rather than $ and + stairs are shown as % rather than < and >. There are some minor dif- + ferences in actual game play: doorways lack doors; a scroll, wand, or + spell of light used in a room lights up the whole room rather than + within a radius around your character. And monsters represented by lower-case letters aren't randomly generated on the rogue level. The slight strangeness of this level is a feature, not a bug.... @@ -2677,19 +2677,19 @@ those monsters who are displayed on the screen. The command "#name" (by default bound to `C'), allows you to assign a name to a monster, which may be useful to help distinguish one from another when multiple - monsters are present. Assigning a name which is just a space will + monsters are present. Assigning a name which is just a space will remove any prior name. - The extended command "#chat" can be used to interact with an - adjacent monster. There is no actual dialog (in other words, you - don't get to choose what you'll say), but chatting with some monsters - such as a shopkeeper or the Oracle of Delphi can produce useful + The extended command "#chat" can be used to interact with an + adjacent monster. There is no actual dialog (in other words, you + don't get to choose what you'll say), but chatting with some monsters + such as a shopkeeper or the Oracle of Delphi can produce useful results. 6.1. Fighting - If you see a monster and you wish to fight it, just attempt to - walk into it. Many monsters you find will mind their own business + If you see a monster and you wish to fight it, just attempt to + walk into it. Many monsters you find will mind their own business unless you attack them. Some of them are very dangerous when angered. Remember: discretion is the better part of valor. @@ -2697,10 +2697,10 @@ ster by moving into its location, you'll be asked to confirm your intent. By default an answer of `y' acknowledges that intent, which can be error prone if you're using `y' to move. You can set the para- - noid_confirmation:attack option to require a response of "yes" + noid_confirmation:attack option to require a response of "yes" - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -2712,7 +2712,7 @@ instead. - If you can't see a monster (if it is invisible, or if you are + If you can't see a monster (if it is invisible, or if you are blinded), the symbol `I' will be shown when you learn of its presence. If you attempt to walk into it, you will try to fight it just like a monster that you can see; of course, if the monster has moved, you @@ -2724,7 +2724,7 @@ 6.2. Your pet You start the game with a little dog (`d'), kitten (`f'), or pony - (`u'), which follows you about the dungeon and fights monsters with + (`u'), which follows you about the dungeon and fights monsters with you. Like you, your pet needs food to survive. Dogs and cats usually feed themselves on fresh carrion and other meats; horses need vegetar- ian food which is harder to come by. If you're worried about your pet @@ -2738,9 +2738,9 @@ Your pet will follow you up and down staircases if it is next to you when you move. Otherwise your pet will be stranded and may become - wild. Similarly, when you trigger certain types of traps which alter - your location (for instance, a trap door which drops you to a lower - dungeon level), any adjacent pet will accompany you and any non-adja- + wild. Similarly, when you trigger certain types of traps which alter + your location (for instance, a trap door which drops you to a lower + dungeon level), any adjacent pet will accompany you and any non-adja- cent pet will be left behind. Your pet may trigger such traps itself; you will not be carried along with it even if adjacent at the time. @@ -2749,24 +2749,24 @@ Some types of creatures in the dungeon can actually be ridden if you have the right equipment and skill. Convincing a wild beast to let you saddle it up is difficult to say the least. Many a dungeoneer - has had to resort to magic and wizardry in order to forge the - alliance. Once you do have the beast under your control however, you - can easily climb in and out of the saddle with the "#ride" command. - Lead the beast around the dungeon when riding, in the same manner as - you would move yourself. It is the beast that you will see displayed + has had to resort to magic and wizardry in order to forge the + alliance. Once you do have the beast under your control however, you + can easily climb in and out of the saddle with the "#ride" command. + Lead the beast around the dungeon when riding, in the same manner as + you would move yourself. It is the beast that you will see displayed on the map. - Riding skill is managed by the "#enhance" command. See the sec- + Riding skill is managed by the "#enhance" command. See the sec- tion on Weapon proficiency for more information about that. - Use the `a' (apply) command and pick a saddle in your inventory + Use the `a' (apply) command and pick a saddle in your inventory to attempt to put that saddle on an adjacent creature. If successful, it will be transferred to that creature's inventory. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -2783,8 +2783,8 @@ 6.4. Bones levels You may encounter the shades and corpses of other adventurers (or - even former incarnations of yourself!) and their personal effects. - Ghosts are hard to kill, but easy to avoid, since they're slow and do + even former incarnations of yourself!) and their personal effects. + Ghosts are hard to kill, but easy to avoid, since they're slow and do little damage. You can plunder the deceased adventurer's possessions; however, they are likely to be cursed. Beware of whatever killed the former player; it is probably still lurking around, gloating over its @@ -2823,16 +2823,16 @@ are, the less the additional load will affect you. There comes a point, though, when the weight of all of that stuff you are carrying around with you through the dungeon will encumber you. Your reactions - will get slower and you'll burn calories faster, requiring food more - frequently to cope with it. Eventually, you'll be so overloaded that + will get slower and you'll burn calories faster, requiring food more + frequently to cope with it. Eventually, you'll be so overloaded that you'll either have to discard some of what you're carrying or collapse under its weight. NetHack will tell you how badly you have loaded yourself. If you - are encumbered, one of the conditions Burdened, Stressed, Strained, + are encumbered, one of the conditions Burdened, Stressed, Strained, - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -2842,25 +2842,25 @@ - Overtaxed, or Overloaded will be shown on the bottom line status dis- + Overtaxed, or Overloaded will be shown on the bottom line status dis- play. - When you pick up an object, it is assigned an inventory letter. - Many commands that operate on objects must ask you to find out which - object you want to use. When NetHack asks you to choose a particular - object you are carrying, you are usually presented with a list of + When you pick up an object, it is assigned an inventory letter. + Many commands that operate on objects must ask you to find out which + object you want to use. When NetHack asks you to choose a particular + object you are carrying, you are usually presented with a list of inventory letters to choose from (see Commands, above). - Some objects, such as weapons, are easily differentiated. Oth- - ers, like scrolls and potions, are given descriptions which vary - according to type. During a game, any two objects with the same - description are the same type. However, the descriptions will vary + Some objects, such as weapons, are easily differentiated. Oth- + ers, like scrolls and potions, are given descriptions which vary + according to type. During a game, any two objects with the same + description are the same type. However, the descriptions will vary from game to game. - When you use one of these objects, if its effect is obvious, - NetHack will remember what it is for you. If its effect isn't - extremely obvious, you will be asked what you want to call this type - of object so you will recognize it later. You can also use the + When you use one of these objects, if its effect is obvious, + NetHack will remember what it is for you. If its effect isn't + extremely obvious, you will be asked what you want to call this type + of object so you will recognize it later. You can also use the "#name" command, for the same purpose at any time, to name all objects of a particular type or just an individual object. When you use "#name" on an object which has already been named, specifying a space @@ -2873,32 +2873,32 @@ otherwise helpful. The most common effect of a curse is being stuck with (and to) the item. Cursed weapons weld themselves to your hand when wielded, so you cannot unwield them. Any cursed item you wear is - not removable by ordinary means. In addition, cursed arms and armor - usually, but not always, bear negative enchantments that make them - less effective in combat. Other cursed objects may act poorly or + not removable by ordinary means. In addition, cursed arms and armor + usually, but not always, bear negative enchantments that make them + less effective in combat. Other cursed objects may act poorly or detrimentally in other ways. - Objects can also be blessed instead. Blessed items usually work - better or more beneficially than normal uncursed items. For example, + Objects can also be blessed instead. Blessed items usually work + better or more beneficially than normal uncursed items. For example, a blessed weapon will do slightly more damage against demons. - Objects which are neither cursed nor blessed are referred to as + Objects which are neither cursed nor blessed are referred to as uncursed. They could just as easily have been described as unblessed, but the uncursed designation is what you will see within the game. A "glass half full versus glass half empty" situation; make of that what you will. - There are magical means of bestowing or removing curses upon - objects, so even if you are stuck with one, you can still have the - curse lifted and the item removed. Priests and Priestesses have an - innate sensitivity to this property in any object, so they can more - easily avoid cursed objects than other character roles. Dropping - objects onto an altar will reveal their bless or curse state provided + There are magical means of bestowing or removing curses upon + objects, so even if you are stuck with one, you can still have the + curse lifted and the item removed. Priests and Priestesses have an + innate sensitivity to this property in any object, so they can more + easily avoid cursed objects than other character roles. Dropping + objects onto an altar will reveal their bless or curse state provided that you can see them land. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -2908,10 +2908,10 @@ - An item with unknown status will be reported in your inventory - with no prefix. An item which you know the state of will be distin- - guished in your inventory by the presence of the word cursed, - uncursed, or blessed in the description of the item. In some cases + An item with unknown status will be reported in your inventory + with no prefix. An item which you know the state of will be distin- + guished in your inventory by the presence of the word cursed, + uncursed, or blessed in the description of the item. In some cases uncursed will be omitted as being redundant when enough other informa- tion is displayed. The implicit_uncursed option can be used to con- trol this; toggle it off to have uncursed be displayed even when that @@ -2924,23 +2924,23 @@ 7.2. Weapons (`)') - Given a chance, most monsters in the Mazes of Menace will gratu- - itously try to kill you. You need weapons for self-defense (killing - them first). Without a weapon, you do only 1-2 hit points of damage - (plus bonuses, if any). Monk characters are an exception; they nor- - mally do more damage with bare (or gloved) hands than they do with + Given a chance, most monsters in the Mazes of Menace will gratu- + itously try to kill you. You need weapons for self-defense (killing + them first). Without a weapon, you do only 1-2 hit points of damage + (plus bonuses, if any). Monk characters are an exception; they nor- + mally do more damage with bare (or gloved) hands than they do with weapons. - There are wielded weapons, like maces and swords, and thrown - weapons, like arrows and spears. To hit monsters with a weapon, you - must wield it and attack them, or throw it at them. You can simply - elect to throw a spear. To shoot an arrow, you should first wield a - bow, then throw the arrow. Crossbows shoot crossbow bolts. Slings + There are wielded weapons, like maces and swords, and thrown + weapons, like arrows and spears. To hit monsters with a weapon, you + must wield it and attack them, or throw it at them. You can simply + elect to throw a spear. To shoot an arrow, you should first wield a + bow, then throw the arrow. Crossbows shoot crossbow bolts. Slings hurl rocks and (other) stones (like gems). - Enchanted weapons have a "plus" (or "to hit enhancement" which - can be either positive or negative) that adds to your chance to hit - and the damage you do to a monster. The only way to determine a + Enchanted weapons have a "plus" (or "to hit enhancement" which + can be either positive or negative) that adds to your chance to hit + and the damage you do to a monster. The only way to determine a weapon's enchantment is to have it magically identified somehow. Most weapons are subject to some type of damage like rust. Such "erosion" damage can be repaired. @@ -2949,9 +2949,9 @@ the amount of damage such a hit will do, depends upon many factors. Among them are: type of weapon, quality of weapon (enchantment and/or erosion), experience level, strength, dexterity, encumbrance, and pro- - ficiency (see below). The monster's armor class--a general defense - rating, not necessarily due to wearing of armor--is a factor too; - also, some monsters are particularly vulnerable to certain types of + ficiency (see below). The monster's armor class--a general defense + rating, not necessarily due to wearing of armor--is a factor too; + also, some monsters are particularly vulnerable to certain types of weapons. Many weapons can be wielded in one hand; some require both hands. @@ -2964,7 +2964,7 @@ `X' command to engage or disengage that. Only some types of charac- - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -2981,27 +2981,27 @@ There might be times when you'd rather not wield any weapon at all. To accomplish that, wield `-', or else use the `A' command which - allows you to unwield the current weapon in addition to taking off + allows you to unwield the current weapon in addition to taking off other worn items. - Those of you in the audience who are AD&D players, be aware that + Those of you in the audience who are AD&D players, be aware that each weapon which existed in AD&D does roughly the same damage to mon- sters in NetHack. Some of the more obscure weapons (such as the aklys, lucern hammer, and bec-de-corbin) are defined in an appendix to Unearthed Arcana, an AD&D supplement. - The commands to use weapons are `w' (wield), `t' (throw), `f' - (fire), `Q' (quiver), `x' (exchange), `X' (twoweapon), and "#enhance" + The commands to use weapons are `w' (wield), `t' (throw), `f' + (fire), `Q' (quiver), `x' (exchange), `X' (twoweapon), and "#enhance" (see below). 7.2.1. Throwing and shooting - You can throw just about anything via the `t' command. It will - prompt for the item to throw; picking `?' will list things in your - inventory which are considered likely to be thrown, or picking `*' - will list your entire inventory. After you've chosen what to throw, - you will be prompted for a direction rather than for a specific tar- - get. The distance something can be thrown depends mainly on the type + You can throw just about anything via the `t' command. It will + prompt for the item to throw; picking `?' will list things in your + inventory which are considered likely to be thrown, or picking `*' + will list your entire inventory. After you've chosen what to throw, + you will be prompted for a direction rather than for a specific tar- + get. The distance something can be thrown depends mainly on the type of object and your strength. Arrows can be thrown by hand, but can be thrown much farther and will be more likely to hit when thrown while you are wielding a bow. @@ -3024,13 +3024,13 @@ when the inventory slot used for `Q' runs out. If your quiver is empty, autoquiver is false, and you are wielding a weapon which returns when thrown, you will throw that weapon instead of filling the - quiver. The fire command also has extra assistance, if fireassist is + quiver. The fire command also has extra assistance, if fireassist is on it will try to wield a launcher matching the ammo in the quiver. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -3040,37 +3040,37 @@ - Some characters have the ability to throw or shoot a volley of - multiple items (from the same stack) in a single action. Knowing how + Some characters have the ability to throw or shoot a volley of + multiple items (from the same stack) in a single action. Knowing how to load several rounds of ammunition at once--or hold several missiles in your hand--and still hit a target is not an easy task. Rangers are among those who are adept at this task, as are those with a high level of proficiency in the relevant weapon skill (in bow skill if you're wielding one to shoot arrows, in crossbow skill if you're wielding one - to shoot bolts, or in sling skill if you're wielding one to shoot - stones). The number of items that the character has a chance to fire - varies from turn to turn. You can explicitly limit the number of - shots by using a numeric prefix before the `t' or `f' command. For + to shoot bolts, or in sling skill if you're wielding one to shoot + stones). The number of items that the character has a chance to fire + varies from turn to turn. You can explicitly limit the number of + shots by using a numeric prefix before the `t' or `f' command. For example, "2f" (or "n2f" if using number_pad mode) would ensure that at most 2 arrows are shot even if you could have fired 3. If you specify - a larger number than would have been shot ("4f" in this example), - you'll just end up shooting the same number (3, here) as if no limit - had been specified. Once the volley is in motion, all of the items - will travel in the same direction; if the first ones kill a monster, + a larger number than would have been shot ("4f" in this example), + you'll just end up shooting the same number (3, here) as if no limit + had been specified. Once the volley is in motion, all of the items + will travel in the same direction; if the first ones kill a monster, the others can still continue beyond that spot. 7.2.2. Weapon proficiency - You will have varying degrees of skill in the weapons available. + You will have varying degrees of skill in the weapons available. Weapon proficiency, or weapon skills, affect how well you can use par- ticular types of weapons, and you'll be able to improve your skills as - you progress through a game, depending on your role, your experience + you progress through a game, depending on your role, your experience level, and use of the weapons. - For the purposes of proficiency, weapons have been divided up - into various groups such as daggers, broadswords, and polearms. Each - role has a limit on what level of proficiency a character can achieve - for each group. For instance, wizards can become highly skilled in + For the purposes of proficiency, weapons have been divided up + into various groups such as daggers, broadswords, and polearms. Each + role has a limit on what level of proficiency a character can achieve + for each group. For instance, wizards can become highly skilled in daggers or staves but not in swords or bows. The "#enhance" extended command is used to review current weapons @@ -3082,13 +3082,13 @@ appear in the list shown by "#enhance". (Divine intervention might unrestrict a particular skill, in which case it will start at unskilled and be limited to basic.) Some characters can enhance their - barehanded combat or martial arts skill beyond expert to "master" or + barehanded combat or martial arts skill beyond expert to "master" or "grand master". - Use of a weapon in which you're restricted or unskilled will - incur a modest penalty in the chance to hit a monster and also in the - amount of damage done when you do hit; at basic level, there is no - penalty or bonus; at skilled level, you receive a modest bonus in the + Use of a weapon in which you're restricted or unskilled will + incur a modest penalty in the chance to hit a monster and also in the + amount of damage done when you do hit; at basic level, there is no + penalty or bonus; at skilled level, you receive a modest bonus in the chance to hit and amount of damage done; at expert level, the bonus is higher. A successful hit has a chance to boost your training towards the next skill level (unless you've already reached the limit for this @@ -3096,7 +3096,7 @@ you'll be told that you feel more confident in your skills. At that - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -3113,12 +3113,12 @@ 7.2.3. Two-Weapon combat - Some characters can use two weapons at once. Setting things up - to do so can seem cumbersome but becomes second nature with use. To - wield two weapons, you need to use the "#twoweapon" command. But - first you need to have a weapon in each hand. (Note that your two - weapons are not fully equal; the one in the hand you normally wield - with is considered primary and the other one is considered secondary. + Some characters can use two weapons at once. Setting things up + to do so can seem cumbersome but becomes second nature with use. To + wield two weapons, you need to use the "#twoweapon" command. But + first you need to have a weapon in each hand. (Note that your two + weapons are not fully equal; the one in the hand you normally wield + with is considered primary and the other one is considered secondary. The most noticeable difference is after you stop--or before you begin, for that matter--wielding two weapons at once. The primary is your wielded weapon and the secondary is just an item in your inventory @@ -3133,28 +3133,28 @@ the correct weapon, use `w', `x', `w' to first wield the intended sec- ondary, swap it to off hand, and then wield the primary. - The whole process can be simplified via use of the pushweapon - option. When it is enabled, then using `w' to wield something causes - the currently wielded weapon to become your alternate weapon. So the - sequence `w', `w' can be used to first wield the weapon you intend to - be secondary, and then wield the one you want as primary which will + The whole process can be simplified via use of the pushweapon + option. When it is enabled, then using `w' to wield something causes + the currently wielded weapon to become your alternate weapon. So the + sequence `w', `w' can be used to first wield the weapon you intend to + be secondary, and then wield the one you want as primary which will push the first into secondary position. - When in two-weapon combat mode, using the `X' command toggles - back to single-weapon mode. Throwing or dropping either of the - weapons or having one of them be stolen or destroyed will also make - you revert to single-weapon combat. + When in two-weapon combat mode, using the `X' command toggles + back to single-weapon mode. Throwing or dropping either of the weap- + ons or having one of them be stolen or destroyed will also make you + revert to single-weapon combat. 7.3. Armor (`[') - Lots of unfriendly things lurk about; you need armor to protect - yourself from their blows. Some types of armor offer better protec- - tion than others. Your armor class is a measure of this protection. - Armor class (AC) is measured as in AD&D, with 10 being the equivalent - of no armor, and lower numbers meaning better armor. Each suit of + Lots of unfriendly things lurk about; you need armor to protect + yourself from their blows. Some types of armor offer better protec- + tion than others. Your armor class is a measure of this protection. + Armor class (AC) is measured as in AD&D, with 10 being the equivalent + of no armor, and lower numbers meaning better armor. Each suit of armor which exists in AD&D gives the same protection in NetHack. - Here is a list of the armor class values provided by suits of + Here is a list of the armor class values provided by suits of armor: Dragon scale mail 1 Plate mail, Crystal plate mail 3 @@ -3162,7 +3162,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -3181,8 +3181,8 @@ Leather jacket 9 none 10 - You can also wear other pieces of armor (cloak over suit, shirt - under suit, helmet, gloves, boots, shield) to lower your armor class + You can also wear other pieces of armor (cloak over suit, shirt + under suit, helmet, gloves, boots, shield) to lower your armor class even further. Most of these provide a one or two point improvement to AC (making the overall value smaller and eventually negative) but can also be enchanted. Shirts are an exception; they don't provide any @@ -3193,11 +3193,11 @@ If a piece of armor is enchanted, its armor protection will be better (or worse) than normal, and its "plus" (or minus) will subtract - from your armor class. For example, a +1 chain mail would give you - better protection than normal chain mail, lowering your armor class - one unit further to 4. When you put on a piece of armor, you immedi- - ately find out the armor class and any "plusses" it provides. Cursed - pieces of armor usually have negative enchantments (minuses) in addi- + from your armor class. For example, a +1 chain mail would give you + better protection than normal chain mail, lowering your armor class + one unit further to 4. When you put on a piece of armor, you immedi- + ately find out the armor class and any "plusses" it provides. Cursed + pieces of armor usually have negative enchantments (minuses) in addi- tion to being unremovable. Many types of armor are subject to some kind of damage like rust. @@ -3211,15 +3211,15 @@ The commands to use armor are `W' (wear) and `T' (take off). The `A' command can be used to take off armor as well as other worn items. Also, `P' (put on) and `R' (remove) which are normally for accessories - can be used for armor, but pieces of armor won't be shown as likely + can be used for armor, but pieces of armor won't be shown as likely candidates in a prompt for choosing what to put on or remove. 7.4. Food (`%') - Food is necessary to survive. If you go too long without eating - you will faint, and eventually die of starvation. Some types of food - will spoil, and become unhealthy to eat, if not protected. Food - stored in ice boxes or tins ("cans") will usually stay fresh, but ice + Food is necessary to survive. If you go too long without eating + you will faint, and eventually die of starvation. Some types of food + will spoil, and become unhealthy to eat, if not protected. Food + stored in ice boxes or tins ("cans") will usually stay fresh, but ice boxes are heavy, and tins take a while to open. When you kill monsters, they usually leave corpses which are also @@ -3228,7 +3228,7 @@ what you eat." - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -3242,25 +3242,25 @@ ian monsters will typically never eat animal corpses, while vegetarian players can, but with some rather unpleasant side-effects. - You can name one food item after something you like to eat with + You can name one food item after something you like to eat with the fruit option. The command to eat food is `e'. 7.5. Scrolls (`?') - Scrolls are labeled with various titles, probably chosen by - ancient wizards for their amusement value (for example "READ ME," or - "THANX MAUD" backwards). Scrolls disappear after you read them + Scrolls are labeled with various titles, probably chosen by + ancient wizards for their amusement value (for example "READ ME," or + "THANX MAUD" backwards). Scrolls disappear after you read them (except for blank ones, without magic spells on them). - One of the most useful of these is the scroll of identify, which - can be used to determine what another object is, whether it is cursed - or blessed, and how many uses it has left. Some objects of subtle + One of the most useful of these is the scroll of identify, which + can be used to determine what another object is, whether it is cursed + or blessed, and how many uses it has left. Some objects of subtle enchantment are difficult to identify without these. - A scroll whose label is known can be read even when the hero is - blind. If a scroll has been discovered, it will be listed in inven- + A scroll whose label is known can be read even when the hero is + blind. If a scroll has been discovered, it will be listed in inven- tory by type rather than by label, but the label is known in that sit- uation even though it isn't shown. @@ -3269,13 +3269,13 @@ A mail daemon may run up and deliver mail to you as a scroll of mail (on versions compiled with this feature). To use this feature on - versions where NetHack mail delivery is triggered by electronic mail - appearing in your system mailbox, you must let NetHack know where to - look for new mail by setting the "MAIL" environment variable to the - file name of your mailbox. You may also want to set the "MAILREADER" - environment variable to the file name of your favorite reader, so - NetHack can shell to it when you read the scroll. On versions of - NetHack where mail is randomly generated internal to the game, these + versions where NetHack mail delivery is triggered by electronic mail + appearing in your system mailbox, you must let NetHack know where to + look for new mail by setting the "MAIL" environment variable to the + file name of your mailbox. You may also want to set the "MAILREADER" + environment variable to the file name of your favorite reader, so + NetHack can shell to it when you read the scroll. On versions of + NetHack where mail is randomly generated internal to the game, these environment variables are ignored. You can disable the mail daemon by turning off the mail option. @@ -3289,12 +3289,12 @@ Clear potions are potions of water. Sometimes these are blessed or cursed, resulting in holy or unholy water. Holy water is the bane of the undead, so potions of holy water are good things to throw (`t') - at them. It is also sometimes very useful to dip ("#dip") an object + at them. It is also sometimes very useful to dip ("#dip") an object into a potion. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -3315,11 +3315,11 @@ direction. The number of charges in a wand is random and decreases by one whenever you use it. - When the number of charges left in a wand becomes zero, attempts - to use the wand will usually result in nothing happening. Occasion- - ally, however, it may be possible to squeeze the last few mana points - from an otherwise spent wand, destroying it in the process. A wand - may be recharged by using suitable magic, but doing so runs the risk + When the number of charges left in a wand becomes zero, attempts + to use the wand will usually result in nothing happening. Occasion- + ally, however, it may be possible to squeeze the last few mana points + from an otherwise spent wand, destroying it in the process. A wand + may be recharged by using suitable magic, but doing so runs the risk of causing it to explode. The chance for such an explosion starts out very small and increases each time the wand is recharged. @@ -3330,37 +3330,37 @@ When you have fully identified a particular wand, inventory dis- play will include additional information in parentheses: the number of - times it has been recharged followed by a colon and then by its cur- - rent number of charges. A current charge count of -1 is a special + times it has been recharged followed by a colon and then by its cur- + rent number of charges. A current charge count of -1 is a special case indicating that the wand has been cancelled. - The command to use a wand is `z' (zap). To break one, use the + The command to use a wand is `z' (zap). To break one, use the `a' (apply) command. 7.8. Rings (`=') - Rings are very useful items, since they are relatively permanent - magic, unlike the usually fleeting effects of potions, scrolls, and + Rings are very useful items, since they are relatively permanent + magic, unlike the usually fleeting effects of potions, scrolls, and wands. - Putting on a ring activates its magic. You can wear at most two + Putting on a ring activates its magic. You can wear at most two rings at any time, one on the ring finger of each hand. - Most worn rings also cause you to grow hungry more rapidly, the + Most worn rings also cause you to grow hungry more rapidly, the rate varying with the type of ring. - When wearing gloves, rings are worn underneath. If the gloves - are cursed, rings cannot be put on and any already being worn cannot - be removed. When worn gloves aren't cursed, you don't have to manu- - ally take them off before putting on or removing a ring and then re- + When wearing gloves, rings are worn underneath. If the gloves + are cursed, rings cannot be put on and any already being worn cannot + be removed. When worn gloves aren't cursed, you don't have to manu- + ally take them off before putting on or removing a ring and then re- wear them after. That's done implicitly to avoid unnecessary tedium. - The commands to use rings are `P' (put on) and `R' (remove). + The commands to use rings are `P' (put on) and `R' (remove). `A', `W', and `T' can also be used; see Amulets. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -3372,8 +3372,8 @@ 7.9. Spellbooks (`+') - Spellbooks are tomes of mighty magic. When studied with the `r' - (read) command, they transfer to the reader the knowledge of a spell + Spellbooks are tomes of mighty magic. When studied with the `r' + (read) command, they transfer to the reader the knowledge of a spell (and therefore eventually become unreadable)--unless the attempt back- fires. Reading a cursed spellbook or one with mystic runes beyond your ken can be harmful to your health! @@ -3387,8 +3387,8 @@ Casting a spell calls forth magical energies and focuses them with your naked mind. Some of the magical energy released comes from within you. Casting temporarily drains your magical power, which will - slowly be recovered, and causes you to need additional food. Casting - of spells also requires practice. With practice, your skill in each + slowly be recovered, and causes you to need additional food. Casting + of spells also requires practice. With practice, your skill in each category of spell casting will improve. Over time, however, your mem- ory of each spell will dim, and you will need to relearn it. @@ -3405,7 +3405,7 @@ affect all spells within the group. Advanced skill may increase the potency of spells, reduce their risk of failure during casting attempts, and improve the accuracy of the estimate for how much longer - they will be retained in your memory. Skill slots are shared with + they will be retained in your memory. Skill slots are shared with weapons skills. (See also the section on "Weapon proficiency".) Casting a spell also requires flexible movement, and wearing var- @@ -3414,19 +3414,19 @@ The command to read a spellbook is the same as for scrolls, `r' (read). The `+' command lists each spell you know along with its level, skill category, chance of failure when casting, and an estimate - of how strongly it is remembered. The `Z' (cast) command casts a + of how strongly it is remembered. The `Z' (cast) command casts a spell. 7.10. Tools (`(') - Tools are miscellaneous objects with various purposes. Some - tools have a limited number of uses, akin to wand charges. For exam- - ple, lamps burn out after a while. Other tools are containers, which + Tools are miscellaneous objects with various purposes. Some + tools have a limited number of uses, akin to wand charges. For exam- + ple, lamps burn out after a while. Other tools are containers, which objects can be placed into or taken out of. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -3436,21 +3436,21 @@ - Some tools (such as a blindfold) can be worn and can be put on - and removed like other accessories (rings, amulets); see Amulets. - Other tools (such as pick-axe) can be wielded as weapons in addition - to being applied for their usual purpose, and in some cases (again, + Some tools (such as a blindfold) can be worn and can be put on + and removed like other accessories (rings, amulets); see Amulets. + Other tools (such as pick-axe) can be wielded as weapons in addition + to being applied for their usual purpose, and in some cases (again, pick-axe) become wielded as a weapon even when applied. - The blind option can be set (prior to game start) to attempt to - play the entire game without being able to see (a self-imposed chal- + The blind option can be set (prior to game start) to attempt to + play the entire game without being able to see (a self-imposed chal- lenge which is very difficult to accomplish). The command to use a tool is `a' (apply). 7.10.1. Containers - You may encounter bags, boxes, and chests in your travels. A + You may encounter bags, boxes, and chests in your travels. A tool of this sort can be opened with the "#loot" extended command when you are standing on top of it (that is, on the same floor spot), or with the `a' (apply) command when you are carrying it. However, @@ -3475,16 +3475,16 @@ things you're already carrying). If a chest or large box is described as "broken", that means that - it can't be locked rather than that it no longer functions as a con- + it can't be locked rather than that it no longer functions as a con- tainer. - The apply and loot commands allow you to take out and/or put in - an arbitrary number of items in a single operation. If you want to - take everything out of a container, you can use the "#tip" command to - pour the contents onto the floor. This may be your only way to get - things out if your hands are stuck to a cursed two-handed weapon. - When your hands aren't stuck, you have the potential to pour the con- - tents into another container. (As of this writing, the other con- + The apply and loot commands allow you to take out and/or put in + an arbitrary number of items in a single operation. If you want to + take everything out of a container, you can use the "#tip" command to + pour the contents onto the floor. This may be your only way to get + things out if your hands are stuck to a cursed two-handed weapon. + When your hands aren't stuck, you have the potential to pour the con- + tents into another container. (As of this writing, the other con- tainer must be carried rather than on the floor.) @@ -3492,7 +3492,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -3515,7 +3515,7 @@ The commands to use amulets are the same as for rings, `P' (put on) and `R' (remove). `A' can be used to remove various worn items including amulets. Also, `W' (wear) and `T' (take off) which are nor- - mally for armor can be used for amulets and other accessories (rings + mally for armor can be used for amulets and other accessories (rings and eyewear), but accessories won't be shown as likely candidates in a prompt for choosing what to wear or take off. @@ -3526,16 +3526,16 @@ gems increase your score if you bring them with you when you exit. Other small rocks are also categorized as gems, but they are much - less valuable. All rocks, however, can be used as projectile weapons - (if you have a sling). In the most desperate of cases, you can still + less valuable. All rocks, however, can be used as projectile weapons + (if you have a sling). In the most desperate of cases, you can still throw them by hand. 7.13. Large rocks (``') - Statues and boulders are not particularly useful, and are gener- + Statues and boulders are not particularly useful, and are gener- ally heavy. It is rumored that some statues are not what they seem. - Boulders occasionally block your path. You can push one forward + Boulders occasionally block your path. You can push one forward (by attempting to walk onto its spot) when nothing blocks its path, or you can smash it into a pile of small rocks with breaking magic or a pick-axe. It is possible to move onto a boulder's location if certain @@ -3558,7 +3558,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -3571,15 +3571,15 @@ 7.14. Gold (`$') Gold adds to your score, and you can buy things in shops with it. - There are a number of monsters in the dungeon that may be influenced + There are a number of monsters in the dungeon that may be influenced by the amount of gold you are carrying (shopkeepers aside). - Gold pieces are the only type of object where bless/curse state - does not apply. They're always uncursed but never described as - uncursed even if you turn off the implicit_uncursed option. You can - set the goldX option if you prefer to have gold pieces be treated as - bless/curse state unknown rather than as known to be uncursed. Only - matters when you're using an object selection prompt that can filter + Gold pieces are the only type of object where bless/curse state + does not apply. They're always uncursed but never described as + uncursed even if you turn off the implicit_uncursed option. You can + set the goldX option if you prefer to have gold pieces be treated as + bless/curse state unknown rather than as known to be uncursed. Only + matters when you're using an object selection prompt that can filter by "BUCX" state. 7.15. Persistence of Objects @@ -3592,9 +3592,9 @@ location again. One notable exception is that if the object gets cov- ered by the "remembered, unseen monster" marker. When that marker is later removed after you've verified that no monster is there, you will - have forgotten that there was any object there regardless of whether - the unseen monster actually took the object. If the object is still - there, then once you see or feel that location again you will re-dis- + have forgotten that there was any object there regardless of whether + the unseen monster actually took the object. If the object is still + there, then once you see or feel that location again you will re-dis- cover the object and resume remembering it. The situation is the same for a pile of objects, except that only @@ -3604,18 +3604,18 @@ 8. Conduct - As if winning NetHack were not difficult enough, certain players - seek to challenge themselves by imposing restrictions on the way they - play the game. The game automatically tracks some of these chal- - lenges, which can be checked at any time with the #conduct command or - at the end of the game. When you perform an action which breaks a - challenge, it will no longer be listed. This gives players extra - "bragging rights" for winning the game with these challenges. Note - that it is perfectly acceptable to win the game without resorting to - these restrictions and that it is unusual for players to adhere to + As if winning NetHack were not difficult enough, certain players + seek to challenge themselves by imposing restrictions on the way they + play the game. The game automatically tracks some of these chal- + lenges, which can be checked at any time with the #conduct command or + at the end of the game. When you perform an action which breaks a + challenge, it will no longer be listed. This gives players extra + "bragging rights" for winning the game with these challenges. Note + that it is perfectly acceptable to win the game without resorting to + these restrictions and that it is unusual for players to adhere to challenges the first time they win the game. - Several of the challenges are related to eating behavior. The + Several of the challenges are related to eating behavior. The most difficult of these is the foodless challenge. Although creatures can survive long periods of time without food, there is a physiologi- cal need for water; thus there is no restriction on drinking bever- @@ -3624,7 +3624,7 @@ either. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -3634,34 +3634,34 @@ - A strict vegan diet is one which avoids any food derived from - animals. The primary source of nutrition is fruits and vegetables. - The corpses and tins of blobs (`b'), jellies (`j'), and fungi (`F') - are also considered to be vegetable matter. Certain human food is + A strict vegan diet is one which avoids any food derived from + animals. The primary source of nutrition is fruits and vegetables. + The corpses and tins of blobs (`b'), jellies (`j'), and fungi (`F') + are also considered to be vegetable matter. Certain human food is prepared without animal products; namely, lembas wafers, cram rations, food rations (gunyoki), K-rations, and C-rations. Metal or another normally indigestible material eaten while polymorphed into a creature - that can digest it is also considered vegan food. Note however that + that can digest it is also considered vegan food. Note however that eating such items still counts against foodless conduct. - Vegetarians do not eat animals; however, they are less selective - about eating animal byproducts than vegans. In addition to the vegan - items listed above, they may eat any kind of pudding (`P') other than - the black puddings, eggs and food made from eggs (fortune cookies and - pancakes), food made with milk (cream pies and candy bars), and lumps + Vegetarians do not eat animals; however, they are less selective + about eating animal byproducts than vegans. In addition to the vegan + items listed above, they may eat any kind of pudding (`P') other than + the black puddings, eggs and food made from eggs (fortune cookies and + pancakes), food made with milk (cream pies and candy bars), and lumps of royal jelly. Monks are expected to observe a vegetarian diet. Eating any kind of meat violates the vegetarian, vegan, and food- less conducts. This includes tripe rations, the corpses or tins of any monsters not mentioned above, and the various other chunks of meat - found in the dungeon. Swallowing and digesting a monster while poly- - morphed is treated as if you ate the creature's corpse. Eating - leather, dragon hide, or bone items while polymorphed into a creature - that can digest it, or eating monster brains while polymorphed into a - mind flayer, is considered eating an animal, although wax is only an + found in the dungeon. Swallowing and digesting a monster while poly- + morphed is treated as if you ate the creature's corpse. Eating + leather, dragon hide, or bone items while polymorphed into a creature + that can digest it, or eating monster brains while polymorphed into a + mind flayer, is considered eating an animal, although wax is only an animal byproduct. - Regardless of conduct, there will be some items which are indi- + Regardless of conduct, there will be some items which are indi- gestible, and others which are hazardous to eat. Using a swallow-and- digest attack against a monster is equivalent to eating the monster's corpse. Please note that the term "vegan" is used here only in the @@ -3676,10 +3676,10 @@ An atheist is one who rejects religion. This means that you can- not #pray, #offer sacrifices to any god, #turn undead, or #chat with a priest. Particularly selective readers may argue that playing Monk or - Priest characters should violate this conduct; that is a choice left + Priest characters should violate this conduct; that is a choice left to the player. Offering the Amulet of Yendor to your god is necessary to win the game and is not counted against this conduct. You are also - not penalized for being spoken to by an angry god, priest(ess), or + not penalized for being spoken to by an angry god, priest(ess), or other religious figure; a true atheist would hear the words but attach no special meaning to them. @@ -3690,7 +3690,7 @@ with your hands and feet. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -3707,26 +3707,26 @@ An illiterate character does not read or write. This includes reading a scroll, spellbook, fortune cookie message, or t-shirt; writ- - ing a scroll; or making an engraving of anything other than a single - "X" (the traditional signature of an illiterate person). Reading an - engraving, or any item that is absolutely necessary to win the game, - is not counted against this conduct. The identity of scrolls and - spellbooks (and knowledge of spells) in your starting inventory is - assumed to be learned from your teachers prior to the start of the + ing a scroll; or making an engraving of anything other than a single + "X" (the traditional signature of an illiterate person). Reading an + engraving, or any item that is absolutely necessary to win the game, + is not counted against this conduct. The identity of scrolls and + spellbooks (and knowledge of spells) in your starting inventory is + assumed to be learned from your teachers prior to the start of the game and isn't counted. - There is a side-branch to the main dungeon called "Sokoban," - briefly described in the earlier section about Traps. As mentioned - there, the goal is to push boulders into pits and/or holes to plug - those in order to both get the boulders out of the way and be able to - go past the traps. There are some special "rules" that are active - when in that branch of the dungeon. Some rules can't be bypassed, - such as being unable to push a boulder diagonally. Other rules can, + There is a side-branch to the main dungeon called "Sokoban," + briefly described in the earlier section about Traps. As mentioned + there, the goal is to push boulders into pits and/or holes to plug + those in order to both get the boulders out of the way and be able to + go past the traps. There are some special "rules" that are active + when in that branch of the dungeon. Some rules can't be bypassed, + such as being unable to push a boulder diagonally. Other rules can, such as not smashing boulders with magic or tools, but doing so causes you to receive a luck penalty. No message about that is given at the time, but it is tracked as a conduct. The #conduct command and end of - game disclosure will report whether you have abided by the special - rules of Sokoban, and if not, how many times you violated them, pro- + game disclosure will report whether you have abided by the special + rules of Sokoban, and if not, how many times you violated them, pro- viding you with a way to discover which actions incur bad luck so that you can be better informed about whether or not to avoid repeating those actions in the future. (Note: the Sokoban conduct will only be @@ -3740,10 +3740,10 @@ There are several other challenges tracked by the game. It is possible to eliminate one or more species of monsters by genocide; playing without this feature is considered a challenge. When the game - offers you an opportunity to genocide monsters, you may respond with - the monster type "none" if you want to decline. You can change the - form of an item into another item of the same type ("polypiling") or - the form of your own body into another creature ("polyself") by wand, + offers you an opportunity to genocide monsters, you may respond with + the monster type "none" if you want to decline. You can change the + form of an item into another item of the same type ("polypiling") or + the form of your own body into another creature ("polyself") by wand, spell, or potion of polymorph; avoiding these effects are each consid- ered challenges. Polymorphing monsters, including pets, does not break either of these challenges. Finally, you may sometimes receive @@ -3756,7 +3756,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -3822,7 +3822,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -3832,18 +3832,18 @@ - There's no guaranteed Novel so the achievement to read one might - not always be attainable (except perhaps by wishing). Similarly, the - Big Room level is not always present. Unlike with the Novel, there's + There's no guaranteed Novel so the achievement to read one might + not always be attainable (except perhaps by wishing). Similarly, the + Big Room level is not always present. Unlike with the Novel, there's no way to wish for this opportunity. - The "special items" hidden in Mines' End and Sokoban are not - unique but are considered to be prizes or rewards for exploring those - levels since doing so is not necessary to complete the game. Finding - other instances of the same objects doesn't record the corresponding + The "special items" hidden in Mines' End and Sokoban are not + unique but are considered to be prizes or rewards for exploring those + levels since doing so is not necessary to complete the game. Finding + other instances of the same objects doesn't record the corresponding achievement. - The Medusa achievement is recorded if she dies for any reason, + The Medusa achievement is recorded if she dies for any reason, even if you are not directly responsible, and only if she dies. The 5-note tune can be learned via trial and error with a musical @@ -3853,14 +3853,14 @@ Blind, Deaf, Nudist, and Pauper are also conducts, and they can only be enabled by setting the correspondingly named option in NETHACKOPTIONS or run-time configuration file prior to game start. In - the case of Blind and Deaf, the option also enforces the conduct. - They aren't really significant accomplishments unless/until you make + the case of Blind and Deaf, the option also enforces the conduct. + They aren't really significant accomplishments unless/until you make substantial progress into the dungeon. 9. Options - Due to variations in personal tastes and conceptions of how - NetHack should do things, there are options you can set to change how + Due to variations in personal tastes and conceptions of how + NetHack should do things, there are options you can set to change how NetHack behaves. 9.1. Setting the options @@ -3869,7 +3869,7 @@ command allows you to view all options and change most of them. You can also set options automatically by placing them in a configuration file, or in the NETHACKOPTIONS environment variable. Some versions of - NetHack also have front-end programs that allow you to set options + NetHack also have front-end programs that allow you to set options before starting the game or a global configuration for system adminis- trators. @@ -3882,13 +3882,13 @@ directory. The file may not exist, but it is a normal ASCII text file and can be created with any text editor. - On Windows, the name is ".nethackrc" located in the folder - "%USERPROFILE%\NetHack\". The file may not exist, but it is a normal - ASCII text file can can be created with any text editor. After run- - ning NetHack for the first time, you should find a default template + On Windows, the name is ".nethackrc" located in the folder + "%USERPROFILE%\NetHack\". The file may not exist, but it is a normal + ASCII text file can can be created with any text editor. After run- + ning NetHack for the first time, you should find a default template - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -3898,40 +3898,40 @@ - for the configuration file named ".nethackrc.template" in - "%USERPROFILE%\NetHack\". If you have not created the configuration + for the configuration file named ".nethackrc.template" in + "%USERPROFILE%\NetHack\". If you have not created the configuration file, NetHack will create one for you using the default template file. On MS-DOS, it is "defaults.nh" in the same folder as nethack.exe. - Any line in the configuration file starting with `#' is treated + Any line in the configuration file starting with `#' is treated as a comment and ignored. Empty lines are ignored. Any line beginning with `[' and ending in `]' is a section marker (the closing `]' can be followed by whitespace and then an arbitrary comment beginning with `#'). The text between the square brackets is the section name. Section markers are only valid after a CHOOSE - directive and their names are case insensitive. Lines after a section - marker belong to that section up until another section starts or a - marker without a name is encountered or the file ends. Lines within - sections are ignored unless a CHOOSE directive has selected that sec- + directive and their names are case-insensitive. Lines after a section + marker belong to that section up until another section starts or a + marker without a name is encountered or the file ends. Lines within + sections are ignored unless a CHOOSE directive has selected that sec- tion. - You can use different configuration directives in the file, some - of which can be used multiple times. In general, the directives are - written in capital letters, followed by an equals sign, followed by + You can use different configuration directives in the file, some + of which can be used multiple times. In general, the directives are + written in capital letters, followed by an equals sign, followed by settings particular to that directive. Here is a list of allowed directives: OPTIONS - There are two types of options, boolean and compound options. - Boolean options toggle a setting on or off, while compound options - take more diverse values. Prefix a boolean option with "no" or `!' - to turn it off. For compound options, the option name and value are + There are two types of options, boolean and compound options. Bool- + ean options toggle a setting on or off, while compound options take + more diverse values. Prefix a boolean option with "no" or `!' to + turn it off. For compound options, the option name and value are separated by a colon. Some options are persistent, and apply only to new games. You can specify multiple OPTIONS directives, and mul- - tiple options separated by commas in a single OPTIONS directive. + tiple options separated by commas in a single OPTIONS directive. (Comma separated options are processed from right to left.) Example: @@ -3954,7 +3954,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -3979,7 +3979,7 @@ AUTOCOMPLETE Enable or disable an extended command autocompletion. Autocomple- tion has no effect for the X11 windowport. You can specify multiple - autocompletions. To enable autocompletion, list the extended com- + autocompletions. To enable autocompletion, list the extended com- mand. Prefix the command with "!" to disable the autocompletion for that command. @@ -3994,7 +3994,7 @@ BINDINGS Change the key bindings of some special keys, menu accelerators, extended commands, or mouse buttons. You can specify multiple bind- - ings. Format is key followed by the command, separated by a colon. + ings. Format is key followed by the command, separated by a colon. See the "Changing Key Bindings" section for more information. Example: @@ -4017,10 +4017,10 @@ OPTIONS=!rest_on_space If [] is present, the preceding section is closed and no new section - begins; whatever follows will be common to all sections. Otherwise + begins; whatever follows will be common to all sections. Otherwise - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -4033,11 +4033,11 @@ the last section extends to the end of the options file. MENUCOLOR - Highlight menu lines with different colors. See the "Configuring + Highlight menu lines with different colors. See the "Configuring Menu Colors" section. MSGTYPE - Change the way messages are shown in the top status line. See the + Change the way messages are shown in the top status line. See the "Configuring Message Types" section. ROGUESYMBOLS @@ -4047,12 +4047,12 @@ Define a sound mapping. See the "Configuring User Sounds" section. SOUNDDIR - Define the directory that contains the sound files. See the "Con- + Define the directory that contains the sound files. See the "Con- figuring User Sounds" section. SYMBOLS - Override one or more symbols in the symbol set used for all dungeon - levels except for the special rogue level. See the "Modifying + Override one or more symbols in the symbol set used for all dungeon + levels except for the special rogue level. See the "Modifying NetHack Symbols" section. Example: @@ -4086,7 +4086,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -4120,19 +4120,19 @@ The NETHACKOPTIONS variable is a comma-separated list of initial values for the various options. Some can only be turned on or off. You turn one of these on by adding the name of the option to the list, - and turn it off by typing a `!' or "no" before the name. Others take - a character string as a value. You can set string options by typing - the option name, a colon or equals sign, and then the value of the - string. The value is terminated by the next comma or the end of + and turn it off by typing a `!' or "no" before the name. Others take + a character string as a value. You can set string options by typing + the option name, a colon or equals sign, and then the value of the + string. The value is terminated by the next comma or the end of string. - For example, to set up an environment variable so that color is - on, legacy is off, character name is set to "Blue Meanie", and named + For example, to set up an environment variable so that color is + on, legacy is off, character name is set to "Blue Meanie", and named fruit is set to "lime", you would enter the command % setenv NETHACKOPTIONS "color,\!leg,name:Blue Meanie,fruit:lime" - in csh (note the need to escape the `!' since it's special to that + in csh (note the need to escape the `!' since it's special to that shell), or the pair of commands $ NETHACKOPTIONS="color,!leg,name:Blue Meanie,fruit:lime" @@ -4140,19 +4140,19 @@ in sh, ksh, or bash. - The NETHACKOPTIONS value is effectively the same as a single - OPTIONS directive in a configuration file. The "OPTIONS=" prefix is - implied and comma separated options are processed from right to left. - Other types of configuration directives such as BIND or MSGTYPE are + The NETHACKOPTIONS value is effectively the same as a single + OPTIONS directive in a configuration file. The "OPTIONS=" prefix is + implied and comma separated options are processed from right to left. + Other types of configuration directives such as BIND or MSGTYPE are not allowed. - Instead of a comma-separated list of options, NETHACKOPTIONS can - be set to the full name of a configuration file you want to use. If - that full name doesn't start with a slash, precede it with `@' (at- - sign) to let NetHack know that the rest is intended as a file name. + Instead of a comma-separated list of options, NETHACKOPTIONS can + be set to the full name of a configuration file you want to use. If + that full name doesn't start with a slash, precede it with `@' (at- + sign) to let NetHack know that the rest is intended as a file name. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -4166,11 +4166,11 @@ 9.4. Customization options - Here are explanations of what the various options do. Character - strings that are too long may be truncated. Some of the options + Here are explanations of what the various options do. Character + strings that are too long may be truncated. Some of the options listed may be inactive in your dungeon. - Some options are persistent, and are saved and reloaded along + Some options are persistent, and are saved and reloaded along with the game. Changing a persistent option in the configuration file applies only to new games. @@ -4183,9 +4183,9 @@ Persistent. alignment - Your starting alignment (align:lawful, align:neutral, or - align:chaotic). You may specify just the first letter. Many roles - and the non-human races restrict which alignments are allowed. See + Your starting alignment (align:lawful, align:neutral, or + align:chaotic). You may specify just the first letter. Many roles + and the non-human races restrict which alignments are allowed. See role for a description of how to use negation to exclude choices. If align is not specified, there is no default value; player will be @@ -4218,7 +4218,7 @@ This option controls what happens when you attempt the `f' (fire) - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -4231,20 +4231,20 @@ command when nothing is quivered or readied (default false). When true, the computer will fill your quiver or quiver sack or make ready some suitable weapon. Note that it will not take into account - the blessed/cursed status, enchantment, damage, or quality of the - weapon; you are free to manually fill your quiver or quiver sack or - make ready with the `Q' command instead. If no weapon is found or - the option is false, the `t' (throw) command is executed instead. + the blessed/cursed status, enchantment, damage, or quality of the + weapon; you are free to manually fill your quiver or quiver sack or + make ready with the `Q' command instead. If no weapon is found or + the option is false, the `t' (throw) command is executed instead. Persistent. autounlock - Controls what action to take when attempting to walk into a locked - door or to loot a locked container. Takes a plus-sign separated + Controls what action to take when attempting to walk into a locked + door or to loot a locked container. Takes a plus-sign separated list of values: Untrap - prompt about whether to attempt to find a trap; it might fail to find one even when present; if it does find one, - it will ask whether you want to try to disarm the trap; + it will ask whether you want to try to disarm the trap; if you decline, your character will forget that the door or box is trapped; Apply-Key - if carrying a key or other unlocking tool, prompt about @@ -4254,9 +4254,9 @@ decline to use the key; has no effect on containers); Force - try to force a container's lid with your currently wielded weapon (if you omit untrap or decline to attempt - untrap and you omit apply-key or you lack a key or you + untrap and you omit apply-key or you lack a key or you decline to use the key; has no effect on doors); - None - none of the above; can't be combined with the other + None - none of the above; can't be combined with the other choices. Omitting the value is treated as if autounlock:apply-key. Preceding @@ -4284,7 +4284,7 @@ Name your starting cat (for example "catname:Morris"). Cannot be - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -4305,11 +4305,11 @@ program crash (default on). Persistent. cmdassist - Have the game provide some additional command assistance for new + Have the game provide some additional command assistance for new players if it detects some anticipated mistakes (default on). confirm - Have user confirm attacks on pets, shopkeepers, and other peaceable + Have user confirm attacks on pets, shopkeepers, and other peaceable creatures (default on). Persistent. dark_room @@ -4319,13 +4319,13 @@ Start the character permanently deaf (default false). Persistent. dropped_nopick - If this option is on, items you dropped will not be automatically - picked up, even if autopickup is also on and they are in - pickup_types or match a positive autopickup exception (default on). + If this option is on, items you dropped will not be automatically + picked up, even if autopickup is also on and they are in + pickup_types or match a positive autopickup exception (default on). Persistent. disclose - Controls what information the program reveals when the game ends. + Controls what information the program reveals when the game ends. Value is a space separated list of prompting/category pairs (default is "ni na nv ng nc no", prompt with default response of `n' for each candidate). Persistent. The possibilities are: @@ -4337,7 +4337,7 @@ c - display your conduct; also achievements, if any; o - display dungeon overview. - Each disclosure possibility can optionally be preceded by a prefix + Each disclosure possibility can optionally be preceded by a prefix which lets you refine how it behaves. Here are the valid prefixes: y - prompt you and default to yes on the prompt; @@ -4345,12 +4345,12 @@ + - disclose it without prompting; - - do not disclose it and do not prompt. - The listings of vanquished monsters and of genocided types can be + The listings of vanquished monsters and of genocided types can be sorted, so there are two additional choices for `v' and `g': - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -4363,14 +4363,14 @@ ? - prompt you and default to ask on the prompt; # - disclose it without prompting, ask for sort order. - Asking refers to picking one of the orderings from a menu. The `+' - disclose without prompting choice, or being prompted and answering - `y' rather than `a', will default to showing monsters in the order + Asking refers to picking one of the orderings from a menu. The `+' + disclose without prompting choice, or being prompted and answering + `y' rather than `a', will default to showing monsters in the order specified by the sortvanquished option. - Omitted categories are implicitly added with `n' prefix. Specified - categories with omitted prefix implicitly use `+' prefix. Order of - the disclosure categories does not matter, program display for end- + Omitted categories are implicitly added with `n' prefix. Specified + categories with omitted prefix implicitly use `+' prefix. Order of + the disclosure categories does not matter, program display for end- of-game disclosure follows a set sequence. (for example "disclose:yi na +v -g o") The example sets inventory to @@ -4381,16 +4381,16 @@ Note that the vanquished monsters list includes all monsters killed by traps and each other as well as by you. And the dungeon overview - shows all levels you had visited but does not reveal things about + shows all levels you had visited but does not reveal things about them that you hadn't discovered. dogname - Name your starting dog (for example "dogname:Fang"). Cannot be set + Name your starting dog (for example "dogname:Fang"). Cannot be set with the `O' command. extmenu - Changes the extended commands interface to pop-up a menu of avail- - able commands. It is keystroke compatible with the traditional + Changes the extended commands interface to pop-up a menu of avail- + able commands. It is keystroke compatible with the traditional interface except that it does not require that you hit Enter. It is implemented for the tty interface (default off). @@ -4404,9 +4404,9 @@ command. fireassist - This option controls what happens when you attempt the `f' (fire) - and don't have an appropriate launcher, such as a bow or a sling, - wielded. If on, you will automatically wield the launcher. Default + This option controls what happens when you attempt the `f' (fire) + and don't have an appropriate launcher, such as a bow or a sling, + wielded. If on, you will automatically wield the launcher. Default is on. fixinv @@ -4416,7 +4416,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -4433,15 +4433,15 @@ fruit Name a fruit after something you enjoy eating (for example "fruit:mango") (default "slime mold"). Basically a nostalgic whimsy - that NetHack uses from time to time. You should set this to some- - thing you find more appetizing than slime mold. Apples, oranges, - pears, bananas, and melons already exist in NetHack, so don't use + that NetHack uses from time to time. You should set this to some- + thing you find more appetizing than slime mold. Apples, oranges, + pears, bananas, and melons already exist in NetHack, so don't use those. gender - Your starting gender (gender:male or gender:female). You may spec- - ify just the first letter. Although you can still denote your gen- - der using either of the deprecated male and female options, if the + Your starting gender (gender:male or gender:female). You may spec- + ify just the first letter. Although you can still denote your gen- + der using either of the deprecated male and female options, if the gender option is also present it will take precedence. See role for a description of how to use negation to exclude choices. @@ -4451,17 +4451,17 @@ goldX When filtering objects based on bless/curse state (BUCX), whether to - treat gold pieces as X (unknown bless/curse state, when "on") or U - (known to be uncursed, when "off", the default). Gold is never - blessed or cursed, but it is not described as "uncursed" even when + treat gold pieces as X (unknown bless/curse state, when "on") or U + (known to be uncursed, when "off", the default). Gold is never + blessed or cursed, but it is not described as "uncursed" even when the implicit_uncursed option is "off". help - If more information is available for an object looked at with the - `/' command, ask if you want to see it (default on). Turning help - off makes just looking at things faster, since you aren't inter- - rupted with the "More info?" prompt, but it also means that you - might miss some interesting and/or important information. Persis- + If more information is available for an object looked at with the + `/' command, ask if you want to see it (default on). Turning help + off makes just looking at things faster, since you aren't inter- + rupted with the "More info?" prompt, but it also means that you + might miss some interesting and/or important information. Persis- tent. herecmd_menu @@ -4482,7 +4482,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -4517,16 +4517,16 @@ The "Qt" interface also supports hitpointbar, by drawing a solid bar above the name and title with a hard-coded color scheme. (As of this writing, having the bar enabled unintentionally inhibits resiz- - ing the status panel. To resize that, use the #optionsfull command - to toggle the hitpointbar option off, perform the resize while it's + ing the status panel. To resize that, use the #optionsfull command + to toggle the hitpointbar option off, perform the resize while it's off, then use the same command to toggle it back on.) horsename - Name your starting horse (for example "horsename:Trigger"). Cannot + Name your starting horse (for example "horsename:Trigger"). Cannot be set with the `O' command. ignintr - Ignore interrupt signals, including breaks (default off). Persis- + Ignore interrupt signals, including breaks (default off). Persis- tent. implicit_uncursed @@ -4548,7 +4548,7 @@ `b', and `c' keyboard shortcuts rather than the mnemonics `o', `i', - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -4586,18 +4586,18 @@ menustyle Controls the method used when you need to choose various objects (in - response to the Drop (aka droptype) command, for instance). The - value specified should be the first letter of one of the following: - traditional, combination, full, or partial. Default is full. Per- + response to the Drop (aka droptype) command, for instance). The + value specified should be the first letter of one of the following: + traditional, combination, full, or partial. Default is full. Per- sistent. - Traditional was the only method available for very early versions; - it consists of a prompt for object class characters, followed by an - object-by-object prompt for all items matching the selected object + Traditional was the only method available for very early versions; + it consists of a prompt for object class characters, followed by an + object-by-object prompt for all items matching the selected object class(es). Combination starts with a prompt for object class(es) of interest, but then displays a menu of matching objects rather than prompting one-by-one. Full displays a menu of object classes rather - than a character prompt, and then a menu of matching objects for + than a character prompt, and then a menu of matching objects for selection. (Choosing its `A' (Autoselect-All) choice skips the sec- ond menu. To avoid choosing that by accident, set paranoid_con- firm:AutoAll to require confirmation.) Partial skips the object @@ -4614,7 +4614,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -4645,11 +4645,11 @@ menu_objsyms Inventory and other object menus are normally separated by object class (weapons, armor, and so forth), with a menu header line at the - beginning of each group. You can have menus add the display symbol + beginning of each group. You can have menus add the display symbol for the class of objects for each header line. You can also add the display symbol for the individual item in each menu entry. For a tiles map, that would be a small rendition of an object's tile. For - a text map, it is the same character as is used for the object's + a text map, it is the same character as is used for the object's class, which would be most useful when there are no headers separat- ing objects among classes. Possible values are @@ -4675,12 +4675,12 @@ Key to go to the previous menu page. Default `<'. menu_search - Key to search for some text and toggle selection state of matching + Key to search for some text and toggle selection state of matching menu items. Default `:'. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -4707,11 +4707,11 @@ `}'. mon_movement - Show a message when hero notices a monster movement (default is + Show a message when hero notices a monster movement (default is off). monpolycontrol - Prompt for new form whenever any monster changes shape (default + Prompt for new form whenever any monster changes shape (default off). Debug mode only. montelecontrol @@ -4729,12 +4729,12 @@ port is the same as specifying 0. msghistory - The number of top line messages to keep (and be able to recall with + The number of top line messages to keep (and be able to recall with `^P') (default 20). Cannot be set with the `O' command. msg_window - Allows you to change the way recalled messages are displayed. Cur- - rently it is only supported for tty (all four choices) and for + Allows you to change the way recalled messages are displayed. Cur- + rently it is only supported for tty (all four choices) and for curses (`f' and `r' choices, default `r'). The possible values are: s - single message (default; only choice prior to 3.4.0); @@ -4742,11 +4742,11 @@ f - full window, oldest message first; r - full window reversed, newest message first. - For backward compatibility, no value needs to be specified (which - defaults to "full"), or it can be negated (which defaults to "sin- + For backward compatibility, no value needs to be specified (which + defaults to "full"), or it can be negated (which defaults to "sin- - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -4759,20 +4759,20 @@ gle"). name - Set your character's name (defaults to your user name). You can - also set your character's role by appending a dash and one or more - letters of the role (that is, by suffixing one of -A -B -C -H -K -M - -P -Ra -Ro -S -T -V -W). If -@ is used for the role, then a random + Set your character's name (defaults to your user name). You can + also set your character's role by appending a dash and one or more + letters of the role (that is, by suffixing one of -A -B -C -H -K -M + -P -Ra -Ro -S -T -V -W). If -@ is used for the role, then a random one will be automatically chosen. - On some systems, the default is the player's user name; on others, + On some systems, the default is the player's user name; on others, there is no default and the player will be prompted. The former can made to behave like the latter by specifying a generic name such as ``player''. Cannot be set with the `O' command. news Read the NetHack news file, if present (default on). Since the news - is shown at the beginning of the game, there's no point in setting + is shown at the beginning of the game, there's no point in setting this with the `O' command. nudist @@ -4793,26 +4793,26 @@ -1 - by letters but use `z' to go northwest, `y' to zap wands For backward compatibility, omitting a value is the same as specify- - ing 1 and negating number_pad is the same as specifying 0. (Set- - tings 2 and 4 are for compatibility with MS-DOS or old PC Hack; in - addition to the different behavior for `5', `Alt-5' acts as `G' and - `Alt-0' acts as `I'. Setting -1 is to accommodate some QWERTZ key- - boards which have the location of the `y' and `z' keys swapped.) - When moving by numbers, to enter a count prefix for those commands - which accept one (such as "12s" to search twelve times), precede it + ing 1 and negating number_pad is the same as specifying 0. (Set- + tings 2 and 4 are for compatibility with MS-DOS or old PC Hack; in + addition to the different behavior for `5', `Alt-5' acts as `G' and + `Alt-0' acts as `I'. Setting -1 is to accommodate some QWERTZ key- + boards which have the location of the `y' and `z' keys swapped.) + When moving by numbers, to enter a count prefix for those commands + which accept one (such as "12s" to search twelve times), precede it with the letter `n' ("n12s"). packorder - Specify the order to list object types in (default + Specify the order to list object types in (default "")[%?+!=/(*`0_"). The value of this option should be a string con- taining the symbols for the various object types. Any omitted types are filled in at the end from the previous order. paranoid_confirmation - A space separated list of specific situations where alternate + A space separated list of specific situations where alternate - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -4822,11 +4822,11 @@ - prompting is desired. The default is "paranoid_confirmation:pray + prompting is desired. The default is "paranoid_confirmation:pray swim trap". - Confirm - for any prompts which are set to require "yes" rather - than `y', also require "no" to reject instead of + Confirm - for any prompts which are set to require "yes" rather + than `y', also require "no" to reject instead of accepting any non-yes response as no; changes pray and AutoAll to require "yes" or `no' too; quit - require "yes" rather than `y' to confirm quitting the @@ -4843,8 +4843,8 @@ continue eating; Were-change - require "yes" rather than `y' to confirm changing form due to lycanthropy when hero has polymorph control; - pray - require `y' to confirm an attempt to pray rather than - immediately praying; on by default; (to require "yes" + pray - require `y' to confirm an attempt to pray rather than + immediately praying; on by default; (to require "yes" rather than just `y', set Confirm too); trap - require `y' to confirm an attempt to move into or onto a known trap, unless doing so is considered to be @@ -4854,9 +4854,9 @@ mation can be skipped by using the `m' movement pre- fix; swim - prevent walking into water or lava; on by default; (to - deliberately step onto/into such terrain when this is + deliberately step onto/into such terrain when this is set, use the `m' movement prefix when adjacent); - AutoAll - require confirmation when the `A' (Autoselect-All) + AutoAll - require confirmation when the `A' (Autoselect-All) choice is selected in object class filtering menus for menustyle:Full; (to require "yes" rather than just `y', set Confirm too); @@ -4869,16 +4869,16 @@ use paranoid_confirmation:none. To keep them enabled while setting any of the others, you can include them in the new list, such as paranoid_confirmation:attack pray swim Remove or you can precede the - first entry in the list with a plus sign, paranoid_confirma- - tion:+attack Remove. To remove an entry that has been previously - set without removing others, precede the first entry in the list - with a minus sign, paranoid_confirmation:-swim. To both add some - new entries and remove some old ones, you can use multiple para- - noid_confirmation option settings, or you can use the `+' form and - list entries to be added by their name and entries to be removed by + first entry in the list with a plus sign, paranoid_confirma- + tion:+attack Remove. To remove an entry that has been previously + set without removing others, precede the first entry in the list + with a minus sign, paranoid_confirmation:-swim. To both add some + new entries and remove some old ones, you can use multiple para- + noid_confirmation option settings, or you can use the `+' form and + list entries to be added by their name and entries to be removed by - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -4900,7 +4900,7 @@ false). This only makes sense for windowing system interfaces that implement - this feature. For those that do, the perminv_mode option can be + this feature. For those that do, the perminv_mode option can be used to refine what gets displayed for perm_invent. Setting that to a value other than none while perm_invent is false will change it to true. @@ -4921,9 +4921,9 @@ petattr Specifies one or more text highlighting attributes to use when show- - ing pets on the map. Effectively a superset of the hilite_pet - boolean option. Curses or tty interface only; value is one of none, - bold, dim, underline, italic, blink, and inverse. Some of those + ing pets on the map. Effectively a superset of the hilite_pet bool- + ean option. Curses or tty interface only; value is one of none, + bold, dim, underline, italic, blink, and inverse. Some of those choices might not work, depending upon terminal hardware or terminal emulation software. @@ -4944,7 +4944,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -4961,28 +4961,28 @@ sistent. pickup_thrown - If this option is on and autopickup is also on, try to pick up - things that you threw, even if they aren't in pickup_types or match + If this option is on and autopickup is also on, try to pick up + things that you threw, even if they aren't in pickup_types or match an autopickup exception. Default is on. Persistent. pickup_types - Specify the object types to be picked up when autopickup is on. + Specify the object types to be picked up when autopickup is on. Default is all types. Persistent. - The value is a list of object symbols, such as pickup_types:$?! to - pick up gold, scrolls, and potions. You can use autopickup_excep- + The value is a list of object symbols, such as pickup_types:$?! to + pick up gold, scrolls, and potions. You can use autopickup_excep- tion configuration file lines to further refine autopickup behavior. - There is no way to set pickup_types to "none". (Setting it to an - empty value reverts to "all".) If you want to avoid automatically - picking up any types of items but do want to have autopickup on in - order to have autopickup_exception settings control what you do and - don't pick up, you can set pickup_types to `.'. That is the type - symbol for venom and you won't come across any venom items so won't + There is no way to set pickup_types to "none". (Setting it to an + empty value reverts to "all".) If you want to avoid automatically + picking up any types of items but do want to have autopickup on in + order to have autopickup_exception settings control what you do and + don't pick up, you can set pickup_types to `.'. That is the type + symbol for venom and you won't come across any venom items so won't unintentionally pick such up. pile_limit - When walking across a pile of objects on the floor, threshold at + When walking across a pile of objects on the floor, threshold at which the message "there are few/several/many objects here" is given instead of showing a popup list of those objects. A value of 0 means "no limit" (always list the objects); a value of 1 effectively @@ -4993,16 +4993,16 @@ Values are "normal", "explore", or "debug". Allows selection of explore mode (also known as discovery mode) or debug mode (also known as wizard mode) instead of normal play. Debug mode might only - be allowed for someone logged in under a particular user name (on - multi-user systems) or specifying a particular character name (on - single-user systems) or it might be disabled entirely. Requesting + be allowed for someone logged in under a particular user name (on + multi-user systems) or specifying a particular character name (on + single-user systems) or it might be disabled entirely. Requesting it when not allowed or not possible results in explore mode instead. Default is normal play. pushweapon Using the `w' (wield) command when already wielding something pushes - the old item into your alternate weapon slot (default off). Like- - wise for the `a' (apply) command if it causes the applied item to + the old item into your alternate weapon slot (default off). Like- + wise for the `a' (apply) command if it causes the applied item to become wielded. Persistent. query_menu @@ -5010,7 +5010,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -5031,21 +5031,21 @@ race Selects your race (for example, race:human). Choices are human, dwarf, elf, gnome, and orc but most roles restrict which of the non- - human races are allowed. See role for a description of how to use + human races are allowed. See role for a description of how to use negation to exclude choices. - If race is not specified, there is no default value; player will be - prompted unless role forces a choice for race. Cannot be set with + If race is not specified, there is no default value; player will be + prompted unless role forces a choice for race. Cannot be set with the `O' command. Persistent. reroll - Allows rerolling your character's starting inventory and attributes + Allows rerolling your character's starting inventory and attributes (default false). Persistent. - Note that rerolling your character is not a recommended way to play - if aiming merely to win (a lucky start has a much smaller influence - on whether or not you win the game than your actions later in the - game). This option exists partly as an acknowledgement that some + Note that rerolling your character is not a recommended way to play + if aiming merely to win (a lucky start has a much smaller influence + on whether or not you win the game than your actions later in the + game). This option exists partly as an acknowledgement that some players will insist on doing so anyway, and partly because rerolling may be necessary for certain types of challenge games. @@ -5055,12 +5055,12 @@ role Pick your type of character (for example, role:Samurai); synonym for - character. See name for an alternate method of specifying your + character. See name for an alternate method of specifying your role. - This option can also be used to limit selection when role is chosen - randomly. Use a space-separated list of roles and either negate - each one or negate the option itself instead. Negation is accom- + This option can also be used to limit selection when role is chosen + randomly. Use a space-separated list of roles and either negate + each one or negate the option itself instead. Negation is accom- plished in the same manner as with boolean options, by prefixing the option or its value(s) with `!' or "no". Examples: @@ -5076,7 +5076,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -5097,9 +5097,9 @@ on reading an existing save file. runmode - Controls the amount of screen updating for the map window when - engaged in multi-turn movement (running via shift+direction or con- - trol+direction and so forth, or via the travel command or mouse + Controls the amount of screen updating for the map window when + engaged in multi-turn movement (running via shift+direction or con- + trol+direction and so forth, or via the travel command or mouse click). The possible values are: teleport - update the map after movement has finished; @@ -5107,10 +5107,10 @@ walk - update the map after each step; crawl - like walk, but pause briefly after each step. - This option only affects the game's screen display, not the actual - results of moving. The default is "run"; versions prior to 3.4.1 - used "teleport" only. Whether or not the effect is noticeable will - depend upon the window port used or on the type of terminal. Per- + This option only affects the game's screen display, not the actual + results of moving. The default is "run"; versions prior to 3.4.1 + used "teleport" only. Whether or not the effect is noticeable will + depend upon the window port used or on the type of terminal. Per- sistent. safe_pet @@ -5142,7 +5142,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -5166,10 +5166,10 @@ showvers Include the game's version number on the status lines (default off). Potentially useful if you switch between different versions or vari- - ants, or you are making screenshots or streaming video. Using the - statuslines:3 option is recommended so that there will be more room - available for status information, unless you're using nethack's Qt - interface or your terminal emulator window displays fewer than 25 + ants, or you are making screenshots or streaming video. Using the + statuslines:3 option is recommended so that there will be more room + available for status information, unless you're using NetHack's Qt + interface or your terminal emulator window displays fewer than 25 lines. Persistent. silent @@ -5208,7 +5208,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -5239,8 +5239,8 @@ index; z - order by count, low to high; ties broken by internal index. - Can be interactively set via the `m O' command or via using the `m' - prefix before either the #vanquished command or the #genocided com- + Can be interactively set via the `m O' command or via using the `m' + prefix before either the #vanquished command or the #genocided com- mand. sounds @@ -5274,7 +5274,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -5286,18 +5286,18 @@ symset This option may be used to select one of the named symbol sets found - within "symbols" to alter the symbols displayed on the screen. Use + within "symbols" to alter the symbols displayed on the screen. Use "symset:default" to explicitly select the default symbols. 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. timed_delay When pausing momentarily for display effect, such as with explosions and moving objects, use a timer rather than sending extra characters - to the screen. (Applies to "tty" and "curses" interfaces only; - "X11" interface always uses a timer-based delay. The default is on + to the screen. (Applies to "tty" and "curses" interfaces only; + "X11" interface always uses a timer-based delay. The default is on if configured into the program.) Persistent. tips @@ -5307,8 +5307,8 @@ Draw a tombstone graphic upon your death (default on). Persistent. toptenwin - Put the ending display in a NetHack window instead of on stdout - (default off). Setting this option makes the score list visible + Put the ending display in a NetHack window instead of on stdout + (default off). Setting this option makes the score list visible when a windowing version of NetHack is started without a parent win- dow, but it no longer leaves the score list around after game end on a terminal or emulating window. @@ -5316,7 +5316,7 @@ travel Allow the travel command via mouse click (default on). Turning this option off will prevent the game from attempting unintended moves if - you make inadvertent mouse clicks on the map window. Does not + you make inadvertent mouse clicks on the map window. Does not affect traveling via the `_' ("#travel") command. Persistent. tutorial @@ -5340,7 +5340,7 @@ n - none (no coordinates shown) [default]. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -5389,24 +5389,24 @@ When used, it should be the first option set since its value might enable or disable the availability of various other options. For multiple lines in a configuration file, that would be the first non- - comment line. For a comma-separated list in NETHACKOPTIONS or an - OPTIONS line in a configuration file, that would be the rightmost + comment line. For a comma-separated list in NETHACKOPTIONS or an + OPTIONS line in a configuration file, that would be the rightmost option in the list. wizweight - Augment object descriptions with their objects' weight (default + Augment object descriptions with their objects' weight (default off). Debug mode only. zerocomp - When writing out a save file, perform zero-comp compression of the - contents. Not all ports support zero-comp compression. It has no + When writing out a save file, perform zero-comp compression of the + contents. Not all ports support zero-comp compression. It has no effect on reading an existing save file. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -5418,10 +5418,10 @@ 9.5. Window Port Customization options - Here are explanations of the various options that are used to - customize and change the characteristics of the windowtype that you - have chosen. Character strings that are too long may be truncated. - Not all window ports will adjust for all settings listed here. You + Here are explanations of the various options that are used to + customize and change the characteristics of the windowtype that you + have chosen. Character strings that are too long may be truncated. + Not all window ports will adjust for all settings listed here. You can safely add any of these options to your configuration file, and if the window port is capable of adjusting to suit your preferences, it will attempt to do so. If it can't it will silently ignore it. You @@ -5431,19 +5431,19 @@ `O' command. align_message - Where to align or place the message window (top, bottom, left, or + Where to align or place the message window (top, bottom, left, or right) align_status - Where to align or place the status window (top, bottom, left, or + Where to align or place the status window (top, bottom, left, or right). ascii_map - If NetHack can, it should display the map using simple characters - (letters and punctuation) rather than tiles graphics. In some - cases, characters can be augmented with line-drawing symbols; use - the symset option to select a symbol set such as DECgraphics or - IBMgraphics if your display supports them. Setting ascii_map to + If NetHack can, it should display the map using simple characters + (letters and punctuation) rather than tiles graphics. In some + cases, characters can be augmented with line-drawing symbols; use + the symset option to select a symbol set such as DECgraphics or + IBMgraphics if your display supports them. Setting ascii_map to True forces tiled_map to be False. color @@ -5452,15 +5452,15 @@ eight_bit_tty If NetHack can, it should pass eight-bit character values (for exam- - ple, specified with the traps option) straight through to your ter- + ple, specified with the traps option) straight through to your ter- minal (default off). font_map - if NetHack can, it should use a font by the chosen name for the map + if NetHack can, it should use a font by the chosen name for the map window. font_menu - If NetHack can, it should use a font by the chosen name for menu + If NetHack can, it should use a font by the chosen name for menu windows. font_message @@ -5472,7 +5472,7 @@ tus window. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -5483,7 +5483,7 @@ font_text - If NetHack can, it should use a font by the chosen name for text + If NetHack can, it should use a font by the chosen name for text windows. font_size_map @@ -5538,7 +5538,7 @@ If NetHack can, it should scroll the display when the hero or cursor - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -5568,8 +5568,8 @@ Acceptable values are 2 and 3 (default is 2). When set to 3, the tty interface moves some fields around and mainly - shows status conditions on their own line. A display capable of - showing at least 25 lines is recommended. The value can be toggled + shows status conditions on their own line. A display capable of + showing at least 25 lines is recommended. The value can be toggled back and forth during the game with the `O' command. The curses interface does likewise if the align_status option is set @@ -5581,30 +5581,30 @@ out vertically. A value of 2 makes status be slightly condensed, moving some fields to different lines to eliminate one whole line, reducing the height needed. (If NetHack has been built using a ver- - sion of Qt older than qt-5.9, statuslines can only be set in the - run-time configuration file or via NETHACKOPTIONS, not during play + sion of Qt older than qt-5.9, statuslines can only be set in the + run-time configuration file or via NETHACKOPTIONS, not during play with the `O' command.) term_cols and term_rows - Curses interface only. Number of columns and rows to use for the - display. Curses will attempt to resize to the values specified but - will settle for smaller sizes if they are too big. Default is the + Curses interface only. Number of columns and rows to use for the + display. Curses will attempt to resize to the values specified but + will settle for smaller sizes if they are too big. Default is the current window size. tile_file - Specify the name of an alternative tile file to override the + Specify the name of an alternative tile file to override the default. - Note: the X11 interface uses X resources rather than NetHack's - options to select an alternate tile file. See NetHack.ad, the sam- + Note: the X11 interface uses X resources rather than NetHack's + options to select an alternate tile file. See NetHack.ad, the sam- ple X "application defaults" file. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -5621,20 +5621,20 @@ Specify the preferred width of each tile in a tile capable port tiled_map - If NetHack can, it should display the map using tiles graphics - rather than simple characters (letters and punctuation, possibly - augmented by line-drawing symbols). Setting tiled_map to True + If NetHack can, it should display the map using tiles graphics + rather than simple characters (letters and punctuation, possibly + augmented by line-drawing symbols). Setting tiled_map to True forces ascii_map to be False. use_darkgray Use bold black instead of blue for black glyphs (TTY only). use_inverse - If NetHack can, it should display inverse when the game specifies + If NetHack can, it should display inverse when the game specifies it. vary_msgcount - If NetHack can, it should display this number of messages at a time + If NetHack can, it should display this number of messages at a time in the message window. windowborders @@ -5648,21 +5648,21 @@ 3 - on, except forced off for perm_invent 4 - auto, except forced off for perm_invent - (The 26x82 size threshold for `2' refers to number of rows and - columns of the display. A width of at least 110 columns (80+2+26+2) - is needed to show borders if align_status is set to left or right.) + (The 26x82 size threshold for `2' refers to number of rows and col- + umns of the display. A width of at least 110 columns (80+2+26+2) is + needed to show borders if align_status is set to left or right.) - The persistent inventory window, when enabled, can grow until it is + The persistent inventory window, when enabled, can grow until it is too big to fit on most displays, resulting in truncation of its con- tents. If borders are forced on (1) or the display is big enough to show them (2), setting the value to 3 or 4 instead will keep borders for the map, message, and status windows but have room for two addi- - tional lines of inventory plus widen each inventory line by two - columns. + tional lines of inventory plus widen each inventory line by two col- + umns. windowcolors - If NetHack can, it should display all windows of a particular style - with the specified foreground and background colors. Windows GUI + If NetHack can, it should display all windows of a particular style + with the specified foreground and background colors. Windows GUI and curses windowport only. The format is OPTION=windowcolors:style foreground/background @@ -5670,7 +5670,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -5680,63 +5680,63 @@ - where style is one of "menu", "message", "status", or "text", and + where style is one of "menu", "message", "status", or "text", and foreground and background are colors, either numeric (hash sign fol- lowed by three pairs of hexadecimal digits, #rrggbb), one of the named colors (black, red, green, brown, blue, magenta, cyan, orange, - bright-green, yellow, bright-blue, bright-magenta, bright-cyan, - white, gray, purple, silver, maroon, fuchsia, lime, olive, navy, - teal, aqua), or (for Windows only) one of Windows UI colors (true- - black, activeborder, activecaption, appworkspace, background, btn- - face, btnshadow, btntext, captiontext, graytext, greytext, high- - light, highlighttext, inactiveborder, inactivecaption, menu, menu- + bright-green, yellow, bright-blue, bright-magenta, bright-cyan, + white, gray, purple, silver, maroon, fuchsia, lime, olive, navy, + teal, aqua), or (for Windows only) one of Windows UI colors (true- + black, activeborder, activecaption, appworkspace, background, btn- + face, btnshadow, btntext, captiontext, graytext, greytext, high- + light, highlighttext, inactiveborder, inactivecaption, menu, menu- text, scrollbar, window, windowframe, windowtext). wraptext - If NetHack can, it should wrap long lines of text if they don't fit + If NetHack can, it should wrap long lines of text if they don't fit in the visible area of the window. 9.6. Crash Report Options - Please note that NetHack does not send any information off your + Please note that NetHack does not send any information off your computer unless you manually click submit on a form. OPTION=crash_email:email_address OPTION=crash_name:your_name - These options are used only to save you some typing on the + These options are used only to save you some typing on the crash report and #bugreport forms. OPTION=crash_urlmax:bytes - This option is used to limit the length of the URLs generated - and is only needed if your browser cannot handle arbitrarily + This option is used to limit the length of the URLs generated + and is only needed if your browser cannot handle arbitrarily long URLs. 9.7. Platform-specific Customization options - Here are explanations of options that are used by specific plat- + Here are explanations of options that are used by specific plat- forms or ports to customize and change the port behavior. altkeyhandling - Select an alternate way to handle keystrokes (Win32 tty NetHack - only). The name of the handling type is one of "default", "ray", + Select an alternate way to handle keystrokes (Win32 tty NetHack + only). The name of the handling type is one of "default", "ray", "340". altmeta - On systems where this option is available, it can be set to tell - NetHack to convert a two character sequence beginning with ESC into + On systems where this option is available, it can be set to tell + NetHack to convert a two character sequence beginning with ESC into a meta-shifted version of the second character (default off). - This conversion is only done for commands, not for other input - prompts. Note that typing one or more digits as a count prefix - prior to a command--preceded by n if the number_pad option is set-- + This conversion is only done for commands, not for other input + prompts. Note that typing one or more digits as a count prefix + prior to a command--preceded by n if the number_pad option is set-- is also subject to this conversion, so attempting to abort the count by typing ESC will leave NetHack waiting for another character to complete the two character sequence. Type a second ESC to finish cancelling such a count. At other prompts a single ESC suffices. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -5753,13 +5753,13 @@ only). rawio - Force raw (non-cbreak) mode for faster output and more bulletproof - input (MS-DOS sometimes treats `^P' as a printer toggle without it) - (default off, OS/2, PC, and ST NetHack only). Note: DEC Rainbows + Force raw (non-cbreak) mode for faster output and more bulletproof + input (MS-DOS sometimes treats `^P' as a printer toggle without it) + (default off, OS/2, PC, and ST NetHack only). Note: DEC Rainbows hang if this is turned on. Cannot be set with the `O' command. subkeyvalue - (Win32 tty NetHack only). May be used to alter the value of key- + (Win32 tty NetHack only). May be used to alter the value of key- strokes that the operating system returns to NetHack to help compen- sate for international keyboard issues. OPTIONS=subkeyvalue:171/92 will return 92 to NetHack, if 171 was originally going to be @@ -5768,8 +5768,8 @@ video Set the video mode used (PC NetHack only). Values are "autodetect", - "default", "vga", or "vesa". Setting "vesa" will cause the game to - display tiles, using the full capability of the VGA hardware. Set- + "default", "vga", or "vesa". Setting "vesa" will cause the game to + display tiles, using the full capability of the VGA hardware. Set- ting "vga" will cause the game to display tiles, fixed at 640x480 in 16 colors, a mode that is compatible with all VGA hardware. Third party tilesets will probably not work. Setting "autodetect" @@ -5802,7 +5802,7 @@ support on a platform where there is no regular expression library. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -5823,23 +5823,23 @@ beyond what is available through the pickup_types option. By placing autopickup_exception lines in your configuration file, - you can define patterns to be checked when the game is about to + you can define patterns to be checked when the game is about to autopickup something. autopickup_exception Sets an exception to the pickup_types option. The autopickup_excep- tion option should be followed by a regular expression to be used as - a pattern to match against the singular form of the description of + a pattern to match against the singular form of the description of an object at your location. - In addition, some characters are treated specially if they occur as + In addition, some characters are treated specially if they occur as the first character in the pattern, specifically: < - always pickup an object that matches rest of pattern; > - never pickup an object that matches rest of pattern. - The autopickup_exception rules are processed in the order in which - they appear in your configuration file, thus allowing a later rule + The autopickup_exception rules are processed in the order in which + they appear in your configuration file, thus allowing a later rule to override an earlier rule. Exceptions can be set with the `O' command, but because they are not @@ -5853,7 +5853,7 @@ autopickup_exception=">*corpse" autopickup_exception=">* cursed*" - The first example above will result in autopickup of any type of + The first example above will result in autopickup of any type of arrow. The second example results in the exclusion of any corpse from autopickup. The last example results in the exclusion of items known to be cursed from autopickup. @@ -5861,14 +5861,14 @@ 9.10. Changing Key Bindings It is possible to change the default key bindings of some special - commands, menu accelerator keys, and extended commands, by using BIND - stanzas in the configuration file. Format is key, followed by the - command to bind to, separated by a colon. The key can be a single - character ("x"), a control key ("^X", "C-x"), a meta key ("M-x"), a + commands, menu accelerator keys, and extended commands, by using BIND + stanzas in the configuration file. Format is key, followed by the + command to bind to, separated by a colon. The key can be a single + character ("x"), a control key ("^X", "C-x"), a meta key ("M-x"), a mouse button, or a three-digit decimal ASCII code. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -5885,8 +5885,8 @@ BIND=v:loot Extended command keys - You can bind multiple keys to the same extended command. Unbind a - key by using "nothing" as the extended command to bind to. You can + You can bind multiple keys to the same extended command. Unbind a + key by using "nothing" as the extended command to bind to. You can also bind the "", "", and "" keys. Menu accelerator keys @@ -5896,12 +5896,12 @@ port some of the menu accelerators. Mouse buttons - You can bind "mouse1" or "mouse2" to "nothing", "therecmdmenu", + You can bind "mouse1" or "mouse2" to "nothing", "therecmdmenu", "clicklook", or "mouseaction". Special command keys - Below are the special commands you can rebind. Some of them can be - bound to same keys with no problems, others are in the same "con- + Below are the special commands you can rebind. Some of them can be + bound to same keys with no problems, others are in the same "con- text", and if bound to same keys, only one of those commands will be available. Special command can only be bound to a single key. @@ -5918,23 +5918,23 @@ click. You will be asked to pick a location. Use movement key- strokes to move the cursor around the map, then type the get- pos.pick.once key (default `,') or the getpos.pick key (default `.') - to finish as if performing a left or right click. Only useful when + to finish as if performing a left or right click. Only useful when using the #therecmdmenu command. Default is `_'. getdir.self - When asked for a direction, the key to target yourself. Default is + When asked for a direction, the key to target yourself. Default is `.'. getdir.self2 - When asked for a direction, an alternate key to target yourself. + When asked for a direction, an alternate key to target yourself. Default is `s'. getpos.autodescribe - When asked for a location, the key to toggle autodescribe. Default + When asked for a location, the key to toggle autodescribe. Default is `#'. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -5964,15 +5964,15 @@ When asked for a location, the key to show help. Default is `?'. getpos.mon.next - When asked for a location, the key to go to next closest monster. + When asked for a location, the key to go to next closest monster. Default is `m'. getpos.mon.prev - When asked for a location, the key to go to previous closest mon- + When asked for a location, the key to go to previous closest mon- ster. Default is `M'. getpos.obj.next - When asked for a location, the key to go to next closest object. + When asked for a location, the key to go to next closest object. Default is `o'. getpos.obj.prev @@ -6000,7 +6000,7 @@ sibly ask for more info. When simulating a mouse click after being - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -6049,15 +6049,15 @@ tion. Default is `z'. getpos.valid.prev - When asked for a location, the key to go to previous closest valid + When asked for a location, the key to go to previous closest valid location. Default is `Z'. 9.11. Configuring Message Types - You can change the way the messages are shown in the message + You can change the way the messages are shown in the message area, when the message matches a user-defined pattern. - In general, the configuration file entries to describe the mes- + In general, the configuration file entries to describe the mes- sage types look like this: MSGTYPE=type "pattern" type - how the message should be shown; @@ -6066,7 +6066,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -6083,17 +6083,17 @@ show - show message normally; hide - never show the message; stop - wait for user with more-prompt; - norep - show the message once, but not again if no other message is + norep - show the message once, but not again if no other message is shown in between. - Here's an example of message types using NetHack's internal pattern + Here's an example of message types using NetHack's internal pattern matching facility: MSGTYPE=stop "You feel hungry." MSGTYPE=hide "You displaced *." - specifies that whenever a message "You feel hungry" is shown, the - user is prompted with more-prompt, and a message matching "You dis- + specifies that whenever a message "You feel hungry" is shown, the + user is prompted with more-prompt, and a message matching "You dis- placed ." is not shown at all. The order of the defined MSGTYPE lines is important; the last match- @@ -6102,24 +6102,24 @@ 9.12. Configuring Menu Colors Some platforms allow you to define colors used in menu lines when - the line matches a user-defined pattern. At this time the tty, + the line matches a user-defined pattern. At this time the tty, curses, win32tty and win32gui interfaces support this. - In general, the configuration file entries to describe the menu + In general, the configuration file entries to describe the menu color mappings look like this: MENUCOLOR="pattern"=color&attribute pattern - the pattern to match; color - the color to use for lines matching the pattern; - attribute - the attribute to use for lines matching the pat- - tern. The attribute is optional, and if left out, - you must also leave out the preceding ampersand. + attribute - the attribute to use for lines matching the pat- + tern. The attribute is optional, and if left out, + you must also leave out the preceding ampersand. If no attribute is defined, no attribute is used. The pattern should be a regular expression. - Allowed colors are black, red, green, brown, blue, magenta, cyan, + Allowed colors are black, red, green, brown, blue, magenta, cyan, gray, orange, light-green, yellow, light-blue, light-magenta, light- cyan, and white. And no-color, the default foreground color, which isn't necessarily the same as any of the other colors. @@ -6128,11 +6128,11 @@ and inverse. "Normal" is a synonym for "none". Note that the plat- form used may interpret the attributes any way it wants. - Here's an example of menu colors using NetHack's internal pattern + Here's an example of menu colors using NetHack's internal pattern matching facility: - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -6146,7 +6146,7 @@ MENUCOLOR="* cursed *"=red MENUCOLOR="* cursed *(being worn)"=red&underline - specifies that any menu line with " blessed " contained in it will + specifies that any menu line with " blessed " contained in it will be shown in green color, lines with " cursed " will be shown in red, and lines with " cursed " followed by "(being worn)" on the same line will be shown in red color and underlined. You can have multi- @@ -6162,10 +6162,10 @@ Some platforms allow you to define sound files to be played when a message that matches a user-defined pattern is delivered to the mes- - sage window. At this time the Qt port and the win32tty and win32gui + sage window. At this time the Qt port and the win32tty and win32gui ports support the use of user sounds. - The following configuration file entries are relevant to mapping + The following configuration file entries are relevant to mapping user sounds to messages: SOUNDDIR @@ -6198,7 +6198,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -6248,15 +6248,15 @@ them. To specify multiple attributes, use `+' to combine those. For example: "magenta&inverse+dim". - Note that the display may substitute or ignore particular attrib- - utes depending upon its capabilities, and in general may interpret the - attributes any way it wants. For example, on some display systems a - request for bold might yield blink or vice versa. On others, issuing - an attribute request while another is already set up will replace the - earlier attribute rather than combine with it. Since NetHack issues - attribute requests sequentially (at least with the tty interface) - rather than all at once, the only way a situation like that can be - controlled is to specify just one attribute. + Note that the display may substitute or ignore particular + attributes depending upon its capabilities, and in general may inter- + pret the attributes any way it wants. For example, on some display + systems a request for bold might yield blink or vice versa. On oth- + ers, issuing an attribute request while another is already set up will + replace the earlier attribute rather than combine with it. Since + NetHack issues attribute requests sequentially (at least with the tty + interface) rather than all at once, the only way a situation like that + can be controlled is to specify just one attribute. You can adjust the appearance of the following status fields: title dungeon-level experience-level @@ -6264,7 +6264,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -6290,32 +6290,32 @@ Instead of a behavior, "condition" takes the following condition flags: stone, slime, strngl, foodpois, termill, blind, deaf, stun, conf, hallu, lev, fly, and ride. You can use "major_troubles" as an - alias for stone through termill, "minor_troubles" for blind through + alias for stone through termill, "minor_troubles" for blind through hallu, "movement" for lev, fly, and ride, and "all" for every condi- tion. Allowed behaviors are "always", "up", "down", "changed", a percent- age or absolute number threshold, or text to match against. For the - hitpoints field, the additional behavior "criticalhp" is available. - It overrides other behavior rules if hit points are at or below the - major problem threshold (which varies depending upon maximum hit + hitpoints field, the additional behavior "criticalhp" is available. + It overrides other behavior rules if hit points are at or below the + major problem threshold (which varies depending upon maximum hit points and experience level). * "always" will set the default attributes for that field. - * "up", "down" set the field attributes for when the field value - changes upwards or downwards. This attribute times out after + * "up", "down" set the field attributes for when the field value + changes upwards or downwards. This attribute times out after statushilites turns. - * "changed" sets the field attribute for when the field value - changes. This attribute times out after statushilites turns. - (If a field has both a "changed" rule and an "up" or "down" - rule which matches a change in the field's value, the "up" or + * "changed" sets the field attribute for when the field value + changes. This attribute times out after statushilites turns. + (If a field has both a "changed" rule and an "up" or "down" + rule which matches a change in the field's value, the "up" or "down" one takes precedence.) - * percentage sets the field attribute when the field value - matches the percentage. It is specified as a number between 0 - and 100, followed by `%' (percent sign). If the percentage is + * percentage sets the field attribute when the field value + matches the percentage. It is specified as a number between 0 + and 100, followed by `%' (percent sign). If the percentage is prefixed with `<=' or `>=', it also matches when value is below or above the percentage. Use prefix `<' or `>' to match when strictly below or above. (The numeric limit is relaxed @@ -6325,12 +6325,12 @@ sponding maximum field. Percentage highlight rules are also allowed for "experience level" and "experience points" (valid when the showexp option is enabled). For those, the percentage - is based on the progress from the start of the current experi- - ence level to the start of the next level. So if level 2 - starts at 20 points and level 3 starts at 40 points, having 30 + is based on the progress from the start of the current experi- + ence level to the start of the next level. So if level 2 + starts at 20 points and level 3 starts at 40 points, having 30 - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -6340,9 +6340,9 @@ - points is 50% and 35 points is 75%. 100% is unattainable for - experience because you'll gain a level and the calculations - will be reset for that new level, but a rule for =100% is + points is 50% and 35 points is 75%. 100% is unattainable for + experience because you'll gain a level and the calculations + will be reset for that new level, but a rule for =100% is allowed and matches the special case of being exactly 1 experi- ence point short of the next level. @@ -6355,17 +6355,17 @@ * criticalhp only applies to the hitpoints field and only when current hit points are below a threshold (which varies by maxi- - mum hit points and experience level). When the threshold is - met, a criticalhp rule takes precedence over all other hit- + mum hit points and experience level). When the threshold is + met, a criticalhp rule takes precedence over all other hit- points rules. - * text match sets the attribute when the field value matches the - text. Text matches can only be used for "alignment", "carry- - ing-capacity", "hunger", "dungeon-level", and "title". For - title, only the role's rank title is tested; the character's + * text match sets the attribute when the field value matches the + text. Text matches can only be used for "alignment", "carry- + ing-capacity", "hunger", "dungeon-level", and "title". For + title, only the role's rank title is tested; the character's name is ignored. - The in-game options menu can help you determine the correct syn- + The in-game options menu can help you determine the correct syn- tax for a configuration file. The whole feature can be disabled by setting option statushilites @@ -6396,7 +6396,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -6416,10 +6416,10 @@ You can also override one or more symbols using the SYMBOLS and ROGUESYMBOLS configuration file options. Symbols are specified as name:value pairs. Note that NetHack escape-processes the value string - in conventional C fashion. This means that \ is a prefix to take the - following character literally. Thus \ needs to be represented as \\. - The special prefix form \m switches on the meta bit in the symbol - value, and the ^ prefix causes the following character to be treated + in conventional C fashion. This means that \ is a prefix to take the + following character literally. Thus \ needs to be represented as \\. + The special prefix form \m switches on the meta bit in the symbol + value, and the ^ prefix causes the following character to be treated as a control character. NetHack Symbols @@ -6462,7 +6462,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -6528,7 +6528,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -6594,7 +6594,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -6651,7 +6651,7 @@ Notes: - * Several symbols in this table appear to be blank. They are the + * Several symbols in this table appear to be blank. They are the space character, except for S_pet_override and S_hero_override which don't have any default value and can only be used if enabled in the "sysconf" file. @@ -6660,7 +6660,7 @@ and boulders are the rock being referred to, but since version - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -6678,17 +6678,17 @@ If your platform or terminal supports the display of UTF-8 char- acter sequences, you can customize your game display by assigning Uni- - code codepoint values and red-green-blue colors to glyph representa- - tions. The customizations can be specified for use with a symset that - has a UTF8 handler within the symbols file such as the enhanced1 set, + code codepoint values and red-green-blue colors to glyph representa- + tions. The customizations can be specified for use with a symset that + has a UTF8 handler within the symbols file such as the enhanced1 set, or individually within your nethack.rc file. The format for defining a glyph representation is: OPTIONS=glyph:glyphid/U+nnnn/R-G-B - The window port that is active needs to provide support for dis- - playing UTF-8 character sequences and explicit red-green-blue colors + The window port that is active needs to provide support for dis- + playing UTF-8 character sequences and explicit red-green-blue colors in order for the glyph representation to be visible. For example, the following line in your configuration file will cause the glyph repre- sentation for glyphid G_pool to use Unicode codepoint U+224B and the @@ -6698,24 +6698,24 @@ The list of acceptable glyphid's can be produced by nethack --dumpg- lyphids. Individual NetHack glyphs can be specified using the G_ pre- - fix, or you can use an S_ symbol for a glyphid and store the custom - representation for all NetHack glyphs that would map to that particu- + fix, or you can use an S_ symbol for a glyphid and store the custom + representation for all NetHack glyphs that would map to that particu- lar symbol. - You will need to select a symset with a UTF8 handler to enable + You will need to select a symset with a UTF8 handler to enable the display of the customizations, such as the Enhanced symset. 9.17. Configuring NetHack for Play by the Blind - NetHack can be set up to use only standard ASCII characters for - making maps of the dungeons. This makes even the MS-DOS versions of - NetHack (which use special line-drawing characters by default) com- - pletely accessible to the blind who use speech and/or Braille access - technologies. Players will require a good working knowledge of their + NetHack can be set up to use only standard ASCII characters for + making maps of the dungeons. This makes even the MS-DOS versions of + NetHack (which use special line-drawing characters by default) com- + pletely accessible to the blind who use speech and/or Braille access + technologies. Players will require a good working knowledge of their screen-reader's review features, and will have to know how to navigate horizontally and vertically character by character. They will also find the search capabilities of their screen-readers to be quite valu- - able. Be certain to examine this Guidebook before playing so you have + able. Be certain to examine this Guidebook before playing so you have an idea what the screen layout is like. You'll also need to be able to locate the PC cursor. It is always where your character is located. Merely searching for an @-sign will not always find your character @@ -6726,7 +6726,7 @@ tion of items on the screen. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -6736,7 +6736,7 @@ - NetHack can also be compiled with support for sending the game + NetHack can also be compiled with support for sending the game messages to an external program, such as a text-to-speech synthesizer. If the "#version" extended command shows "external program as a mes- sage handler", your NetHack has been compiled with the capability. @@ -6792,7 +6792,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -6811,8 +6811,8 @@ instead of moving 8 units at a time. nostatus_updates - Prevent updates to the status lines at the bottom of the screen, if - your screen-reader reads those lines. The same information can be + Prevent updates to the status lines at the bottom of the screen, if + your screen-reader reads those lines. The same information can be seen via the "#attributes" command. showdamage @@ -6820,45 +6820,45 @@ 9.18. Global Configuration for System Administrators - If NetHack is compiled with the SYSCF option, a system adminis- - trator should set up a global configuration; this is a file in the - same format as the traditional per-user configuration file (see - above). This file should be named sysconf and placed in the same - directory as the other NetHack support files. The options recognized - in this file are listed below. Any option not set uses a compiled-in + If NetHack is compiled with the SYSCF option, a system adminis- + trator should set up a global configuration; this is a file in the + same format as the traditional per-user configuration file (see + above). This file should be named sysconf and placed in the same + directory as the other NetHack support files. The options recognized + in this file are listed below. Any option not set uses a compiled-in default (which may not be appropriate for your system). - WIZARDS = A space-separated list of user names who are allowed to - play in debug mode (commonly referred to as wizard mode). A value - of a single asterisk (*) allows anyone to start a game in debug + WIZARDS = A space-separated list of user names who are allowed to + play in debug mode (commonly referred to as wizard mode). A value + of a single asterisk (*) allows anyone to start a game in debug mode. - SHELLERS = A list of users who are allowed to use the shell escape + SHELLERS = A list of users who are allowed to use the shell escape command (!). The syntax is the same as WIZARDS. EXPLORERS = A list of users who are allowed to use the explore mode. The syntax is the same as WIZARDS. MSGHANDLER = A path and filename of executable. Whenever a message- - window message is shown, NetHack runs this program. The program + window message is shown, NetHack runs this program. The program will get the message as the only parameter. - MAXPLAYERS = Limit the maximum number of games that can be running + MAXPLAYERS = Limit the maximum number of games that can be running at the same time. - SUPPORT = A string explaining how to get local support (no default + SUPPORT = A string explaining how to get local support (no default value). - RECOVER = A string explaining how to recover a game on this system + RECOVER = A string explaining how to recover a game on this system (no default value). - SEDUCE = 0 or 1 to disable or enable, respectively, the SEDUCE + SEDUCE = 0 or 1 to disable or enable, respectively, the SEDUCE option. When disabled, incubi and succubi behave like nymphs. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -6868,11 +6868,11 @@ - CHECK_PLNAME = Setting this to 1 will make the EXPLORERS, WIZARDS, - and SHELLERS check for the player name instead of the user's login + CHECK_PLNAME = Setting this to 1 will make the EXPLORERS, WIZARDS, + and SHELLERS check for the player name instead of the user's login name. - CHECK_SAVE_UID = 0 or 1 to disable or enable, respectively, the UID + CHECK_SAVE_UID = 0 or 1 to disable or enable, respectively, the UID (used identification number) checking for save files (to verify that the user who is restoring is the same one who saved). @@ -6917,14 +6917,14 @@ %n - player name %N - first character of player name - LIVELOG = A bit-mask of types of events that should be written to - the livelog file if one is present. The sample sysconf file accom- - panying the program contains a comment which lists the meaning of - the various bits used. Intended for server systems supporting + LIVELOG = A bit-mask of types of events that should be written to + the livelog file if one is present. The sample sysconf file accom- + panying the program contains a comment which lists the meaning of + the various bits used. Intended for server systems supporting simultaneous play by multiple players (to be clear, each one running - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -6942,40 +6942,40 @@ CRASHREPORTURL = If set to https://www.nethack.org/links/cr-37BETA.html and support is compiled - in, brings up a browser window pre-populated with the information - needed to report a problem if the game panics or ends up in an - internally inconsistent state, or if the #bugreport command is + in, brings up a browser window pre-populated with the information + needed to report a problem if the game panics or ends up in an + internally inconsistent state, or if the #bugreport command is invoked. 10. Scoring - NetHack maintains a list of the top scores or scorers on your - machine, depending on how it is set up. In the latter case, each - account on the machine can post only one non-winning score on this - list. If you score higher than someone else on this list, or better - your previous score, you will be inserted in the proper place under - your current name. How many scores are kept can also be set up when + NetHack maintains a list of the top scores or scorers on your + machine, depending on how it is set up. In the latter case, each + account on the machine can post only one non-winning score on this + list. If you score higher than someone else on this list, or better + your previous score, you will be inserted in the proper place under + your current name. How many scores are kept can also be set up when NetHack is compiled. - Your score is chiefly based upon how much experience you gained, + Your score is chiefly based upon how much experience you gained, how much loot you accumulated, how deep you explored, and how the game ended. If you quit the game, you escape with all of your gold intact. - If, however, you get killed in the Mazes of Menace, the guild will - only hear about 90% of your gold when your corpse is discovered - (adventurers have been known to collect finder's fees). So, consider - whether you want to take one last hit at that monster and possibly - live, or quit and stop with whatever you have. If you quit, you keep + If, however, you get killed in the Mazes of Menace, the guild will + only hear about 90% of your gold when your corpse is discovered + (adventurers have been known to collect finder's fees). So, consider + whether you want to take one last hit at that monster and possibly + live, or quit and stop with whatever you have. If you quit, you keep all your gold, but if you swing and live, you might find more. - If you just want to see what the current top players/games list + If you just want to see what the current top players/games list is, you can type nethack -s all on most versions. 11. Explore mode NetHack is an intricate and difficult game. Novices might falter in fear, aware of their ignorance of the means to survive. Well, fear - not. Your dungeon comes equipped with an "explore" or "discovery" - mode that enables you to keep old save files and cheat death, at the + not. Your dungeon comes equipped with an "explore" or "discovery" + mode that enables you to keep old save files and cheat death, at the paltry cost of not getting on the high score list. There are two ways of enabling explore mode. One is to start the @@ -6990,7 +6990,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -7003,12 +7003,12 @@ 11.1. Debug mode Debug mode, also known as wizard mode, is undocumented aside from - this brief description and the various "debug mode only" commands - listed among the command descriptions. It is intended for tracking - down problems within the program rather than to provide god-like pow- - ers to your character, and players who attempt debugging are expected - to figure out how to use it themselves. It is initiated by starting - the game with the -D command-line switch or with the playmode:debug + this brief description and the various "debug mode only" commands + listed among the command descriptions. It is intended for tracking + down problems within the program rather than to provide god-like pow- + ers to your character, and players who attempt debugging are expected + to figure out how to use it themselves. It is initiated by starting + the game with the -D command-line switch or with the playmode:debug option. For some systems, the player must be logged in under a particular @@ -7022,41 +7022,41 @@ The original hack game was modeled on the Berkeley UNIX rogue game. Large portions of this document were shamelessly cribbed from A - Guide to the Dungeons of Doom, by Michael C. Toy and Kenneth C. R. C. - Arnold. Small portions were adapted from Further Exploration of the + Guide to the Dungeons of Doom, by Michael C. Toy and Kenneth C. R. C. + Arnold. Small portions were adapted from Further Exploration of the Dungeons of Doom, by Ken Arromdee. - NetHack is the product of literally scores of people's work. + NetHack is the product of literally scores of people's work. Main events in the course of the game development are described below: - Jay Fenlason wrote the original Hack, with help from Kenny Wood- + Jay Fenlason wrote the original Hack, with help from Kenny Wood- land, Mike Thome, and Jon Payne. - Andries Brouwer did a major re-write while at Stichting Mathema- - tisch Centrum (now Centrum Wiskunde & Informatica), transforming Hack + Andries Brouwer did a major re-write while at Stichting Mathema- + tisch Centrum (now Centrum Wiskunde & Informatica), transforming Hack into a very different game. He published the Hack source code for use on UNIX systems by posting that to Usenet newsgroup net.sources (later - renamed comp.sources) releasing version 1.0 in December of 1984, then - versions 1.0.1, 1.0.2, and finally 1.0.3 in July of 1985. Usenet - newsgroup net.games.hack (later renamed rec.games.hack, eventually - replaced by rec.games.roguelike.nethack) was created for discussing + renamed comp.sources) releasing version 1.0 in December of 1984, then + versions 1.0.1, 1.0.2, and finally 1.0.3 in July of 1985. Usenet + newsgroup net.games.hack (later renamed rec.games.hack, eventually + replaced by rec.games.roguelike.nethack) was created for discussing it. - Don G. Kneller ported Hack 1.0.3 to Microsoft C and MS-DOS, pro- - ducing PC HACK 1.01e, added support for DEC Rainbow graphics in ver- - sion 1.03g, and went on to produce at least four more versions (3.0, - 3.2, 3.51, and 3.6; note that these are old Hack version numbers, not + Don G. Kneller ported Hack 1.0.3 to Microsoft C and MS-DOS, pro- + ducing PC HACK 1.01e, added support for DEC Rainbow graphics in ver- + sion 1.03g, and went on to produce at least four more versions (3.0, + 3.2, 3.51, and 3.6; note that these are old Hack version numbers, not contemporary NetHack ones). - R. Black ported PC HACK 3.51 to Lattice C and the Atari + R. Black ported PC HACK 3.51 to Lattice C and the Atari 520/1040ST, producing ST Hack 1.03. - Mike Stephenson merged these various versions back together, + Mike Stephenson merged these various versions back together, incorporating many of the added features, and produced NetHack version 1.4 in 1987. He then coordinated a cast of thousands in enhancing and - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -7073,35 +7073,35 @@ Later, Mike coordinated a major re-write of the game, heading a team which included Ken Arromdee, Jean-Christophe Collet, Steve Creps, - Eric Hendrickson, Izchak Miller, Eric S. Raymond, John Rupley, Mike + Eric Hendrickson, Izchak Miller, Eric S. Raymond, John Rupley, Mike Threepoint, and Janet Walz, to produce NetHack 3.0c. - NetHack 3.0 was ported to the Atari by Eric R. Smith, to OS/2 by - Timo Hakulinen, and to VMS by David Gentzel. The three of them and - Kevin Darcy later joined the main NetHack Development Team to produce + NetHack 3.0 was ported to the Atari by Eric R. Smith, to OS/2 by + Timo Hakulinen, and to VMS by David Gentzel. The three of them and + Kevin Darcy later joined the main NetHack Development Team to produce subsequent revisions of 3.0. - Olaf Seibert ported NetHack 2.3 and 3.0 to the Amiga. Norm - Meluch, Stephen Spackman and Pierre Martineau designed overlay code - for PC NetHack 3.0. Johnny Lee ported NetHack 3.0 to the Macintosh. - Along with various other Dungeoneers, they continued to enhance the + Olaf Seibert ported NetHack 2.3 and 3.0 to the Amiga. Norm + Meluch, Stephen Spackman and Pierre Martineau designed overlay code + for PC NetHack 3.0. Johnny Lee ported NetHack 3.0 to the Macintosh. + Along with various other Dungeoneers, they continued to enhance the PC, Macintosh, and Amiga ports through the later revisions of 3.0. - Version 3.0 went through ten relatively rapidly released "patch- + Version 3.0 went through ten relatively rapidly released "patch- level" revisions. Versions at the time were known as 3.0 for the base release and variously as "3.0a" through "3.0j", "3.0 patchlevel 1" through "3.0 patchlevel 10", or "3.0pl1" through "3.0pl10" rather than - 3.0.0 and 3.0.1 through 3.0.10; the three component numbering scheme + 3.0.0 and 3.0.1 through 3.0.10; the three component numbering scheme began to be used with 3.1.0. - Headed by Mike Stephenson and coordinated by Izchak Miller and - Janet Walz, the NetHack Development Team which now included Ken - Arromdee, David Cohrs, Jean-Christophe Collet, Kevin Darcy, Matt Day, - Timo Hakulinen, Steve Linhart, Dean Luick, Pat Rankin, Eric Raymond, - and Eric Smith undertook a radical revision of 3.0. They re-struc- - tured the game's design, and re-wrote major parts of the code. They - added multiple dungeons, a new display, special individual character - quests, a new endgame and many other new features, and produced + Headed by Mike Stephenson and coordinated by Izchak Miller and + Janet Walz, the NetHack Development Team which now included Ken + Arromdee, David Cohrs, Jean-Christophe Collet, Kevin Darcy, Matt Day, + Timo Hakulinen, Steve Linhart, Dean Luick, Pat Rankin, Eric Raymond, + and Eric Smith undertook a radical revision of 3.0. They re-struc- + tured the game's design, and re-wrote major parts of the code. They + added multiple dungeons, a new display, special individual character + quests, a new endgame and many other new features, and produced NetHack 3.1. Version 3.1.0 was released in January of 1993. Ken Lorber, Gregg Wonderly and Greg Olson, with help from Richard @@ -7113,16 +7113,16 @@ 3.1 to the PC. Jon W{tte and Hao-yang Wang, with help from Ross Brown, Mike Eng- - ber, David Hairston, Michael Hamel, Jonathan Handler, Johnny Lee, Tim - Lennan, Rob Menke, and Andy Swanson, developed NetHack 3.1 for the - Macintosh, porting it for MPW. Building on their development, Bart + ber, David Hairston, Michael Hamel, Jonathan Handler, Johnny Lee, Tim + Lennan, Rob Menke, and Andy Swanson, developed NetHack 3.1 for the + Macintosh, porting it for MPW. Building on their development, Bart House added a Think C port. - Timo Hakulinen ported NetHack 3.1 to OS/2. Eric Smith ported + Timo Hakulinen ported NetHack 3.1 to OS/2. Eric Smith ported NetHack 3.1 to the Atari. Pat Rankin, with help from Joshua - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -7132,7 +7132,7 @@ - Delahunty, was responsible for the VMS version of NetHack 3.1. + Delahunty, was responsible for the VMS version of NetHack 3.1. Michael Allison ported NetHack 3.1 to Windows NT. Dean Luick, with help from David Cohrs, developed NetHack 3.1 for @@ -7140,31 +7140,31 @@ nh10.bdf, an optionally used custom X11 font which has tiny images in place of letters and punctuation, a precursor of tiles. Those images don't extend to individual monster and object types, just replacements - for monster and object classes (so one custom image for all "a" - insects and another for all "[" armor and so forth, not separate + for monster and object classes (so one custom image for all "a" + insects and another for all "[" armor and so forth, not separate images for beetles and ants or for cloaks and boots). - Warwick Allison wrote a graphically displayed version of NetHack - for the Atari where the tiny pictures were described as "icons" and - were distinct for specific types of monsters and objects rather than - just their classes. He contributed them to the NetHack Development - Team which rechristened them "tiles", original usage which has subse- - quently been picked up by various other games. NetHack's tiles sup- - port was then implemented on other platforms (initially MS-DOS but + Warwick Allison wrote a graphically displayed version of NetHack + for the Atari where the tiny pictures were described as "icons" and + were distinct for specific types of monsters and objects rather than + just their classes. He contributed them to the NetHack Development + Team which rechristened them "tiles", original usage which has subse- + quently been picked up by various other games. NetHack's tiles sup- + port was then implemented on other platforms (initially MS-DOS but eventually Windows, Qt, and X11 too). - The 3.2 NetHack Development Team, comprised of Michael Allison, - Ken Arromdee, David Cohrs, Jessie Collet, Steve Creps, Kevin Darcy, - Timo Hakulinen, Steve Linhart, Dean Luick, Pat Rankin, Eric Smith, - Mike Stephenson, Janet Walz, and Paul Winner, released version 3.2.0 + The 3.2 NetHack Development Team, comprised of Michael Allison, + Ken Arromdee, David Cohrs, Jessie Collet, Steve Creps, Kevin Darcy, + Timo Hakulinen, Steve Linhart, Dean Luick, Pat Rankin, Eric Smith, + Mike Stephenson, Janet Walz, and Paul Winner, released version 3.2.0 in April of 1996. - Version 3.2 marked the tenth anniversary of the formation of the + Version 3.2 marked the tenth anniversary of the formation of the development team. In a testament to their dedication to the game, all thirteen members of the original NetHack Development Team remained on the team at the start of work on that release. During the interval between the release of 3.1.3 and 3.2.0, one of the founding members of - the NetHack Development Team, Dr. Izchak Miller, was diagnosed with + the NetHack Development Team, Dr. Izchak Miller, was diagnosed with cancer and passed away. That release of the game was dedicated to him by the development and porting teams. @@ -7177,18 +7177,18 @@ "variants" publicly available: Tom Proudfoot and Yuval Oren created NetHack++, which was quickly - renamed NetHack-- when some people incorrectly assumed that it was a - conversion of the C source code to C++. Working independently, - Stephen White wrote NetHack Plus. Tom Proudfoot later merged NetHack - Plus and his own NetHack-- to produce SLASH. Larry Stewart-Zerba and - Warwick Allison improved the spell casting system with the Wizard + renamed NetHack-- when some people incorrectly assumed that it was a + conversion of the C source code to C++. Working independently, + Stephen White wrote NetHack Plus. Tom Proudfoot later merged NetHack + Plus and his own NetHack-- to produce SLASH. Larry Stewart-Zerba and + Warwick Allison improved the spell casting system with the Wizard Patch. Warwick Allison also ported NetHack to use the Qt interface. - Warren Cheung combined SLASH with the Wizard Patch to produce + Warren Cheung combined SLASH with the Wizard Patch to produce Slash'EM, and with the help of Kevin Hugo, added more features. Kevin - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -7201,10 +7201,10 @@ later joined the NetHack Development Team and incorporated the best of these ideas into NetHack 3.3. - The final update to 3.2 was the bug fix release 3.2.3, which was - released simultaneously with 3.3.0 in December 1999 just in time for - the Year 2000. Because of the newer version, 3.2.3 was released as a - source code patch only, without any ready-to-play distribution for + The final update to 3.2 was the bug fix release 3.2.3, which was + released simultaneously with 3.3.0 in December 1999 just in time for + the Year 2000. Because of the newer version, 3.2.3 was released as a + source code patch only, without any ready-to-play distribution for systems that usually had such. (To anyone considering resurrecting an old version: all versions @@ -7213,48 +7213,48 @@ 1999's year 99 was followed by 2000's year 100. That got written out successfully but it unintentionally introduced an extra column in the file layout which prevented score entries from being read back in cor- - rectly, interfering with insertion of new high scores and with - retrieval of old character names to use for random ghost and statue + rectly, interfering with insertion of new high scores and with + retrieval of old character names to use for random ghost and statue names in the current game.) - The 3.3 NetHack Development Team, consisting of Michael Allison, - Ken Arromdee, David Cohrs, Jessie Collet, Steve Creps, Kevin Darcy, + The 3.3 NetHack Development Team, consisting of Michael Allison, + Ken Arromdee, David Cohrs, Jessie Collet, Steve Creps, Kevin Darcy, Timo Hakulinen, Kevin Hugo, Steve Linhart, Ken Lorber, Dean Luick, Pat Rankin, Eric Smith, Mike Stephenson, Janet Walz, and Paul Winner, released 3.3.0 in December 1999 and 3.3.1 in August of 2000. Version 3.3 offered many firsts. It was the first version to sep- - arate race and profession. The Elf class was removed in preference to - an elf race, and the races of dwarves, gnomes, and orcs made their - first appearance in the game alongside the familiar human race. Monk - and Ranger roles joined Archeologists, Barbarians, Cavemen, Healers, - Knights, Priests, Rogues, Samurai, Tourists, Valkyries and of course, - Wizards. It was also the first version to allow you to ride a steed, - and was the first version to have a publicly available web-site list- - ing all the bugs that had been discovered. Despite that constantly - growing bug list, 3.3 proved stable enough to last for more than a + arate race and profession. The Elf class was removed in preference to + an elf race, and the races of dwarves, gnomes, and orcs made their + first appearance in the game alongside the familiar human race. Monk + and Ranger roles joined Archeologists, Barbarians, Cavemen, Healers, + Knights, Priests, Rogues, Samurai, Tourists, Valkyries and of course, + Wizards. It was also the first version to allow you to ride a steed, + and was the first version to have a publicly available web-site list- + ing all the bugs that had been discovered. Despite that constantly + growing bug list, 3.3 proved stable enough to last for more than a year and a half. - The 3.4 NetHack Development Team initially consisted of Michael - Allison, Ken Arromdee, David Cohrs, Jessie Collet, Kevin Hugo, Ken - Lorber, Dean Luick, Pat Rankin, Mike Stephenson, Janet Walz, and Paul - Winner, with Warwick Allison joining just before the release of + The 3.4 NetHack Development Team initially consisted of Michael + Allison, Ken Arromdee, David Cohrs, Jessie Collet, Kevin Hugo, Ken + Lorber, Dean Luick, Pat Rankin, Mike Stephenson, Janet Walz, and Paul + Winner, with Warwick Allison joining just before the release of NetHack 3.4.0 in March 2002. - As with version 3.3, various people contributed to the game as a - whole as well as supporting ports on the different platforms that + As with version 3.3, various people contributed to the game as a + whole as well as supporting ports on the different platforms that NetHack runs on: Pat Rankin maintained 3.4 for VMS. - Michael Allison maintained NetHack 3.4 for the MS-DOS platform. + Michael Allison maintained NetHack 3.4 for the MS-DOS platform. Paul Winner and Yitzhak Sapir provided encouragement. - Dean Luick, Mark Modrall, and Kevin Hugo maintained and enhanced + Dean Luick, Mark Modrall, and Kevin Hugo maintained and enhanced the Macintosh port of 3.4. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -7264,18 +7264,18 @@ - Michael Allison, David Cohrs, Alex Kompel, Dion Nicolaas, and - Yitzhak Sapir maintained and enhanced 3.4 for the Microsoft Windows - platform. Alex Kompel contributed a new graphical interface for the - Windows port. Alex Kompel also contributed a Windows CE port for + Michael Allison, David Cohrs, Alex Kompel, Dion Nicolaas, and + Yitzhak Sapir maintained and enhanced 3.4 for the Microsoft Windows + platform. Alex Kompel contributed a new graphical interface for the + Windows port. Alex Kompel also contributed a Windows CE port for 3.4.1. - Ron Van Iwaarden was the sole maintainer of NetHack for OS/2 the - past several releases. Unfortunately Ron's last OS/2 machine stopped - working in early 2006. A great many thanks to Ron for keeping NetHack + Ron Van Iwaarden was the sole maintainer of NetHack for OS/2 the + past several releases. Unfortunately Ron's last OS/2 machine stopped + working in early 2006. A great many thanks to Ron for keeping NetHack alive on OS/2 all these years. - Janne Salmijarvi and Teemu Suikki maintained and enhanced the + Janne Salmijarvi and Teemu Suikki maintained and enhanced the Amiga port of 3.4 after Janne Salmijarvi resurrected it for 3.3.1. Christian "Marvin" Bressler maintained 3.4 for the Atari after he @@ -7285,42 +7285,42 @@ ning of a long release hiatus. 3.4.3 proved to be a remarkably stable version that provided continued enjoyment by the community for more than a decade. The NetHack Development Team slowly and quietly contin- - ued to work on the game behind the scenes during the tenure of 3.4.3. - It was during that same period that several new variants emerged - within the NetHack community. Notably sporkhack by Derek S. Ray, - unnethack by Patric Mueller, nitrohack and its successors originally - by Daniel Thaler and then by Alex Smith, and Dynahack by Tung Nguyen. - Some of those variants continue to be developed, maintained, and + ued to work on the game behind the scenes during the tenure of 3.4.3. + It was during that same period that several new variants emerged + within the NetHack community. Notably sporkhack by Derek S. Ray, + unnethack by Patric Mueller, nitrohack and its successors originally + by Daniel Thaler and then by Alex Smith, and Dynahack by Tung Nguyen. + Some of those variants continue to be developed, maintained, and enjoyed by the community to this day. In September 2014, an interim snapshot of the code under develop- ment was released publicly by other parties. Since that code was a work-in-progress and had not gone through the process of debugging it as a suitable release, it was decided that the version numbers present - on that code snapshot would be retired and never used in an official - NetHack release. An announcement was posted on the NetHack Develop- - ment Team's official nethack.org website to that effect, stating that + on that code snapshot would be retired and never used in an official + NetHack release. An announcement was posted on the NetHack Develop- + ment Team's official nethack.org website to that effect, stating that there would never be a 3.4.4, 3.5, or 3.5.0 official release version. - In January 2015, preparation began for the release of NetHack + In January 2015, preparation began for the release of NetHack 3.6. - At the beginning of development for what would eventually get - released as 3.6.0, the NetHack Development Team consisted of Warwick - Allison, Michael Allison, Ken Arromdee, David Cohrs, Jessie Collet, - Ken Lorber, Dean Luick, Pat Rankin, Mike Stephenson, Janet Walz, and - Paul Winner. In early 2015, ahead of the release of 3.6.0, new mem- - bers Sean Hunt, Pasi Kallinen, and Derek S. Ray joined the NetHack + At the beginning of development for what would eventually get + released as 3.6.0, the NetHack Development Team consisted of Warwick + Allison, Michael Allison, Ken Arromdee, David Cohrs, Jessie Collet, + Ken Lorber, Dean Luick, Pat Rankin, Mike Stephenson, Janet Walz, and + Paul Winner. In early 2015, ahead of the release of 3.6.0, new mem- + bers Sean Hunt, Pasi Kallinen, and Derek S. Ray joined the NetHack Development Team. - Near the end of the development of 3.6.0, one of the significant - inspirations for many of the humorous and fun features found in the + Near the end of the development of 3.6.0, one of the significant + inspirations for many of the humorous and fun features found in the game, author Terry Pratchett, passed away. NetHack 3.6.0 introduced a tribute to him. - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -7365,17 +7365,17 @@ In early May 2019, another 320 bug fixes along with some enhance- ments and the adopted curses window port, were released as 3.6.2. - Bart House, who had contributed to the game as a porting team - participant for decades, joined the NetHack Development Team in late + Bart House, who had contributed to the game as a porting team + participant for decades, joined the NetHack Development Team in late May 2019. - NetHack 3.6.3 was released on December 5, 2019 containing over + NetHack 3.6.3 was released on December 5, 2019 containing over 190 bug fixes to NetHack 3.6.2. - NetHack 3.6.4 was released on December 18, 2019 containing a + NetHack 3.6.4 was released on December 18, 2019 containing a security fix and a few bug fixes. - NetHack 3.6.5 was released on January 27, 2020 containing some + NetHack 3.6.5 was released on January 27, 2020 containing some security fixes and a small number of bug fixes. NetHack 3.6.6 was released on March 8, 2020 containing a security @@ -7386,7 +7386,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -7452,7 +7452,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -7518,7 +7518,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -7584,7 +7584,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 @@ -7650,7 +7650,7 @@ - NetHack 3.7.0 December 05, 2025 + NetHack 3.7.0 December 11, 2025 From e18cf594d388aaa014acea33b0f06178165e2f8a Mon Sep 17 00:00:00 2001 From: Michael Meyer Date: Thu, 8 Jan 2026 11:18:23 -0500 Subject: [PATCH 204/442] Avoid redundant punctuation in engraving message Change the formatting of reading an engraving to include a terminal period only if the engraving does not already include punctuation, to avoid messages like: | You feel the words: "Please don't feed the animals.". This brings the formatting of the read_engr_at() message in line with doread(). One minor concern with this is that degraded engravings can use punctuation to represent "chicken scratch" degraded text rather than as actual punctuation, and ideally it might be better to include the final period if you're reading an engraving like "Hc| ? |?". This is true of burnt T-shirts already, but it's much more common with engravings. It should be possible to identify "real punctuation" by checking whether (ep->engr_txt[pristine_text] == et[elen - 1]) -- but this doesn't actually work without more tinkering, since trimming initial whitespace in u_wipe_engr() updates the actual_text pointer so the indices stop matching. --- src/engrave.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/engrave.c b/src/engrave.c index e029e87dc..40c3f95b1 100644 --- a/src/engrave.c +++ b/src/engrave.c @@ -370,18 +370,26 @@ read_engr_at(coordxy x, coordxy y) if (sensed) { char *et, buf[BUFSZ]; + const char *endpunct; int maxelen = (int) (sizeof buf /* sizeof "literal" counts terminating \0 */ - - sizeof "You feel the words: \"\"."); + - sizeof "You feel the words: \"\"."), + elen = (int) strlen(ep->engr_txt[actual_text]); - if ((int) strlen(ep->engr_txt[actual_text]) > maxelen) { + if (elen > maxelen) { (void) strncpy(buf, ep->engr_txt[actual_text], maxelen); buf[maxelen] = '\0'; et = buf; + elen = maxelen; } else { et = ep->engr_txt[actual_text]; } - You("%s: \"%s\".", (Blind) ? "feel the words" : "read", et); + endpunct = ""; + if (elen > 0 && !strchr(".!?", et[elen - 1])) { + endpunct = "."; + } + You("%s: \"%s\"%s", (Blind) ? "feel the words" : "read", et, + endpunct); Strcpy(ep->engr_txt[remembered_text], ep->engr_txt[actual_text]); ep->eread = 1; ep->erevealed = 1; From c92014bf3c008f886b1052deeabaeec60313b775 Mon Sep 17 00:00:00 2001 From: Michael Meyer Date: Thu, 8 Jan 2026 14:18:25 -0500 Subject: [PATCH 205/442] Follow-up to engraving punctuation Skip the terminal period only if there is true punctuation at the end of the engraving, not degraded text. This feels a bit janky because the way engravings are malloced and structured uses this manual offset to access the space allocated for text. I used a macro to unify all those accesses so that it will be harder to screw it up if something changes in that respect, since repeating (ep + 1) as a magic number across engrave.c seems quite brittle. --- include/engrave.h | 2 ++ src/engrave.c | 15 ++++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/include/engrave.h b/include/engrave.h index 47fd84783..33ee19c80 100644 --- a/include/engrave.h +++ b/include/engrave.h @@ -13,6 +13,8 @@ enum engraving_texts { text_states }; +#define engr_text_space(ep) ((char *) ((ep) + 1)) + struct engr { struct engr *nxt_engr; char *engr_txt[text_states]; diff --git a/src/engrave.c b/src/engrave.c index 40c3f95b1..c84908cf1 100644 --- a/src/engrave.c +++ b/src/engrave.c @@ -374,7 +374,8 @@ read_engr_at(coordxy x, coordxy y) int maxelen = (int) (sizeof buf /* sizeof "literal" counts terminating \0 */ - sizeof "You feel the words: \"\"."), - elen = (int) strlen(ep->engr_txt[actual_text]); + elen = (int) strlen(ep->engr_txt[actual_text]), + off = (int) (ep->engr_txt[actual_text] - engr_text_space(ep)); if (elen > maxelen) { (void) strncpy(buf, ep->engr_txt[actual_text], maxelen); @@ -385,7 +386,11 @@ read_engr_at(coordxy x, coordxy y) et = ep->engr_txt[actual_text]; } endpunct = ""; - if (elen > 0 && !strchr(".!?", et[elen - 1])) { + if (elen < 2 + /* only skip if punctuation is original, not degraded char */ + || !((ep->engr_txt[pristine_text][off + elen - 1] + == et[elen - 1]) + && strchr(".!?", et[elen - 1]))) { endpunct = "."; } You("%s: \"%s\"%s", (Blind) ? "feel the words" : "read", et, @@ -427,7 +432,7 @@ make_engr_at( head_engr = ep; ep->engr_x = x; ep->engr_y = y; - ep->engr_txt[actual_text] = (char *) (ep + 1); + ep->engr_txt[actual_text] = engr_text_space(ep); ep->engr_txt[remembered_text] = ep->engr_txt[actual_text] + smem; ep->engr_txt[pristine_text] = ep->engr_txt[remembered_text] + smem; for(i = 0; i < text_states; ++i) @@ -1556,7 +1561,7 @@ save_engravings(NHFILE *nhfp) szeach = ep->engr_szeach; Sfo_unsigned(nhfp, &engr_alloc, "engraving-engr_alloc"); Sfo_engr(nhfp, ep, "engraving"); - ep->engr_txt[actual_text] = (char *)(ep + 1); + ep->engr_txt[actual_text] = engr_text_space(ep); ep->engr_txt[remembered_text] = ep->engr_txt[actual_text] + szeach; ep->engr_txt[pristine_text] = ep->engr_txt[remembered_text] + szeach; Sfo_char(nhfp, ep->engr_txt[actual_text], "engraving-actual_text", szeach); @@ -1591,7 +1596,7 @@ rest_engravings(NHFILE *nhfp) szeach = ep->engr_szeach; ep->nxt_engr = head_engr; head_engr = ep; - ep->engr_txt[actual_text] = (char *) (ep + 1); /* Andreas Bormann */ + ep->engr_txt[actual_text] = engr_text_space(ep); /* Andreas Bormann */ ep->engr_txt[remembered_text] = ep->engr_txt[actual_text] + szeach; ep->engr_txt[pristine_text] = ep->engr_txt[remembered_text] + szeach; Sfi_char(nhfp, ep->engr_txt[actual_text], From 4e0a91907b97a2d2c55ca9ae61e9be5ac733a7c1 Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 13 Jan 2026 11:04:03 -0800 Subject: [PATCH 206/442] fix pull request #1476 - engraving punctuation Pull request from entrez: reading an engraving added a terminal period after the quoted text even when that text already ended in one. That should be conditional like it already is (post-3.6) for T-shirts. Fixes #1476 --- doc/fixes3-7-0.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 1d7427bf7..913448953 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -2190,6 +2190,10 @@ naming a type of item, unnaming it (with name of ), naming it again impossible: "named object not in disco" throwing entire quiver and then picking the stack back up was not putting it back into quiver +when reading a T-shirt or apron, add trailing period to the slogan unless + already present in the quoted text +when reading an engraving, suppress the addition of a trailing period if the + text already has one Fixes to 3.7.0-x Platform and/or Interface Problems Exposed Via git Repository From 96fedbdeab1754c0e07f5018bed1a577e3b9fb95 Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 14 Jan 2026 09:34:50 -0500 Subject: [PATCH 207/442] update generated files from cron daily --- doc/dlb.txt | 70 +++++++++++++++++++++++++++-------------------------- doc/mnh.txt | 2 ++ 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/doc/dlb.txt b/doc/dlb.txt index 412c19820..7c021e273 100644 --- a/doc/dlb.txt +++ b/doc/dlb.txt @@ -6,47 +6,47 @@ NAME dlb - NetHack data librarian SYNOPSIS - dlb { xct } [ vfIC ] arguments... [ files... ] + dlb {c|t|x}[v][C directory] [file] ... + + dlb {c|t|x}[v]I list-file + + dlb {c|t|x}[v][f archive-file-name] [file] ... DESCRIPTION - Dlb is a file archiving tool in the spirit (and tradition) of tar for - NetHack version 3.1 and higher. It is used to maintain the archive - files from which NetHack reads special level files and other read-only - information. Note that like tar the command and option specifiers are - specified as a continuous string and are followed by any arguments - required in the same order as the option specifiers. + Dlb is a file archiving tool in the spirit (and tradition) of tar(1) + for nethack(6) version 3.1 and higher. It is used to maintain the + archive files from which the game reads special level files and other + read-only information. Note that like tar, the letters specifying the + operation and options are expressed as a continuous string. Unlike + tar, dlb is configured with a default set of file names to process. - This facility is optional and may be excluded during NetHack configura- - tion. + Operations + c causes dlb to create a new archive from files in the current direc- + tory. -COMMANDS - The x command causes dlb to extract the contents of the archive into - the current directory. + t lists the files in the archive. - The c command causes dlb to create a new archive from files in the cur- - rent directory. + x causes dlb to extract the contents of the archive into the current + directory. - The t command lists the files in the archive. +OPTIONS + C dir Change directory to dir before trying to read any files. -OPTIONS AND ARGUMENTS - v verbose output + f archive Read from or write to archive instead of LIBFILE (usually + the nhdat file in the playground). - f archive specify the archive. Default if f not specified is LIBFILE - (usually the nhdat file in the playground). + I list-file Read from list-file the names of files to emplace within + or extract from the archive. The default for archive cre- + ation is LIBLISTFILE. - I lfile specify the file containing the list of files to put in to - or extract from the archive if no files are listed on the command line. - Default for archive creation if no files are listed is LIBLISTFILE. - - C dir change directory. Changes directory before trying to read - any files (including the archive and the lfile). + v Operate verbosely. EXAMPLES Create the default archive from the default file list: - dlb c + dlb c - List the contents of the archive 'foo': - dlb tf foo + List the contents of the archive foo: + dlb tf foo AUTHOR Kenneth Lorber @@ -55,15 +55,17 @@ SEE ALSO nethack(6), tar(1) BUGS - Not a good tar emulation; - does not mean stdin or stdout. Should - include an optional compression facility. Not all read-only files for - NetHack can be read out of an archive; examining the source is the only - way to know which files can be. + o Not a good tar emulation; - does not mean stdin or stdout. + + o Should include an optional compression facility. + + o Not all read-only files for NetHack can be read out of an archive; + examining the source is the only way to know which files can be. COPYRIGHT This file is Copyright (C) Kenneth Lorber, 2024 for version keni-git- - set:1.13. NetHack may be freely redistributed. See license for - details. + set:1.13. NetHack may be freely redistributed. See license for de- + tails. diff --git a/doc/mnh.txt b/doc/mnh.txt index b7e966065..4d78af3d5 100644 --- a/doc/mnh.txt +++ b/doc/mnh.txt @@ -36,6 +36,7 @@ REQUESTS Macro What Initial Note Explanation Name It Is Value +nH num 0 - avoid processing multiple times .BR mac - - hard line break with vertical padding inserted bR num - i .CC x y mac - - aligned one char key x with short definition y @@ -52,6 +53,7 @@ PX num - i PY num - i .SD x mac - - .sd with options c-center i-indent n-no indent SF num - i +UR mac - - URL passthrough for HTML generator .UX mac - - .ux with updated trademark owner From 239286e1fbdba8cd4bf28326656348937c1bf35d Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 14 Jan 2026 14:27:15 -0500 Subject: [PATCH 208/442] update tested versions of Visual Studio 2026-01-14 --- sys/windows/Makefile.nmake | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index faa637986..036710093 100644 --- a/sys/windows/Makefile.nmake +++ b/sys/windows/Makefile.nmake @@ -1,5 +1,5 @@ # NetHack 3.7 Makefile.nmake -# Copyright (c) NetHack Development Team 1993-2025 +# Copyright (c) NetHack Development Team 1993-2026 # #============================================================================== # Build Tools Environment @@ -8,8 +8,8 @@ # MS Visual Studio Visual C++ compiler # # Visual Studio Compilers Tested: -# - Microsoft Visual Studio Community 2022 v 17.14.23 -# - Microsoft Visual Studio Community 2026 v 18.1.1 +# - Microsoft Visual Studio Community 2022 v 17.14.24 +# - Microsoft Visual Studio Community 2026 v 18.2.0 # #============================================================================== # This is used for building two distinct executables of NetHack: @@ -1189,7 +1189,7 @@ rc=Rc.exe # # Recently tested versions: TESTEDVS2022 = 14.44.35222.0 -TESTEDVS2026 = 14.50.35721.0 +TESTEDVS2026 = 14.50.35722.0 VS20261ST = 1450000000 VS2026CUR = $(TESTEDVS2026:.=) From 7881d5b4b2a815011676ce8b52b9d78a44cd6f92 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Wed, 14 Jan 2026 23:21:40 +0000 Subject: [PATCH 209/442] Make air elementals respect damage reduction from AC Players of both 3.4.3 and 3.7 have observed for a long time that air elementals are disproportionately dangerous compared to other endgame threats. (In particular, playtester feedback from 3.7 was that the Plane of Air was much more dangerous than it should be, with playtesters treating it similarly to Astral with respect to the use of high-quality escape items.) It turns out that this was because damage from air elementals while engulfed was entirely ignoring AC, meaning that regardless of your stats, you would be taking around 16.5 damage per turn while engulfed (half physical damage helped, but nothing else did). This commit purely fixes the bug, but does not balance around it, which means that it causes the endgame air elementals to become almost entirely nonthreatening. In a future commit, I plan to balance around this change. --- doc/fixes3-7-0.txt | 2 ++ src/mhitu.c | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 913448953..a53504e4d 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1568,6 +1568,8 @@ throwing ammo without a launcher produces a message polymorphing into a large polyform unequips rather than destroying cloaks clarify in the #quit message that it doesn't save the game cursed potion of invisibility removes intrinsic invisibility +fix bug which caused engulf damage from air elementals and fog clouds to + ignore damage reduction from armor class Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/mhitu.c b/src/mhitu.c index 8599136ed..89f6006dd 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -1535,8 +1535,15 @@ gulpmu(struct monst *mtmp, struct attack *mattk) break; } - if (physical_damage) + if (physical_damage) { + /* same damage reduction for AC as in hitmu */ + if (u.uac < 0) + tmp -= rnd(-u.uac); + if (tmp < 0) + tmp = 1; + tmp = Maybe_Half_Phys(tmp); + } gm.mswallower = mtmp; /* match gulpmm() */ mdamageu(mtmp, tmp); From 253aea33fca7fdfbcd612d2493072342b34aa637 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Thu, 15 Jan 2026 00:06:42 +0000 Subject: [PATCH 210/442] Elemental Planes balance adjustments The previous commit caused air elementals to become almost totally nonthreatening in the endgame (even the buffed ones on the Plane of Air). This commit fixes that (whilst still leaving them somewhat weaker than they were before against characters with good AC), by doubling the damage of home-plane air elementals. The damage of the other home-plane elementals was doubled too, because they were mostly nonthreatening previously. On Fire, this has no real effect as almost any character would be fire-resistant by this point. On Earth, it makes the elementals more of a threat, when they previously weren't. However, it made Water too difficult (albeit more fun, because it became important to avoid letting water elementals swam you). As such, water elementals are made slightly slower to compensate. (My own playtesting indicates that 6 is slightly too fast, but 4 is too slow, so I'm hoping that 5 is the correct value.) --- doc/fixes3-7-0.txt | 2 ++ include/monsters.h | 2 +- src/mhitu.c | 7 +++++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index a53504e4d..17667be2f 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1570,6 +1570,8 @@ clarify in the #quit message that it doesn't save the game cursed potion of invisibility removes intrinsic invisibility fix bug which caused engulf damage from air elementals and fog clouds to ignore damage reduction from armor class +elementals do double damage on their home plane +water elementals move slightly more slowly Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/include/monsters.h b/include/monsters.h index d13cc9178..bf4d30c3d 100644 --- a/include/monsters.h +++ b/include/monsters.h @@ -1600,7 +1600,7 @@ M2_STRONG | M2_NEUTER, 0, 10, CLR_BROWN, EARTH_ELEMENTAL), MON(NAM("water elemental"), S_ELEMENTAL, - LVL(8, 6, 2, 30, 0), (G_NOCORPSE | 1), + LVL(8, 5, 2, 30, 0), (G_NOCORPSE | 1), A(ATTK(AT_CLAW, AD_PHYS, 5, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(2500, 0, MS_SILENT, MZ_HUGE), MR_POISON | MR_STONE, 0, diff --git a/src/mhitu.c b/src/mhitu.c index 89f6006dd..160864c23 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -431,6 +431,13 @@ getmattk( } + /* elementals on their home plane do double damage */ + if (attk != alt_attk_buf && is_home_elemental(mptr)) { + *alt_attk_buf = *attk; + attk = alt_attk_buf; + attk->damn *= 2; + } + return attk; } From b4a3ec49e41c1def16cb4d9bc53f55e6e37bf61f Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Thu, 15 Jan 2026 00:40:42 +0000 Subject: [PATCH 211/442] Make the "totally digested" instadeath timer much faster This is one of the fairest instadeaths in the game (it gives multiple warnings and there are reliable ways to escape it), but is also where most of the threat of purple worms comes from. It was also pretty much nonfunctional: the previously used formula set the timer to somewhere around 50 turns (for a typical character in Gehennom, which is where purple worms are normally encountered). This new formula (which affects only purple worms, as the only monster that engulfs and digests) is based on the same inputs, but produces much smaller numbers, meaning that the instadeath is relevant sometimes. A typical character at purple worm depth will be able to kill faster than the timer if they focus on the purple worm and put some damage on it before they get engulfed, but it is much closer if the purple worm gets the first hit in (possibly requiring the use of escape items, but the common wand of digging works). Hopefully this brings purple worms closer to their intended threat level: previously they were somewhat more nonthreatening than they should have been. --- doc/fixes3-7-0.txt | 1 + src/mhitu.c | 11 +++++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 17667be2f..2f25851a3 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1572,6 +1572,7 @@ fix bug which caused engulf damage from air elementals and fog clouds to ignore damage reduction from armor class elementals do double damage on their home plane water elementals move slightly more slowly +the "totally digested" instadeath timer is much faster Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/mhitu.c b/src/mhitu.c index 160864c23..79b12a737 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -1374,15 +1374,14 @@ gulpmu(struct monst *mtmp, struct attack *mattk) for other swallowings, longer time means more chances for the swallower to attack */ if (mattk->adtyp == AD_DGST) { - tim_tmp = 25 - (int) mtmp->m_lev; - if (tim_tmp > 0) - tim_tmp = rnd(tim_tmp) / 2; - else if (tim_tmp < 0) - tim_tmp = -(rnd(-tim_tmp) / 2); /* having good armor & high constitution makes it take longer for you to be digested, but you'll end up trapped inside for longer too */ - tim_tmp += -u.uac + 10 + (ACURR(A_CON) / 3 - 1); + tim_tmp = (int)ACURR(A_CON) + 10 - (int)u.uac + rn2(20); + if (tim_tmp < 0) + tim_tmp = 0; + tim_tmp /= (int) mtmp->m_lev; + tim_tmp += 3; } else { /* higher level attacker takes longer to eject hero */ tim_tmp = rnd((int) mtmp->m_lev + 10 / 2); From 5d7b7b823a4b187255888b3369b0fef67b69a520 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Thu, 15 Jan 2026 01:01:07 +0000 Subject: [PATCH 212/442] Monster-slowing effects work better against faster enemies Based on the principle that there should always be at least two solutions to any given problem, this allows monster-slowing magic to be an effective solution to fast low-monMR monsters (in particular home-plane air elementals, who after recent commits could reasonably be dealt with using good AC, but I want a second good option to be available, in addition to the existing mediocre options). This helps to make playing with bad AC (for whatever reason) more viable. --- doc/fixes3-7-0.txt | 1 + src/mon.c | 11 ++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 2f25851a3..80edfbc0e 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1573,6 +1573,7 @@ fix bug which caused engulf damage from air elementals and fog clouds to elementals do double damage on their home plane water elementals move slightly more slowly the "totally digested" instadeath timer is much faster +slow monster effects are more effective against faster enemies Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/mon.c b/src/mon.c index 980e4694d..8d8221ae3 100644 --- a/src/mon.c +++ b/src/mon.c @@ -1112,9 +1112,14 @@ mcalcmove( * MFAST's `+ 2' prevents hasted speed 1 from becoming a no-op; * both adjustments have negligible effect on higher speeds. */ - if (mon->mspeed == MSLOW) - mmove = (2 * mmove + 1) / 3; - else if (mon->mspeed == MFAST) + if (mon->mspeed == MSLOW) { + /* slow-monster effects work better against faster monsters: they + lose 1/3 of their speed below 12 but 2/3 of their speed above */ + if (mmove < 12) + mmove = (2 * mmove + 1) / 3; + else + mmove = 4 + (mmove / 3); + } else if (mon->mspeed == MFAST) mmove = (4 * mmove + 2) / 3; if (mon == u.usteed && u.ugallop && svc.context.mv) { From a4e032762f45f18700bba058fdd37caa0b53ec29 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Thu, 15 Jan 2026 18:44:46 +0200 Subject: [PATCH 213/442] Rope golems may death drop grappling hooks --- doc/fixes3-7-0.txt | 2 +- src/mon.c | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 80edfbc0e..c284200c9 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -837,7 +837,7 @@ if a lit potion of oil on the floor was launched by an explosion and it hit it could trigger an "obj_is_local" panic when end of game cleanup tried to extinguish it as a light source place_object() validated coordinates after using them to index level.objects -killed rope golem may drop leashes and bullwhips +killed rope golem may drop leashes, bullwhips, and grappling hooks using magic portals stuns hero for a few turns using level teleporters confuses hero without teleport control for a few turns clear obj->bypass for buried objects [a giant on ice triggers a fire trap, diff --git a/src/mon.c b/src/mon.c index 8d8221ae3..2b88df964 100644 --- a/src/mon.c +++ b/src/mon.c @@ -667,7 +667,9 @@ make_corpse(struct monst *mtmp, unsigned int corpseflags) case PM_ROPE_GOLEM: num = rn2(3); while (num-- > 0) { - obj = mksobj_at(rn2(2) ? LEASH : BULLWHIP, x, y, TRUE, FALSE); + obj = mksobj_at(rn2(2) ? LEASH + : rn2(3) ? BULLWHIP : GRAPPLING_HOOK, + x, y, TRUE, FALSE); } free_mgivenname(mtmp); break; From eed952a095b74039790c073eb7ceb7406a61719d Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Thu, 15 Jan 2026 19:02:40 +0200 Subject: [PATCH 214/442] Add more death drops to leather golems In addition to the leather armor, leather golems can now drop leather cloaks and saddles. --- doc/fixes3-7-0.txt | 1 + src/mon.c | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index c284200c9..ec81cb872 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -838,6 +838,7 @@ if a lit potion of oil on the floor was launched by an explosion and it hit tried to extinguish it as a light source place_object() validated coordinates after using them to index level.objects killed rope golem may drop leashes, bullwhips, and grappling hooks +killed leather golem may drop leather cloaks, and saddles using magic portals stuns hero for a few turns using level teleporters confuses hero without teleport control for a few turns clear obj->bypass for buried objects [a giant on ice triggers a fire trap, diff --git a/src/mon.c b/src/mon.c index 2b88df964..883db340c 100644 --- a/src/mon.c +++ b/src/mon.c @@ -676,7 +676,9 @@ make_corpse(struct monst *mtmp, unsigned int corpseflags) case PM_LEATHER_GOLEM: num = d(2, 4); while (num--) - obj = mksobj_at(LEATHER_ARMOR, x, y, TRUE, FALSE); + obj = mksobj_at(rn2(4) ? LEATHER_ARMOR + : rn2(3) ? LEATHER_CLOAK : SADDLE, + x, y, TRUE, FALSE); free_mgivenname(mtmp); break; case PM_GOLD_GOLEM: From 8b6ba69fd80aece5e5326c7ee596b77c546abcd5 Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 15 Jan 2026 17:02:38 -0500 Subject: [PATCH 215/442] linux.370 hints: validate sysconf GDBPATH With sys/unix/hints/linux.370, if GDBPATH doesn't exist, comment out the GDBPATH line in sysconf during 'make install' or 'make update'. [ macOS apparently uses sys/unix/hints/macosx.sh to do its sysconf manipulation, so there is no corresponding change for sys/unix/hints/macOS.370 ] Closes #1477 --- sys/unix/Makefile.top | 2 ++ sys/unix/hints/linux.370 | 14 ++++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/sys/unix/Makefile.top b/sys/unix/Makefile.top index af0a966ef..c108f3552 100644 --- a/sys/unix/Makefile.top +++ b/sys/unix/Makefile.top @@ -440,6 +440,8 @@ update: $(PRECHECK) $(GAME) recover $(VARDAT) spec_levs sys/unix/sysconf touch $(VARDIR)/perm $(VARDIR)/record # sysconf, but only if it does not exist true; $(SYSCONFENSURE) +# other steps from hints file + true; $(POSTUPDATE) # and a reminder @echo You may also want to install the man pages via the doc Makefile. diff --git a/sys/unix/hints/linux.370 b/sys/unix/hints/linux.370 index 4f49e9921..3ceb7cda3 100755 --- a/sys/unix/hints/linux.370 +++ b/sys/unix/hints/linux.370 @@ -390,13 +390,15 @@ endif #?WANT_SOURCE_INSTALL INSTDIR=$(HACKDIR) VARDIR = $(HACKDIR) -ifneq "$(CCISCLANG)" "" -# gdb may not be installed if clang is chosen compiler so the game -# won't start in that case due to a sysconf error. Comment out -# relevant lines in sysconf. -POSTINSTALL+= sed -i -e 's;^GDBPATH=/usr/bin/gdb;\#GDBPATH=/usr/bin/gdb;' \ +ifdef MAKEFILE_TOP +TESTGDBPATH=/usr/bin/gdb +POSTINSTALL+= test -f $(TESTGDBPATH) || \ + sed -i -e 's;^GDBPATH=/usr/bin/gdb;\#GDBPATH=/usr/bin/gdb;' \ -e 's;PANICTRACE_GDB=1;PANICTRACE_GDB=0;' $(INSTDIR)/sysconf; -endif +POSTUPDATE+= test -f $(TESTGDBPATH) || \ + sed -i -e 's;^GDBPATH=/usr/bin/gdb;\#GDBPATH=/usr/bin/gdb;' \ + -e 's;PANICTRACE_GDB=1;PANICTRACE_GDB=0;' $(INSTDIR)/sysconf; +endif #MAKEFILE_TOP ifeq '$(USE_NONOSTATICFN)' '1' CFLAGS += -DNONOSTATICFN From ae452e04b9dd6712050e99d47c071c86b60a0b9d Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Fri, 16 Jan 2026 21:17:15 +0200 Subject: [PATCH 216/442] Don't discover generic objects Potion of object detection and then looking at a generic object glyph on the map would try to "discover" the generic object, and the loop in discover_object would go too far, overwriting the exclusion_zones pointer. Take the easy route and just prevent discovery of generic objects; there are probably other places where the generic objects should be handled too, but the fuzzer hasn't hit them. --- src/o_init.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/o_init.c b/src/o_init.c index d53ee1ecd..8b81da282 100644 --- a/src/o_init.c +++ b/src/o_init.c @@ -451,6 +451,9 @@ discover_object( boolean mark_as_encountered, boolean credit_hero) { + if (oindx < FIRST_OBJECT) /* don't discover generic objects */ + return; + if ((!objects[oindx].oc_name_known && mark_as_known) || (!objects[oindx].oc_encountered && mark_as_encountered) || (Role_if(PM_SAMURAI) From e9f57281f8516556ed5b8cb5ec6acd433faf363b Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Tue, 20 Jan 2026 17:50:27 +0200 Subject: [PATCH 217/442] Expose flip_level to lua --- doc/lua.adoc | 9 +++++++++ src/nhlua.c | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/doc/lua.adoc b/doc/lua.adoc index 3b457b670..f040886ec 100644 --- a/doc/lua.adoc +++ b/doc/lua.adoc @@ -120,6 +120,15 @@ Example: local k = nh.eckey("help"); +=== flip_level + +Flip the level horizontally or vertically. + +Example: + + nh.flip_level(1); + + === getlin Asks the player for a text to enter, and returns the entered string. diff --git a/src/nhlua.c b/src/nhlua.c index 439f06d95..4f96e24c4 100644 --- a/src/nhlua.c +++ b/src/nhlua.c @@ -35,6 +35,7 @@ staticfn int nhl_stairways(lua_State *); staticfn int nhl_pushkey(lua_State *); staticfn int nhl_doturn(lua_State *); staticfn int nhl_debug_flags(lua_State *); +staticfn int nhl_flip_level(lua_State *); staticfn int nhl_timer_has_at(lua_State *); staticfn int nhl_timer_peek_at(lua_State *); staticfn int nhl_timer_stop_at(lua_State *); @@ -1446,6 +1447,22 @@ nhl_debug_flags(lua_State *L) return 0; } +/* flip level */ +/* nh.flip_level(n); */ +staticfn int +nhl_flip_level(lua_State *L) +{ + int argc = lua_gettop(L); + int flp = 0; + + if (argc == 1) + flp = lua_tointeger(L, 1); + + flip_level(flp, !gi.in_mklev); + + return 0; +} + DISABLE_WARNING_UNREACHABLE_CODE /* does location at x,y have timer? */ @@ -1821,6 +1838,7 @@ static const struct luaL_Reg nhl_functions[] = { { "pushkey", nhl_pushkey }, { "doturn", nhl_doturn }, { "debug_flags", nhl_debug_flags }, + { "flip_level", nhl_flip_level }, { NULL, NULL } }; From c5dc08d23a971bc5723d72a2518237ba00ef41ac Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Wed, 21 Jan 2026 20:15:14 +0200 Subject: [PATCH 218/442] Tests for gas_cloud, exclusion, levregion --- test/test_des.lua | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/test_des.lua b/test/test_des.lua index 4cf7f53fd..31b72ffa3 100644 --- a/test/test_des.lua +++ b/test/test_des.lua @@ -494,6 +494,27 @@ function test_corridor() des.corridor({ srcroom=0, srcwall="south", srcdoor=0, destroom=1, destwall="north", destdoor=0 }); end +function test_gas_cloud() + des.gas_cloud({ x = 5, y = 5 }); + des.gas_cloud({ coord = {10,10} }); + des.gas_cloud({ selection=selection.area(15,5, 17,7), damage = 5 }); + des.gas_cloud({ selection=selection.area(20,5, 22,7), damage = 5, ttl = 10 }); +end + +function test_exclusion() + des.exclusion({ type = "teleport", region = { 0,0, 10,5 } }); + des.exclusion({ type = "teleport-up", region = { 0,0, 10,5 } }); + des.exclusion({ type = "teleport-down", region = { 0,0, 10,5 } }); + des.exclusion({ type = "monster-generation", region = { 0,0, 10,5 } }); +end + +function test_levregion() + des.levregion({ type="stair-up", region={01,00,79,20}, region_islev=1, exclude={0,0,28,12} }) + des.levregion({ type="stair-down", region={01,00,79,20}, region_islev=1, exclude={0,0,28,12} }) + des.levregion({ type="branch", region={01,00,79,20}, region_islev=1, exclude={0,0,28,12} }) + des.levregion({ region={25,11,25,11}, type="portal", name="fakewiz1" }); +end + function run_tests() test_level_init(); test_message(); @@ -520,6 +541,9 @@ function run_tests() test_terrain(); test_replace_terrain(); test_corridor(); + test_gas_cloud(); + test_exclusion(); + test_levregion(); des.reset_level(); des.level_init(); From b9f8f845fceb7f5943a66620d8ca7402e9765b16 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Wed, 21 Jan 2026 20:21:12 +0200 Subject: [PATCH 219/442] Test for drawbridge --- test/test_des.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/test_des.lua b/test/test_des.lua index 31b72ffa3..6c9bb465f 100644 --- a/test/test_des.lua +++ b/test/test_des.lua @@ -515,6 +515,15 @@ function test_levregion() des.levregion({ region={25,11,25,11}, type="portal", name="fakewiz1" }); end +function test_drawbridge() + des.terrain(05,08, "L"); + des.terrain(06,08, "|"); + des.drawbridge({ dir="east", state="closed", x=05,y=08 }); + des.terrain(10,08, "L"); + des.terrain(10,09, "-"); + des.drawbridge({ dir="south", state="random", coord={10,08} }) +end + function run_tests() test_level_init(); test_message(); @@ -544,6 +553,7 @@ function run_tests() test_gas_cloud(); test_exclusion(); test_levregion(); + test_drawbridge(); des.reset_level(); des.level_init(); From 49a87bd09c1cce498760d3eacdc4be7191aef8f0 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Thu, 22 Jan 2026 18:16:51 +0200 Subject: [PATCH 220/442] Lua pushkey and getlin Allow lua nh.pushkey to push multiple keys, make getlin return the keys in the command queue. --- src/nhlua.c | 5 ++++- src/windows.c | 21 +++++++++++++++++++++ test/test_src.lua | 10 ++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/nhlua.c b/src/nhlua.c index 4f96e24c4..767726277 100644 --- a/src/nhlua.c +++ b/src/nhlua.c @@ -1381,7 +1381,10 @@ nhl_pushkey(lua_State *L) if (argc == 1) { const char *key = luaL_checkstring(L, 1); - cmdq_add_key(CQ_CANNED, key[0]); + while (*key) { + cmdq_add_key(CQ_CANNED, *key); + key++; + } } return 0; diff --git a/src/windows.c b/src/windows.c index b929cf62f..ff9fd3863 100644 --- a/src/windows.c +++ b/src/windows.c @@ -1867,6 +1867,27 @@ void getlin(const char *query, char *bufp) { boolean old_bot_disabled = gb.bot_disabled; + char *obufp = bufp; + boolean got_cmdq = FALSE; + struct _cmd_queue *cmdq = NULL; + + while ((cmdq = cmdq_pop()) != 0) { + if (cmdq->typ == CMDQ_KEY) { + got_cmdq = TRUE; + *bufp = (cmdq->key != '\n') ? cmdq->key : '\0'; + bufp++; + if (cmdq->key == '\n') + break; + } else { + break; + } + } + + if (got_cmdq) { + *bufp = '\0'; + pline("%s %s", query, obufp); + return; + } program_state.in_getlin = 1; gb.bot_disabled = TRUE; diff --git a/test/test_src.lua b/test/test_src.lua index 2ae45d0ac..645811033 100644 --- a/test/test_src.lua +++ b/test/test_src.lua @@ -121,3 +121,13 @@ for func, fval in pairs(tests) do end end end + +function test_getlin() + nh.pushkey("AbC"); + local str = nh.getlin("What?"); + if str ~= "AbC" then + error("nh.getlin fail, got \"" .. str .. "\""); + end +end + +test_getlin(); From c8712fa28847004c3d714995bd6b3d69a03937a0 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Thu, 22 Jan 2026 18:42:11 +0200 Subject: [PATCH 221/442] Free the cmdq in getlin --- src/windows.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/windows.c b/src/windows.c index ff9fd3863..d816c60ca 100644 --- a/src/windows.c +++ b/src/windows.c @@ -1881,7 +1881,11 @@ getlin(const char *query, char *bufp) } else { break; } + free(cmdq); + cmdq = NULL; } + if (cmdq) + free(cmdq); if (got_cmdq) { *bufp = '\0'; From 3b4ce60df43185981b3dad34809254c2e00edee8 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Fri, 23 Jan 2026 17:43:54 +0200 Subject: [PATCH 222/442] Lua tests: more code coverage --- test/test_des.lua | 11 +++++++++-- test/test_src.lua | 9 +++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/test/test_des.lua b/test/test_des.lua index 6c9bb465f..2e72a129f 100644 --- a/test/test_des.lua +++ b/test/test_des.lua @@ -88,6 +88,8 @@ function test_monster() des.monster(); des.monster("gnome") des.monster("S") + des.monster(":", {12,07}); + des.monster("K", 13,07); des.monster("giant eel",11,06); des.monster("hill giant", {08,06}); des.monster({ id = "ogre" }) @@ -103,7 +105,7 @@ function test_monster() des.monster({ id = "chameleon", appear_as = "mon:bat" }); des.monster({ class = "H", asleep = 1, female = 1, invisible = 1, cancelled = 1, revived = 1, avenge = 1, stunned = 1, confused = 1, fleeing = 20, blinded = 20, paralyzed = 20 }) des.monster({ class = "H", asleep = true, female = true, invisible = true, cancelled = true, revived = true, avenge = true, stunned = true, confused = true }); - des.monster({ id = "ogre", x = 10, y = 15, name = "Fred", + des.monster({ id = "ogre", x = 10, y = 15, name = "Fred", keep_default_invent = true, inventory = function() des.object(); des.object("["); @@ -116,6 +118,9 @@ function test_monster() des.monster({ id = "lurker above", adjacentok = true }); des.monster({ id = "gnome", ignorewater = true }); des.monster({ id = "xan", countbirth = false }); + des.monster({ id = "Angel", align = "law" }); + des.monster({ id = "archeologist" }); + des.monster({ id = "wizard", name = "Rincewind", peaceful = true }); des.reset_level(); des.level_init(); end @@ -125,6 +130,8 @@ function test_object() des.level_init(); des.object() des.object("*") + des.object("*", 55, 12); + des.object("*", {55, 12}); des.object({ class = "%" }); des.object({ id = "statue", contents=0 }) des.object("sack") @@ -559,5 +566,5 @@ function run_tests() des.level_init(); end -nh.debug_flags({mongen = false, hunger = false, overwrite_stairs = true }); +nh.debug_flags({ hunger = false, overwrite_stairs = true }); run_tests(); diff --git a/test/test_src.lua b/test/test_src.lua index 645811033..ac3500aa3 100644 --- a/test/test_src.lua +++ b/test/test_src.lua @@ -130,4 +130,13 @@ function test_getlin() end end +function test_abscoord() + local ax,ay = nh.abscoord(3, 8); + local pt = nh.abscoord({ x = 10, y = 5 }); +end + test_getlin(); +test_abscoord(); + +nh.flip_level(3); + From b5dd0eb894828793036aa74c18d99eee69ef01ff Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Fri, 23 Jan 2026 18:24:28 +0200 Subject: [PATCH 223/442] Lua tests: more code coverage for traps --- test/test_des.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/test_des.lua b/test/test_des.lua index 2e72a129f..4477c620c 100644 --- a/test/test_des.lua +++ b/test/test_des.lua @@ -312,14 +312,17 @@ function test_trap() des.trap("level teleport", {42, 06}); check_trap_at(42, 06, "level teleport"); - des.trap({ type = "falling rock", x = 43, y = 06 }); + des.trap({ type = "falling rock", x = 43, y = 06, victim = false }); check_trap_at(43, 06, "falling rock"); - des.trap({ type = "dart", coord = {44, 06} }); + des.trap({ type = "dart", coord = {44, 06}, seen = true }); check_trap_at(44, 06, "dart"); des.trap(); des.trap("rust"); + des.trap({ type = "web", spider_on_web = false }); + des.trap({ type = "rolling boulder", coord = {40,10}, launchfrom = {39,11} }); + des.trap({ type = "teleport", teledest = {5,5} }); end function test_wall_prop() From 595e4720cc13ea6185df7ace446ca7e9416a986d Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 24 Jan 2026 00:20:24 -0800 Subject: [PATCH 224/442] Guidebook update: artifacts Add "objects" subsections for Artifacts and for Relics (invocation tools). They aren't very detailed but fill a gap. I did this a while back but couldn't preview the outcome (aside from the plain text version). I used to be able to execute the command 'open Guidebook.ps' to display it with the Preview program. That program is still there but Apple has dropped support for Postscript, presumably to stop paying royalties to Adobe or whoever owns it. I've used ghostview for this before but encountered unexplained trouble this time. It eventually worked; I don't know what changed. I haven't attempted to update the LaTeX version of the Guidebook, in order to avoid merge issues with the pending Pull Request in case anyone decides to incorporate that. (I won't; I still don't have tools to test it.) --- doc/Guidebook.mn | 52 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 212f90d9f..a8f739d5a 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -1,4 +1,4 @@ -.\" $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.598 $ $NHDT-Date: 1745139202 2025/04/20 00:53:22 $ +.\" $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.611 $ $NHDT-Date: 1769271623 2026/01/24 08:20:23 $ .\" .\" This is an excerpt from the 'roff' man page from the 'groff' package. .\"+-- @@ -47,7 +47,7 @@ .ds f0 \*(vr .ds f1 \" empty .\"DO NOT REMOVE NH_DATESUB .ds f2 Date(%B %-d, %Y) -.ds f2 December 11, 2025 +.ds f2 January 24, 2026 . .\" A note on some special characters: .\" \(lq = left double quote @@ -2582,6 +2582,54 @@ Sometimes the bless or curse state of objects is referred to as their or \(lq\f(CRBUCX\fP\(rq for Blessed, Uncursed, Cursed, or unknown. (The term \fIbeatitude\fP is occasionally used as well.) .hn 2 +Artifacts +.pg +Some objects have been imbued with special powers and are known as +\fBArtifacts\fP. +They have specific types (such as long sword or orcish dagger) and distinct +names such as \fIGiantslayer\fP or \fIGrimtooth\fP. +Artifact weapons typically do more damage than their ordinary counterparts. +Some do extra damage against all monsters, others only against specific +types of monsters so aren't better than regular weapons against other types. +Some confer defensive capabilities when wielded or have other powers that +aren't listed here. +.pg +You might find them simply lying on the floor, including but not limited +to inside shops, or be granted as a reward for \(lq#offer\(rq on an altar +to your patron deity. +A few might be dropped by monsters, or might be converted from an ordinary +object of the same type via assigning the right name. +.\" should we mention dipping for Excalibur here? +Or you might wish for them, if you happen to be granted a wish, but such +wishes can fail. +.pg +Some artifacts have a specific alignment, others don't. +You won't obtain aligned ones that have a different alignment from yours +via offering and might get a shock if you attempt to wish for any of those +or find one and attempt to use it. +.pg +Each role has a distinct artifact that is contained in the \fIQuest\fP +dungeon branch. +These are commonly known as quest artifacts. +All are aligned and most are non-weapons. +They won't be found randomly. +.pg +The \(oq\f(CR\\\fP\(cq and \(oq\f(CR\`a\fP\(cq commands will list +artifacts that you have fully identified (knowing the name and item type +isn't sufficient). +.hn 2 +Relics +.pg +There are three unique items that are named and have limited special +powers but aren't classified as artifacts. +Each is guarded by a particular monster and you'll need to collect all +three for use late in the game. +They are \fIthe Bell of Opening\fP, +\fIthe Candelabrum of Invocation\fP, and +\fIthe Book of the Dead\fP. +Their corresponding descriptions when not yet identified are +silver bell, candelabrum, and papyrus spellbook. +.hn 2 Weapons (\(oq)\(cq) .pg Given a chance, most monsters in the Mazes of Menace will gratuitously try to From cd9ea458950e4cb00dcc97f2ba79be0a95a3f775 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 24 Jan 2026 09:08:16 -0500 Subject: [PATCH 225/442] Pull request #1473 fixes3-7-0.txt entry --- doc/fixes3-7-0.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index ec81cb872..66ab95b39 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -3088,6 +3088,7 @@ add silver maces (pr #1405 by disperse) applied several source comment spelling or grammar fixes that were linked to an old rec.games.roguelike.nethack post from May 2006 by Arthur J. O'Dwyer +modernize code of LaTeX Guidebook (pr #1473 by Daniel Clow) Code Cleanup and Reorganization From 41ccf8ecb26118b99c0425d9a31ed8f4a68f50d6 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 24 Jan 2026 09:47:51 -0500 Subject: [PATCH 226/442] attempt 1 to fix CI build issue --- azure-pipelines.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index fa0dcc7e9..e708b0156 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -242,6 +242,7 @@ steps: displayName: 'Building MSDOS build' - bash: | sudo apt-get install texlive + sudo tlmgr install enumitem make Guidebook make Guidebook.txt make Guidebook.pdf From 33413604a0fcf3dc4d3e2d79b737572248278fe0 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 24 Jan 2026 09:49:20 -0500 Subject: [PATCH 227/442] attempt 2 to fix CI build issue --- azure-pipelines.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index e708b0156..8dd2e101a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -243,6 +243,7 @@ steps: - bash: | sudo apt-get install texlive sudo tlmgr install enumitem + sudo texhash make Guidebook make Guidebook.txt make Guidebook.pdf From f54fc4908fe8284b3e37d1e6bda3697be1a473b0 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 24 Jan 2026 10:28:42 -0500 Subject: [PATCH 228/442] attempt 3 to fix CI build issue --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 8dd2e101a..8d84b9029 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -242,8 +242,8 @@ steps: displayName: 'Building MSDOS build' - bash: | sudo apt-get install texlive - sudo tlmgr install enumitem - sudo texhash + tlmgr install enumitem + texhash make Guidebook make Guidebook.txt make Guidebook.pdf From c19fd7d7f8f8ae2ea543c051e1accace553806ed Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 24 Jan 2026 10:30:25 -0500 Subject: [PATCH 229/442] attempt 4 to fix CI build issue --- azure-pipelines.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 8d84b9029..417a3502c 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -242,6 +242,7 @@ steps: displayName: 'Building MSDOS build' - bash: | sudo apt-get install texlive + tlmgr init-usertree tlmgr install enumitem texhash make Guidebook From da208a0ac4c7e11141135634c50867c0468726fb Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 24 Jan 2026 10:49:42 -0500 Subject: [PATCH 230/442] attempt 5 to fix CI build issue --- azure-pipelines.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 417a3502c..dc8783847 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -241,10 +241,7 @@ steps: workingDirectory: $(Agent.BuildDirectory)/$(netHackPath) displayName: 'Building MSDOS build' - bash: | - sudo apt-get install texlive - tlmgr init-usertree - tlmgr install enumitem - texhash + sudo apt install texlive-guesswhich texlive-latex-extra make Guidebook make Guidebook.txt make Guidebook.pdf From 77ea8de1056e165bdf37138afc911556bc5bd03a Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 24 Jan 2026 10:51:40 -0500 Subject: [PATCH 231/442] attempt 6 to fix CI build issue --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index dc8783847..bbb299e0f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -241,7 +241,7 @@ steps: workingDirectory: $(Agent.BuildDirectory)/$(netHackPath) displayName: 'Building MSDOS build' - bash: | - sudo apt install texlive-guesswhich texlive-latex-extra + sudo apt install texlive texlive-latex-extra make Guidebook make Guidebook.txt make Guidebook.pdf From cb8de068adae1b70fd26c6d6be2464b479422f4f Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 24 Jan 2026 20:03:22 -0800 Subject: [PATCH 232/442] fix #S15038 - cursed magic whistle Reported via contact form but misclassified as spam. Applyin a magic has a chance to teleport the hero to be adjacent to a pet rather than vice versa, but it could do so even on no-teleport levels. --- doc/fixes3-7-0.txt | 3 ++- src/apply.c | 4 ++-- src/teleport.c | 8 +++++++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 66ab95b39..3d0fd6f67 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1,4 +1,4 @@ -NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1580 $ $NHDT-Date: 1764109066 2025/11/25 22:17:46 $ +NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1614 $ $NHDT-Date: 1769342601 2026/01/25 04:03:21 $ General Fixes and Modified Features ----------------------------------- @@ -2201,6 +2201,7 @@ when reading a T-shirt or apron, add trailing period to the slogan unless already present in the quoted text when reading an engraving, suppress the addition of a trailing period if the text already has one +cursed magic whistle could teleport hero on no-teleport levels Fixes to 3.7.0-x Platform and/or Interface Problems Exposed Via git Repository diff --git a/src/apply.c b/src/apply.c index c33b5d929..6cbcb30a8 100644 --- a/src/apply.c +++ b/src/apply.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 apply.c $NHDT-Date: 1753856387 2025/07/29 22:19:47 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.472 $ */ +/* NetHack 3.7 apply.c $NHDT-Date: 1769342601 2026/01/25 04:03:21 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.475 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -500,7 +500,7 @@ use_magic_whistle(struct obj *obj) You("produce a %shigh-%s.", Underwater ? "very " : "", Deaf ? "frequency vibration" : "pitched humming noise"); wake_nearby(TRUE); - if (!rn2(2)) + if (!rn2(2) && !noteleport_level(&gy.youmonst)) tele_to_rnd_pet(); } else { /* it's magic! it works underwater too (at a higher pitch) */ diff --git a/src/teleport.c b/src/teleport.c index 535970dce..48d3fd7dc 100644 --- a/src/teleport.c +++ b/src/teleport.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 teleport.c $NHDT-Date: 1736129950 2025/01/05 18:19:10 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.235 $ */ +/* NetHack 3.7 teleport.c $NHDT-Date: 1769342601 2026/01/25 04:03:21 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.239 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -811,6 +811,12 @@ tele_to_rnd_pet(void) struct monst *mtmp, *pet = (struct monst *) 0; int cnt = 0; + if (noteleport_level(&gy.youmonst)) { + impossible("%s", "attempt to teleport hero to be near a pet" + " on no-teleport level"); + return; + } + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) if (!DEADMONSTER(mtmp) && mtmp->mtame && !mon_offmap(mtmp)) { cnt++; From 68ba3955fd4691c528f9a9ef7e09a3084fc7a4cf Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 25 Jan 2026 13:49:10 +0200 Subject: [PATCH 233/442] Lua tests: more code coverage for rooms --- test/test_des.lua | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/test/test_des.lua b/test/test_des.lua index 4477c620c..ee2899026 100644 --- a/test/test_des.lua +++ b/test/test_des.lua @@ -394,15 +394,24 @@ function test_room() des.room({ x=4, y=3, w=3,h=3 }); end }); - des.room({ type="ordinary", coord={3, 3}, w=3, h=3 }); - des.room(); + des.room({ xalign="right", yalign="bottom", + contents = function(rm) + des.room({ contents = function(rm) + des.door({ state= "random", wall = "random" }); + end }); + end + }); des.room({ contents = function(rm) des.object(); des.monster(); + des.trap(); des.terrain(0,0, "L"); + des.altar({ coord = {0,0} }); des.terrain(rm.width, rm.height, "T"); end }); + des.room({ type="ordinary", coord={3, 3}, w=3, h=3 }); + des.room(); des.random_corridors(); end From 8ae3d0f6bc102727dba5fdcd75cf6a6a46b0318d Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 25 Jan 2026 14:18:01 +0200 Subject: [PATCH 234/442] Lua tests: code coverage for selection room and gradient --- test/test_sel.lua | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/test_sel.lua b/test/test_sel.lua index e96b9be71..637b1b281 100644 --- a/test/test_sel.lua +++ b/test/test_sel.lua @@ -523,6 +523,23 @@ function test_sel_numpoints() end end +function test_sel_room() + des.reset_level(); + des.level_init({ style = "solidfill", fg = " " }); + des.room({ w = 5, h = 3, + contents = function(rm) + local sel = selection.room(); + sel_has_n_points(sel, 5*3, __func__); + end + }); +end + +function test_sel_gradient() + local sela = selection.gradient({ type = "radial", x = 3, y = 5, x2 = 10, y2 = 12, mindist = 4, maxdist = 10}); + local selb = selection.gradient({ type = "square", x = 3, y = 5, x2 = 10, y2 = 12, mindist = 4, maxdist = 10}); + local selc = selection.gradient({ x = 3, y = 5, x2 = 10, y2 = 12, maxdist = 10}); +end + nh.debug_flags({mongen = false, hunger = false, overwrite_stairs = true }); test_selection_params(); test_sel_negate(); @@ -544,3 +561,5 @@ test_sel_iterate(); test_sel_bounds(); test_sel_map(); test_sel_numpoints(); +test_sel_room(); +test_sel_gradient(); From 4bf1fb5c68135865eeb13b089de29420688f7f31 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 25 Jan 2026 14:50:02 +0200 Subject: [PATCH 235/442] Lua tests: more special level code coverage --- test/test_des.lua | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/test_des.lua b/test/test_des.lua index ee2899026..6f4ba5f95 100644 --- a/test/test_des.lua +++ b/test/test_des.lua @@ -102,7 +102,10 @@ function test_monster() des.monster({ class = "H", peaceful = 0 }) des.monster({ id = "giant mimic", appear_as = "obj:boulder" }); des.monster({ id = "giant mimic", appear_as = "ter:altar" }); + des.monster({ id = "giant mimic", appear_as = "mon:giant mimic" }); des.monster({ id = "chameleon", appear_as = "mon:bat" }); + des.monster({ id = "chameleon", appear_as = "mon:yellow light" }); + des.monster({ id = "chameleon", appear_as = "mon:random" }); des.monster({ class = "H", asleep = 1, female = 1, invisible = 1, cancelled = 1, revived = 1, avenge = 1, stunned = 1, confused = 1, fleeing = 20, blinded = 20, paralyzed = 20 }) des.monster({ class = "H", asleep = true, female = true, invisible = true, cancelled = true, revived = true, avenge = true, stunned = true, confused = true }); des.monster({ id = "ogre", x = 10, y = 15, name = "Fred", keep_default_invent = true, @@ -133,6 +136,7 @@ function test_object() des.object("*", 55, 12); des.object("*", {55, 12}); des.object({ class = "%" }); + des.object({ class = "$" }); des.object({ id = "statue", contents=0 }) des.object("sack") des.object({ x = 41, y = 03 }) @@ -142,6 +146,9 @@ function test_object() des.object("diamond", {68, 04}) des.object({ id = "egg", x = 05, y = 04, montype = "yellow dragon" }); des.object({ id = "egg", x = 06, y = 04, montype = "yellow dragon", laid_by_you = true }); + des.object({ id = "tin", montype = "empty" }); + des.object({ id = "tin", montype = "spinach" }); + des.object({ id = "tin", montype = "floating eye" }); des.object({ id = "corpse", montype = "valkyrie" }) des.object({ id = "statue", montype = "C", historic = true, male = true }); des.object({ id = "statue", montype = "C", historic = true, female = true }); @@ -158,6 +165,10 @@ function test_object() des.object({ id = "oil lamp", lit = 1 }); des.object({ id = "silver dagger", spe = 127, buc = "cursed" }); des.object({ id = "silver dagger", spe = -127, buc = "blessed" }); + des.object({ class = "/", buc = "uncursed" }); + des.object({ class = "(", buc = "not-cursed" }); + des.object({ class = ")", buc = "not-uncursed" }); + des.object({ class = "=", buc = "not-blessed" }); des.object({ id = "bamboo arrow", quantity = 100 }); des.object({ id = "leather armor", eroded = 1 }); des.object({ id = "probing", recharged = 2, spe = 3 }); @@ -207,6 +218,7 @@ function test_altar() des.altar({ coord = {48, 20 }, type = "altar", align = "law" }); des.altar({ coord = {50, 20 }, type = "shrine", align = "noalign" }); des.altar({ coord = {52, 20 }, type = "sanctum", align = "coaligned" }); + des.altar({ type = "altar", align = "noncoaligned" }); end function test_map() From f73d9ee704d423da474edb8c8ff6589cc16c1cf0 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 25 Jan 2026 15:18:45 +0200 Subject: [PATCH 236/442] Expose selection size description to lua --- doc/lua.adoc | 10 ++++++++++ src/nhlsel.c | 14 ++++++++++++++ test/test_sel.lua | 18 ++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/doc/lua.adoc b/doc/lua.adoc index f040886ec..859cf922d 100644 --- a/doc/lua.adoc +++ b/doc/lua.adoc @@ -1145,6 +1145,16 @@ Example: local sel2 = selection.clone(sel); +=== describe_size + +Return a text describing the size of the selection. + +Example: + + local sel = selection.fillrect(1,1, 3,3); + local txt = sel:describe_size(); + + === ellipse Example: diff --git a/src/nhlsel.c b/src/nhlsel.c index 306820cf9..3dad65bfb 100644 --- a/src/nhlsel.c +++ b/src/nhlsel.c @@ -33,6 +33,7 @@ staticfn int l_selection_circle(lua_State *); staticfn int l_selection_ellipse(lua_State *); staticfn int l_selection_gradient(lua_State *); staticfn int l_selection_iterate(lua_State *); +staticfn int l_selection_size_description(lua_State *L); staticfn int l_selection_gc(lua_State *); staticfn int l_selection_not(lua_State *); staticfn int l_selection_and(lua_State *); @@ -949,6 +950,18 @@ l_selection_iterate(lua_State *L) return 0; } +/* local txt = sel:describe_size(); */ +/* gives a textual description of the selection size */ +staticfn int +l_selection_size_description(lua_State *L) +{ + int argc = lua_gettop(L); + struct selectionvar *sel = l_selection_check(L, 1); + char buf[BUFSZ]; + + lua_pushstring(L, selection_size_description(sel, buf)); + return 1; +} static const struct luaL_Reg l_selection_methods[] = { { "new", l_selection_new }, @@ -974,6 +987,7 @@ static const struct luaL_Reg l_selection_methods[] = { { "iterate", l_selection_iterate }, { "bounds", l_selection_getbounds }, { "room", l_selection_room }, + { "describe_size", l_selection_size_description }, { NULL, NULL } }; diff --git a/test/test_sel.lua b/test/test_sel.lua index 637b1b281..c5739e8fd 100644 --- a/test/test_sel.lua +++ b/test/test_sel.lua @@ -540,6 +540,23 @@ function test_sel_gradient() local selc = selection.gradient({ x = 3, y = 5, x2 = 10, y2 = 12, maxdist = 10}); end +function test_sel_describe_size() + local sela = selection.fillrect(1,1, 3,3); + if (sela:describe_size() ~= "square 3 by 3") then + error("selection.describe_size returned \"" .. sela:describe_size() "\", not \"square 3 by 3\""); + end + + local selb = selection.fillrect(1,1, 4,3); + if (selb:describe_size() ~= "rectangular 4 by 3") then + error("selection.describe_size returned \"" .. selb:describe_size() "\", not \"rectangular 4 by 3\""); + end + + sela:set(1,1, 0); + if (sela:describe_size() ~= "irregularly shaped 3 by 3") then + error("selection.describe_size returned \"" .. sela:describe_size() "\", not \"irregularly shaped 3 by 3\""); + end +end + nh.debug_flags({mongen = false, hunger = false, overwrite_stairs = true }); test_selection_params(); test_sel_negate(); @@ -563,3 +580,4 @@ test_sel_map(); test_sel_numpoints(); test_sel_room(); test_sel_gradient(); +test_sel_describe_size(); From e1a318053590575f5d65d59da459311ddf37f07f Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 25 Jan 2026 09:51:22 -0500 Subject: [PATCH 237/442] update tested versions of Visual Studio 2026-01-25 --- 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 036710093..aa713f1c7 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.24 -# - Microsoft Visual Studio Community 2026 v 18.2.0 +# - Microsoft Visual Studio Community 2022 v 17.14.25 +# - Microsoft Visual Studio Community 2026 v 18.2.1 # #============================================================================== # This is used for building two distinct executables of NetHack: @@ -1189,7 +1189,7 @@ rc=Rc.exe # # Recently tested versions: TESTEDVS2022 = 14.44.35222.0 -TESTEDVS2026 = 14.50.35722.0 +TESTEDVS2026 = 14.50.35723.0 VS20261ST = 1450000000 VS2026CUR = $(TESTEDVS2026:.=) From 62f39698992cb4db2d8e57f5f3b225928db63b52 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 25 Jan 2026 10:34:27 -0500 Subject: [PATCH 238/442] another Lua version bump follow-up --- sys/vms/Install370.vms | 2 +- sys/vms/Makefile_dat.vms | 2 +- sys/vms/Makefile_doc.vms | 2 +- sys/vms/Makefile_src.vms | 2 +- sys/vms/Makefile_top.vms | 2 +- sys/vms/Makefile_utl.vms | 2 +- sys/vms/vmsbuild.com | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/sys/vms/Install370.vms b/sys/vms/Install370.vms index 12d020c82..edca9e9d0 100644 --- a/sys/vms/Install370.vms +++ b/sys/vms/Install370.vms @@ -16,7 +16,7 @@ Last tested with VSI C x86-64 V7.5-009 (GEM 50XBR) on OpenVMS x86_64 V9.2-2. [.dat] -- data files [.doc] -- documentation files [.include] -- C header files - [.lib.lua546] -- Lua distribution from https://www.lua.org/ + [.lib.lua548] -- Lua distribution from https://www.lua.org/ [.src] -- primary source files [.sys] -- parent for [.sys.*] [.sys.share] -- files shared by several ports, including VMS diff --git a/sys/vms/Makefile_dat.vms b/sys/vms/Makefile_dat.vms index 4059aca4d..09979bb63 100644 --- a/sys/vms/Makefile_dat.vms +++ b/sys/vms/Makefile_dat.vms @@ -24,7 +24,7 @@ WINSHIM=[-.win.shim] WINCHAIN=[-.win.chain] # Lua location relative to src -LUAVER=546 +LUAVER=548 LUADOTVER=5.4.8 LUAUNDERVER=5_4_8 LUAINC=[-.lib.lua.lua$(LUAVER).src] diff --git a/sys/vms/Makefile_doc.vms b/sys/vms/Makefile_doc.vms index 854a303dd..64184d092 100644 --- a/sys/vms/Makefile_doc.vms +++ b/sys/vms/Makefile_doc.vms @@ -25,7 +25,7 @@ WINSHIM=[-.win.shim] WINCHAIN=[-.win.chain] # Lua location relative to src -LUAVER=546 +LUAVER=548 LUADOTVER=5.4.8 LUAUNDERVER=5_4_8 LUAINC=[-.lib.lua.lua$(LUAVER).src] diff --git a/sys/vms/Makefile_src.vms b/sys/vms/Makefile_src.vms index 56b35aa58..473116e81 100644 --- a/sys/vms/Makefile_src.vms +++ b/sys/vms/Makefile_src.vms @@ -35,7 +35,7 @@ WINSHIM=[-.win.shim] WINCHAIN=[-.win.chain] # Lua -LUAVER=546 +LUAVER=548 LUADOTVER=5.4.8 LUAUNDERVER=5_4_8 # Lua location relative to src diff --git a/sys/vms/Makefile_top.vms b/sys/vms/Makefile_top.vms index 94e506fea..47028c80d 100644 --- a/sys/vms/Makefile_top.vms +++ b/sys/vms/Makefile_top.vms @@ -54,7 +54,7 @@ WINSHIM=[.win.shim] WINCHAIN=[.win.chain] # Lua macros -LUAVER=546 +LUAVER=548 LUADOTVER=5.4.8 LUAUNDERVER=5_4_8 LUAINC=[.lib.lua.lua$(LUAVER).src] diff --git a/sys/vms/Makefile_utl.vms b/sys/vms/Makefile_utl.vms index d30d0b629..c17283d13 100644 --- a/sys/vms/Makefile_utl.vms +++ b/sys/vms/Makefile_utl.vms @@ -30,7 +30,7 @@ WINSHIM=[-.win.shim] WINCHAIN=[-.win.chain] # Lua location relative to here -LUAVER=546 +LUAVER=548 LUADOTVER=5.4.8 LUAUNDERVER=5_4_8 LUAINC=[-.lib.lua.lua$(LUAVER).src] diff --git a/sys/vms/vmsbuild.com b/sys/vms/vmsbuild.com index 3749ffe04..6146cd5f1 100755 --- a/sys/vms/vmsbuild.com +++ b/sys/vms/vmsbuild.com @@ -453,7 +453,7 @@ $ gosub compile_list $ milestone "" $ link /EXECUTABLE=nethack.exe vmsmain.obj,date.obj- +[-.src]nethack.olb/library - - +sys$disk:[-.lib.lua]lua546.olb/library + +sys$disk:[-.lib.lua]lua548.olb/library $ milestone "NetHack" $ if c_opt.eq.o_LINK then goto done !"LINK" only $special: From 92a8d1dab3aeefe122635c107c51e759fb730a4a Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 25 Jan 2026 18:32:43 +0200 Subject: [PATCH 239/442] Lua tests: monster creation coverage Expose constants NUMMONS, LOW_PM, and HIGH_PM to lua. Allow converting an int to monster type name. Create one of each type of monster in the lua tests. --- doc/lua.adoc | 12 ++++++++++++ src/nhlua.c | 25 +++++++++++++++++++++++++ test/test_des.lua | 5 +++++ 3 files changed, 42 insertions(+) diff --git a/doc/lua.adoc b/doc/lua.adoc index 859cf922d..6d028085e 100644 --- a/doc/lua.adoc +++ b/doc/lua.adoc @@ -259,6 +259,15 @@ Example: local str = nh.ing_suffix("foo"); +=== int_to_pmname + +Convert integer value to monster type name. + +Example: + + local pmname = nh.int_to_pmname(12); + + === is_genocided Is specific monster type genocided? Returns a boolean value. @@ -1564,6 +1573,9 @@ These constants are in the `nhc` table. |=== | COLNO | Number of map columns | ROWNO | Number of map rows +| NUMMONS | Number of different monster types +| LOW_PM | First monster type id. See <<_int_to_pmname>>. +| HIGH_PM | Last monster type id. See <<_int_to_pmname>>. | DLB | 1 or 0, depending if NetHack is compiled with DLB |=== diff --git a/src/nhlua.c b/src/nhlua.c index 767726277..c83e81865 100644 --- a/src/nhlua.c +++ b/src/nhlua.c @@ -31,6 +31,7 @@ struct e; staticfn int nhl_dump_fmtstr(lua_State *); #endif /* DUMPLOG */ staticfn int nhl_dnum_name(lua_State *); +staticfn int nhl_int_to_pm_name(lua_State *); staticfn int nhl_stairways(lua_State *); staticfn int nhl_pushkey(lua_State *); staticfn int nhl_doturn(lua_State *); @@ -1164,6 +1165,26 @@ nhl_dnum_name(lua_State *L) return 1; } +/* return gender-neutral monster type name by integer value, + or empty string if outside LOW_PM - HIGH_PM range */ +/* local montypename = int_to_pmname(12); */ +staticfn int +nhl_int_to_pm_name(lua_State *L) +{ + int argc = lua_gettop(L); + + if (argc == 1) { + lua_Integer i = luaL_checkinteger(L, 1); + + if (i >= LOW_PM && i <= HIGH_PM) + lua_pushstring(L, mons[i].pmnames[NEUTRAL]); + else + lua_pushstring(L, ""); + } else + nhl_error(L, "Expected an integer parameter"); + return 1; +} + DISABLE_WARNING_UNREACHABLE_CODE /* because nhl_error() does not return */ @@ -1836,6 +1857,7 @@ static const struct luaL_Reg nhl_functions[] = { { "dump_fmtstr", nhl_dump_fmtstr }, #endif /* DUMPLOG */ { "dnum_name", nhl_dnum_name }, + { "int_to_pmname", nhl_int_to_pm_name }, { "variable", nhl_variable }, { "stairways", nhl_stairways }, { "pushkey", nhl_pushkey }, @@ -1851,6 +1873,9 @@ static const struct { } nhl_consts[] = { { "COLNO", COLNO }, { "ROWNO", ROWNO }, + { "NUMMONS", NUMMONS }, + { "LOW_PM", LOW_PM }, + { "HIGH_PM", HIGH_PM }, #ifdef DLB { "DLB", 1 }, #else diff --git a/test/test_des.lua b/test/test_des.lua index 6f4ba5f95..b75f04c1c 100644 --- a/test/test_des.lua +++ b/test/test_des.lua @@ -124,6 +124,11 @@ function test_monster() des.monster({ id = "Angel", align = "law" }); des.monster({ id = "archeologist" }); des.monster({ id = "wizard", name = "Rincewind", peaceful = true }); + + for i = nhc.LOW_PM, nhc.HIGH_PM do + des.monster({ id = nh.int_to_pmname(i) }); + end + des.reset_level(); des.level_init(); end From 2dc95dae423420d9973985b21b28b27593de6175 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 25 Jan 2026 18:38:52 +0200 Subject: [PATCH 240/442] Actually use the lua argc --- src/nhlsel.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/nhlsel.c b/src/nhlsel.c index 3dad65bfb..79ece1aae 100644 --- a/src/nhlsel.c +++ b/src/nhlsel.c @@ -956,11 +956,18 @@ staticfn int l_selection_size_description(lua_State *L) { int argc = lua_gettop(L); - struct selectionvar *sel = l_selection_check(L, 1); - char buf[BUFSZ]; - lua_pushstring(L, selection_size_description(sel, buf)); - return 1; + if (argc == 1) { + struct selectionvar *sel = l_selection_check(L, 1); + char buf[BUFSZ]; + + lua_pushstring(L, selection_size_description(sel, buf)); + return 1; + } else { + nhl_error(L, "wrong parameters"); + /*NOTREACHED*/ + } + return 0; } static const struct luaL_Reg l_selection_methods[] = { From 2a0d3eea64e3cf229c8df60361559e7931c2180c Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 25 Jan 2026 11:49:36 -0500 Subject: [PATCH 241/442] a warning bit Visual Studio build: src/nhlsel.c(970) : warning C4702: unreachable code --- src/nhlsel.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/nhlsel.c b/src/nhlsel.c index 79ece1aae..b06b53545 100644 --- a/src/nhlsel.c +++ b/src/nhlsel.c @@ -910,6 +910,8 @@ l_selection_gradient(lua_State *L) return 1; } +DISABLE_WARNING_UNREACHABLE_CODE + /* sel:iterate(function(x,y) ... end); * The x, y coordinates passed to the function are map- or room-relative * rather than absolute, unless there has been no previous map or room @@ -970,6 +972,8 @@ l_selection_size_description(lua_State *L) return 0; } +RESTORE_WARNING_UNREACHABLE_CODE + static const struct luaL_Reg l_selection_methods[] = { { "new", l_selection_new }, { "clone", l_selection_clone }, From b5ca1a3ed87bb14569b366d688276fbb1a361790 Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 25 Jan 2026 11:40:08 -0800 Subject: [PATCH 242/442] fix comment typo --- src/u_init.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/u_init.c b/src/u_init.c index 1e6ac0ff3..9c37508c0 100644 --- a/src/u_init.c +++ b/src/u_init.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 u_init.c $NHDT-Date: 1737620595 2025/01/23 00:23:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.113 $ */ +/* NetHack 3.7 u_init.c $NHDT-Date: 1769398807 2026/01/25 19:40:07 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.121 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2017. */ /* NetHack may be freely redistributed. See license for details. */ @@ -861,7 +861,7 @@ u_init_race(void) } } -/* for 'pauper' aka 'unpreparsed'; take away any skills (bare-handed combat, +/* for 'pauper' aka 'unprepared'; take away any skills (bare-handed combat, riding) that are better than unskilled; learn the book (without carrying it or knowing its spell yet) for some key spells */ staticfn void From 11bed1f55bcedb2ac4cd1d0362661b99b828f495 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Mon, 26 Jan 2026 17:49:45 +0200 Subject: [PATCH 243/442] Lua tests: generation of each object Test generation of every object, both via des.object and obj.new. Expose FIRST_OBJECT and LAST_OBJECT numbers to lua. Add lua nh.int_to_objname, a function to convert integer value to object base name and class. Allow creating new nethack lua object by specifying id and class. --- doc/lua.adoc | 23 +++++++++++++++++++---- include/extern.h | 2 ++ src/nhlobj.c | 19 ++++++++++++++++++- src/nhlua.c | 29 +++++++++++++++++++++++++++++ src/sp_lev.c | 25 ++++++++++++++----------- test/test_des.lua | 21 +++++++++++++++++++++ test/test_obj.lua | 19 +++++++++++++++++++ 7 files changed, 122 insertions(+), 16 deletions(-) diff --git a/doc/lua.adoc b/doc/lua.adoc index 6d028085e..8ca4fd9c1 100644 --- a/doc/lua.adoc +++ b/doc/lua.adoc @@ -259,6 +259,17 @@ Example: local str = nh.ing_suffix("foo"); +=== int_to_objname + +Convert integer value to object name and class. +Returns two strings, the object base name and the class character. +The returned strings may be empty if an error occurred. + +Example: + + local oname, oclass = nh.int_to_objname(45); + + === int_to_pmname Convert integer value to monster type name. @@ -850,7 +861,7 @@ Example: === object -Create an object. Returns the object as an <> class. +Create an object and place it somewhere on the map. Returns the object as an <> class. The table parameter accepts the following: [options="header"] @@ -874,8 +885,8 @@ The table parameter accepts the following: | greased | boolean | Is the object greased? | broken | boolean | Is the object broken? | achievement | boolean | Is there an achievement attached to the object? -| x, y | int | Coordinates on the level -| coord | table | x,y coordinates in table format +| x, y | int | Coordinates on the level; defaults to a random location. +| coord | table | x,y coordinates in table format; defaults to a random location. | montype | string | Monster id or class | historic | boolean | Is statue historic? | male | boolean | Is statue male? @@ -1380,11 +1391,13 @@ Handling objects via obj-class. === new -Create a new object via wishing routine. +Create a new object, either via wishing routine, or specifying object name and class. +Unlike des.object, does not place the object anywhere. Example: local o = obj.new("rock"); + local o = obj.new({ id = "invisibility", class = "!" }); === isnull @@ -1576,6 +1589,8 @@ These constants are in the `nhc` table. | NUMMONS | Number of different monster types | LOW_PM | First monster type id. See <<_int_to_pmname>>. | HIGH_PM | Last monster type id. See <<_int_to_pmname>>. +| FIRST_OBJECT | First object type id. See <<_int_to_objname>>. +| LAST_OBJECT | Number of object type ids. See <<_int_to_objname>>. | DLB | 1 or 0, depending if NetHack is compiled with DLB |=== diff --git a/include/extern.h b/include/extern.h index 40de54078..3d8261029 100644 --- a/include/extern.h +++ b/include/extern.h @@ -3033,6 +3033,8 @@ extern int nhl_abs_coord(lua_State *) NONNULLARG1; extern void update_croom(void); extern const char *get_trapname_bytype(int); extern void l_register_des(lua_State *) NONNULLARG1; +extern int get_table_objclass(lua_State *) NONNULLARG1; +extern int get_table_objtype(lua_State *) NONNULLARG1; #endif /* !CROSSCOMPILE || CROSSCOMPILE_TARGET */ /* ### spell.c ### */ diff --git a/src/nhlobj.c b/src/nhlobj.c index 0579ddf47..a0535074c 100644 --- a/src/nhlobj.c +++ b/src/nhlobj.c @@ -345,12 +345,13 @@ DISABLE_WARNING_UNREACHABLE_CODE /* create a new object via wishing routine */ /* local o = obj.new("rock"); */ +/* local o = obj.new({ id = "food ration", class = "%" }); */ staticfn int l_obj_new_readobjnam(lua_State *L) { int argc = lua_gettop(L); - if (argc == 1) { + if (argc == 1 && lua_type(L, 1) == LUA_TSTRING) { char buf[BUFSZ]; struct obj *otmp; @@ -360,6 +361,22 @@ l_obj_new_readobjnam(lua_State *L) otmp = NULL; (void) l_obj_push(L, otmp); return 1; + } else if (argc == 1 && lua_type(L, 1) == LUA_TTABLE) { + short id = get_table_objtype(L); + xint16 class = get_table_objclass(L); + struct obj *otmp; + + if (id >= FIRST_OBJECT) { + otmp = mksobj(id, TRUE, FALSE); + } else { + class = def_char_to_objclass(class); + if (class >= MAXOCLASSES) + class = RANDOM_CLASS; + otmp = mkobj(class, FALSE); + } + lua_pop(L, 1); + (void) l_obj_push(L, otmp); + return 1; } else nhl_error(L, "l_obj_new_readobjname: Wrong args"); /*NOTREACHED*/ diff --git a/src/nhlua.c b/src/nhlua.c index c83e81865..092c42e60 100644 --- a/src/nhlua.c +++ b/src/nhlua.c @@ -32,6 +32,7 @@ staticfn int nhl_dump_fmtstr(lua_State *); #endif /* DUMPLOG */ staticfn int nhl_dnum_name(lua_State *); staticfn int nhl_int_to_pm_name(lua_State *); +staticfn int nhl_int_to_obj_name(lua_State *); staticfn int nhl_stairways(lua_State *); staticfn int nhl_pushkey(lua_State *); staticfn int nhl_doturn(lua_State *); @@ -1185,6 +1186,31 @@ nhl_int_to_pm_name(lua_State *L) return 1; } +/* convert integer to object type name and class */ +/* local oname,oclass = int_to_objname(25); */ +staticfn int +nhl_int_to_obj_name(lua_State *L) +{ + int argc = lua_gettop(L); + + if (argc == 1) { + char buf[8]; + lua_Integer i = luaL_checkinteger(L, 1); + + if (i >= 0 && i < NUM_OBJECTS && OBJ_NAME(objects[i])) { + lua_pushstring(L, OBJ_NAME(objects[i])); + buf[0] = def_oc_syms[(int)objects[i].oc_class].sym; + buf[1] = '\0'; + lua_pushstring(L, buf); + } else { + lua_pushstring(L, ""); + lua_pushstring(L, ""); + } + } else + nhl_error(L, "Expected an integer parameter"); + return 2; +} + DISABLE_WARNING_UNREACHABLE_CODE /* because nhl_error() does not return */ @@ -1858,6 +1884,7 @@ static const struct luaL_Reg nhl_functions[] = { #endif /* DUMPLOG */ { "dnum_name", nhl_dnum_name }, { "int_to_pmname", nhl_int_to_pm_name }, + { "int_to_objname", nhl_int_to_obj_name }, { "variable", nhl_variable }, { "stairways", nhl_stairways }, { "pushkey", nhl_pushkey }, @@ -1876,6 +1903,8 @@ static const struct { { "NUMMONS", NUMMONS }, { "LOW_PM", LOW_PM }, { "HIGH_PM", HIGH_PM }, + { "FIRST_OBJECT", FIRST_OBJECT }, + { "LAST_OBJECT", NUM_OBJECTS-1 }, #ifdef DLB { "DLB", 1 }, #else diff --git a/src/sp_lev.c b/src/sp_lev.c index 21357eada..25411aa1f 100644 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -117,9 +117,7 @@ staticfn int find_montype(lua_State *, const char *, int *); staticfn int get_table_montype(lua_State *, int *); staticfn lua_Integer get_table_int_or_random(lua_State *, const char *, int); staticfn int get_table_buc(lua_State *); -staticfn int get_table_objclass(lua_State *); -staticfn int find_objtype(lua_State *, const char *); -staticfn int get_table_objtype(lua_State *); +staticfn int find_objtype(lua_State *, const char *, char); staticfn const char *get_mkroom_name(int) NONNULL; staticfn int get_table_roomtype_opt(lua_State *, const char *, int); staticfn int get_table_traptype_opt(lua_State *, const char *, int); @@ -3443,7 +3441,7 @@ get_table_buc(lua_State *L) return curse_state; } -staticfn int +int get_table_objclass(lua_State *L) { char *s = get_table_str_opt(L, "class", NULL); @@ -3455,13 +3453,14 @@ get_table_objclass(lua_State *L) return ret; } +/* find object otyp by text s (optionally considering oclass) */ staticfn int -find_objtype(lua_State *L, const char *s) +find_objtype(lua_State *L, const char *s, char oclass) { if (s && *s) { int i; const char *objname; - char class = 0; + char class = def_char_to_objclass(oclass); /* In objects.h, some item classes are defined without prefixes (such as "scroll of ") in their names, making some names (such @@ -3479,6 +3478,9 @@ find_objtype(lua_State *L, const char *s) { NULL, 0 } }; + if (class == MAXOCLASSES) + class = 0; + if (strstri(s, " of ")) { for (i = 0; class_prefixes[i].prefix; i++) { const char *p = class_prefixes[i].prefix; @@ -3523,11 +3525,12 @@ find_objtype(lua_State *L, const char *s) return STRANGE_OBJECT; } -staticfn int +int get_table_objtype(lua_State *L) { char *s = get_table_str_opt(L, "id", NULL); - int ret = find_objtype(L, s); + char oclass = get_table_objclass(L); + int ret = find_objtype(L, s, oclass); Free(s); return ret; @@ -3586,7 +3589,7 @@ lspo_object(lua_State *L) tmpobj.id = STRANGE_OBJECT; } else { tmpobj.class = -1; - tmpobj.id = find_objtype(L, paramstr); + tmpobj.id = find_objtype(L, paramstr, -1); } } else if (argc == 2 && lua_type(L, 1) == LUA_TSTRING && lua_type(L, 2) == LUA_TTABLE) { @@ -3599,7 +3602,7 @@ lspo_object(lua_State *L) tmpobj.id = STRANGE_OBJECT; } else { tmpobj.class = -1; - tmpobj.id = find_objtype(L, paramstr); + tmpobj.id = find_objtype(L, paramstr, -1); } } else if (argc == 3 && lua_type(L, 2) == LUA_TNUMBER && lua_type(L, 3) == LUA_TNUMBER) { @@ -3613,7 +3616,7 @@ lspo_object(lua_State *L) tmpobj.id = STRANGE_OBJECT; } else { tmpobj.class = -1; - tmpobj.id = find_objtype(L, paramstr); + tmpobj.id = find_objtype(L, paramstr, -1); } } else { lcheck_param_table(L); diff --git a/test/test_des.lua b/test/test_des.lua index b75f04c1c..151433d58 100644 --- a/test/test_des.lua +++ b/test/test_des.lua @@ -180,6 +180,27 @@ function test_object() des.object({ name = "Random object" }); des.object({ class = "*", name = "Random stone" }); des.object({ id ="broadsword", name = "Dragonbane" }) + + for i = nhc.FIRST_OBJECT, nhc.LAST_OBJECT do + local oid, oclass = nh.int_to_objname(i); + if (oid ~= "") then + local o = des.object({ id = oid, class = oclass }); + local o_t = o:totable(); + + -- crysknife reverts to worm tooth on the floor + if not(oid == "crysknife" and o_t.otyp_name == "worm tooth") then + if (o_t.otyp_name ~= oid) then + error("object name \"" .. o_t.otyp_name .. "\" created, wanted \"" .. oid .. "\""); + end + if (o_t.oclass ~= oclass) then + local str = string.format("object class \"%s\" created, wanted \"%s\" (%s)", o_t.oclass, oclass, oid); + error(str); + end + end + + end + end + des.reset_level(); des.level_init(); end diff --git a/test/test_obj.lua b/test/test_obj.lua index d56a5ba55..68ef9147b 100644 --- a/test/test_obj.lua +++ b/test/test_obj.lua @@ -82,3 +82,22 @@ box:addcontent(o5); local o6 = obj.new("statue"); o6:addcontent(obj.new("spellbook")); + + +-- generate one of each object, check the name and class matches +for i = nhc.FIRST_OBJECT, nhc.LAST_OBJECT do + local oid, oclass = nh.int_to_objname(i); + if (oid ~= "") then + local oi = obj.new({ id = oid, class = oclass }); + local oi_t = oi:totable(); + + if (oi_t.otyp_name ~= oid) then + error("object name \"" .. oi_t.otyp_name .. "\" created, wanted \"" .. oid .. "\""); + end + if (oi_t.oclass ~= oclass) then + local str = string.format("object class \"%s\" created, wanted \"%s\" (%s)", oi_t.oclass, oclass, oid); + error(str); + end + + end +end From e18f4b65a97ee097ac77413fbf0146f8d206abf4 Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 26 Jan 2026 15:27:54 -0800 Subject: [PATCH 244/442] cure sickness feedback A thread on Reddit mentions that successfully casting the cure sickness spell when not Sick doesn't provide any feedback. Change it to report |You are not ill. in that situation. Also, give "you are no longer ill" feedback when actually curing sickness after status gets updated. --- src/spell.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/spell.c b/src/spell.c index 889f624ba..43c2acd51 100644 --- a/src/spell.c +++ b/src/spell.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 spell.c $NHDT-Date: 1762680996 2025/11/09 01:36:36 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.179 $ */ +/* NetHack 3.7 spell.c $NHDT-Date: 1769498874 2026/01/26 23:27:54 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.185 $ */ /* Copyright (c) M. Stephenson 1988 */ /* NetHack may be freely redistributed. See license for details. */ @@ -1549,13 +1549,23 @@ spelleffects(int spell_otyp, boolean atme, boolean force) case SPE_CURE_BLINDNESS: healup(0, 0, FALSE, TRUE); break; - case SPE_CURE_SICKNESS: - if (Sick) - You("are no longer ill."); - if (Slimed) - make_slimed(0L, "The slime disappears!"); + case SPE_CURE_SICKNESS: { + boolean was_sick = !!Sick, was_slimed = !!Slimed; + + /* cure conditions (which updates status) before feedback */ healup(0, 0, TRUE, FALSE); + /* + * Sick + !Slimed -- You are no longer ill. + * !Sick + !Slimed -- You are not ill. + * !Sick + Slimed -- The slime disappears. + * Sick + Slimed -- You are no longer ill. The slime disappears. + */ + if (was_sick || !was_slimed) + You("are %s ill.", was_sick ? "no longer" : "not"); + if (was_slimed) + make_slimed(0L, "The slime disappears!"); break; + } case SPE_CREATE_FAMILIAR: (void) make_familiar((struct obj *) 0, u.ux, u.uy, FALSE); break; From 060c3de8eb81503173f86557bc25f5ce67327b10 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Tue, 27 Jan 2026 17:33:05 +0200 Subject: [PATCH 245/442] Lua tests: code coverage for applying some items Add a new debug flag prevent_pline, which prevents all messages from going out to the UI. This prevents the tests from stopping for -more-. Add rudimentary tests for applying whistles, camera, and stethoscope. --- doc/lua.adoc | 1 + include/flag.h | 1 + src/nhlua.c | 6 ++++++ src/pline.c | 3 +++ test/test_obj.lua | 43 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 54 insertions(+) diff --git a/doc/lua.adoc b/doc/lua.adoc index 8ca4fd9c1..3d03342b8 100644 --- a/doc/lua.adoc +++ b/doc/lua.adoc @@ -61,6 +61,7 @@ Set debugging flags. | mongen | boolean | Do monsters generate | hunger | boolean | Does hero's hunger-state increase | overwrite_stairs | boolean | Allow special-file commands overwrite the stairs +| prevent_pline | boolean | Prevent messages going out to the UI |=== Example: diff --git a/include/flag.h b/include/flag.h index 59ef02c04..dbcccd312 100644 --- a/include/flag.h +++ b/include/flag.h @@ -293,6 +293,7 @@ struct instance_flags { boolean debug_overwrite_stairs; /* debug: allow overwriting stairs */ boolean debug_mongen; /* debug: prevent monster generation */ boolean debug_hunger; /* debug: prevent hunger */ + boolean debug_prevent_pline; /* debug: prevent pline going to UI */ boolean mon_polycontrol; /* debug: control monster polymorphs */ boolean mon_telecontrol; /* debug: control monster teleports */ boolean in_dumplog; /* doing the dumplog right now? */ diff --git a/src/nhlua.c b/src/nhlua.c index 092c42e60..96970f73c 100644 --- a/src/nhlua.c +++ b/src/nhlua.c @@ -1494,6 +1494,12 @@ nhl_debug_flags(lua_State *L) iflags.debug_overwrite_stairs = (boolean) val; } + /* prevent pline going out to the UI */ + val = get_table_boolean_opt(L, "prevent_pline", -1); + if (val != -1) { + iflags.debug_prevent_pline = (boolean) val; + } + return 0; } diff --git a/src/pline.c b/src/pline.c index 48f8fc89f..f79cc6631 100644 --- a/src/pline.c +++ b/src/pline.c @@ -66,6 +66,9 @@ putmesg(const char *line) { int attr = ATR_NONE; + if (iflags.debug_prevent_pline) + return; + if ((gp.pline_flags & URGENT_MESSAGE) != 0 && (windowprocs.wincap2 & WC2_URGENT_MESG) != 0) attr |= ATR_URGENT; diff --git a/test/test_obj.lua b/test/test_obj.lua index 68ef9147b..1baad9832 100644 --- a/test/test_obj.lua +++ b/test/test_obj.lua @@ -101,3 +101,46 @@ for i = nhc.FIRST_OBJECT, nhc.LAST_OBJECT do end end + + +function test_use_item(action, itemname, otherkeys) + nh.debug_flags({ prevent_pline = true }); + u.clear_inventory(); + u.giveobj(obj.new(itemname)); + local o = u.inventory; + local ot = o:totable(); + + nh.pushkey(action); + nh.pushkey(ot.invlet); + + if (otherkeys ~= nil and type(otherkeys) == "string") then + nh.pushkey(otherkeys); + end + + nh.doturn(); + nh.debug_flags({ prevent_pline = false }); +end + +nh.parse_config("OPTIONS=number_pad:0"); +nh.parse_config("OPTIONS=!timed_delay"); + +-- apply +test_use_item("a", "uncursed tin whistle"); +test_use_item("a", "cursed tin whistle"); +test_use_item("a", "blessed magic whistle"); + +test_use_item("a", "uncursed camera", "h"); +test_use_item("a", "uncursed camera", "j"); +test_use_item("a", "uncursed camera", "k"); +test_use_item("a", "blessed camera", ">"); +test_use_item("a", "+0 blessed camera", ">"); + +test_use_item("a", "blessed stethoscope", "h"); +test_use_item("a", "blessed stethoscope", "j"); +test_use_item("a", "blessed stethoscope", "."); +test_use_item("a", "blessed stethoscope", ">"); +test_use_item("a", "blessed stethoscope", "<"); +obj.new("corpse"):placeobj(u.ux, u.uy); +test_use_item("a", "blessed stethoscope", ">"); +obj.new("statue"):placeobj(u.ux, u.uy); +test_use_item("a", "blessed stethoscope", ">"); From b34b7e08a42bb8b0aa797ce2ca3f90471d7a5b8f Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Wed, 28 Jan 2026 10:06:55 +0200 Subject: [PATCH 246/442] Fix lua reset_level The lua des.reset_level() call did not reset the special level coder, so some values were kept and couldn't be changed. Adjust the movement tests for this change. --- src/sp_lev.c | 7 ++++++- test/testmove.lua | 6 ++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/sp_lev.c b/src/sp_lev.c index 25411aa1f..2e1b4ab65 100644 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -5985,8 +5985,13 @@ lspo_reset_level(lua_State *L) boolean wtower = In_W_tower(u.ux, u.uy, &u.uz); iflags.lua_testing = TRUE; - if (L) + if (L) { + if (gc.coder) { + Free(gc.coder); + gc.coder = NULL; + } create_des_coder(); + } makemap_prepost(TRUE, wtower); gi.in_mklev = TRUE; oinit(); /* assign level dependent obj probabilities */ diff --git a/test/testmove.lua b/test/testmove.lua index da1e0ab83..8ded195d5 100644 --- a/test/testmove.lua +++ b/test/testmove.lua @@ -3,14 +3,14 @@ nh.parse_config("OPTIONS=number_pad:0"); nh.parse_config("OPTIONS=runmode:teleport"); +nh.parse_config("OPTIONS=!timed_delay"); local POS = { x = 10, y = 05 }; local number_pad = 0; function initlev() - nh.debug_flags({mongen = false, hunger = false, overwrite_stairs = true }); - des.level_flags("noflip"); des.reset_level(); + des.level_flags("noflip"); des.level_init({ style = "solidfill", fg = ".", lit = true }); des.teleport_region({ region = {POS.x,POS.y,POS.x,POS.y}, region_islev = true, dir="both" }); des.finalize_level(); @@ -140,6 +140,7 @@ local basicmoves = { }; +nh.debug_flags({mongen = false, hunger = false, overwrite_stairs = true, disable_pline = true }); for k, v in pairs(basicmoves) do initlev(); @@ -179,3 +180,4 @@ for k, v in pairs(basicmoves) do end initlev(); +nh.debug_flags({ disable_pline = false }); From f7b3337ae144cac18ba88f302d570497ba22f71d Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 30 Jan 2026 14:00:32 -0800 Subject: [PATCH 247/442] fuzzer vs highlight menus From nearly three months ago... --- src/botl.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/botl.c b/src/botl.c index f90812f53..eddfcccf5 100644 --- a/src/botl.c +++ b/src/botl.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 botl.c $NHDT-Date: 1742207239 2025/03/17 02:27:19 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.274 $ */ +/* NetHack 3.7 botl.c $NHDT-Date: 1769839231 2026/01/30 22:00:31 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.277 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -4301,7 +4301,9 @@ status_hilite_menu(void) countall = status_hilite_linestr_countfield(BL_FLUSH); status_hilite_linestr_done(); - if (redo) + /* fuzzer is unlikely to pick something useful within nested menus; + limit it to one try */ + if (redo && !iflags.debug_fuzzer) goto shlmenu_redo; /* hilite_delta=='statushilites' does double duty: it is the From deec8317ce8b5779e58c4239653c83cf461d6b53 Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 30 Jan 2026 14:17:53 -0800 Subject: [PATCH 248/442] git issue #1467 - selection.match() boundaries Issue reported by copperwater: | a = selection.match(some_mapfrag); | b = selection.match(another_mapfrag); | c = a + b; Instead of being a union of all the points that match either mapfrag, the resulting selection c is empty. [Report included a choice of two possible fixes.] I put both in, without adequate testing of either one. I didn't hit any problems with the existing special levels but didn't try many theme rooms. Closes #1467 --- src/nhlsel.c | 6 +++++- src/selvar.c | 19 +++++++++++++------ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/nhlsel.c b/src/nhlsel.c index b06b53545..9b5ef24c4 100644 --- a/src/nhlsel.c +++ b/src/nhlsel.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 nhlua.c $NHDT-Date: 1737545957 2025/01/22 03:39:17 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.64 $ */ +/* NetHack 3.7 nhlua.c $NHDT-Date: 1769840272 2026/01/30 22:17:52 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.69 $ */ /* Copyright (c) 2018 by Pasi Kallinen */ /* NetHack may be freely redistributed. See license for details. */ @@ -710,6 +710,10 @@ l_selection_match(lua_State *L) for (x = 1; x < sel->wid; x++) selection_setpoint(x, y, sel, mapfrag_match(mf, x,y) ? 1 : 0); + /* unless the (0, 1) coordinate is a match, this would wind up with a + selection with lx=COLNO, hx=0, etc, so fix the boundaries */ + selection_recalc_bounds(sel); + mapfrag_free(&mf); return 1; diff --git a/src/selvar.c b/src/selvar.c index 5d08ca95e..a35b9f7bd 100644 --- a/src/selvar.c +++ b/src/selvar.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 selvar.c $NHDT-Date: 1709677544 2024/03/05 22:25:44 $ $NHDT-Branch: keni-mdlib-followup $:$NHDT-Revision: 1.360 $ */ +/* NetHack 3.7 selvar.c $NHDT-Date: 1769840272 2026/01/30 22:17:52 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.4 $ */ /* Copyright (c) 2024 by Pasi Kallinen */ /* NetHack may be freely redistributed. See license for details. */ @@ -189,11 +189,18 @@ selection_setpoint( return; if (c && !sel->bounds_dirty) { - if (sel->bounds.lx > x) sel->bounds.lx = x; - if (sel->bounds.ly > y) sel->bounds.ly = y; - if (sel->bounds.hx < x) sel->bounds.hx = x; - if (sel->bounds.hy < y) sel->bounds.hy = y; - } else { + if (sel->bounds.lx > x) + sel->bounds.lx = x; + if (sel->bounds.ly > y) + sel->bounds.ly = y; + if (sel->bounds.hx < x) + sel->bounds.hx = x; + if (sel->bounds.hy < y) + sel->bounds.hy = y; + + /* only set bounds_dirty if changing a point from 1 to 0; if changing + a point from 0 to 0, nothing has really changed with the bounds */ + } else if (sel->map[sel->wid * y + x] != 0) { sel->bounds_dirty = TRUE; } From 27adb7c71db809dd9f722116cad84c0b7a41f707 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Tue, 3 Feb 2026 12:07:08 +0200 Subject: [PATCH 249/442] Light-spell is clerical, if playing a priest Ideally the spell adjustment should be in o_init, but by that time hero's role has not yet been set. --- doc/fixes3-7-0.txt | 1 + src/role.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 3d0fd6f67..8bade1d23 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1575,6 +1575,7 @@ elementals do double damage on their home plane water elementals move slightly more slowly the "totally digested" instadeath timer is much faster slow monster effects are more effective against faster enemies +light-spell is clerical, if playing a priest Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/role.c b/src/role.c index e95d3f121..8d4feaab2 100644 --- a/src/role.c +++ b/src/role.c @@ -2084,6 +2084,9 @@ role_init(void) /* 0 or 1; no gods are neuter, nor is gender randomized */ svq.quest_status.godgend = !strcmpi(align_gtitle(alignmnt), "goddess"); + if (Role_if(PM_CLERIC)) + objects[SPE_LIGHT].oc_skill = P_CLERIC_SPELL; + #if 0 /* * Disable this fixup so that mons[] can be const. The only From 37168ab188f6b2f400e54c3168efc724253e89a9 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Wed, 4 Feb 2026 22:35:19 +0200 Subject: [PATCH 250/442] Boomerangs can hit multiple monsters The number of monsters it can hit depends on the enchantment. --- doc/fixes3-7-0.txt | 1 + include/extern.h | 1 + src/dothrow.c | 55 ++++++++++++++++++++++++++++------------------ src/zap.c | 9 ++++++-- 4 files changed, 43 insertions(+), 23 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 8bade1d23..5af951add 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1576,6 +1576,7 @@ water elementals move slightly more slowly the "totally digested" instadeath timer is much faster slow monster effects are more effective against faster enemies light-spell is clerical, if playing a priest +boomerang can hit multiple monsters Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/include/extern.h b/include/extern.h index 3d8261029..899f72b4a 100644 --- a/include/extern.h +++ b/include/extern.h @@ -829,6 +829,7 @@ extern void hurtle(int, int, int, boolean); extern void mhurtle(struct monst *, int, int, int) NONNULLARG1; extern boolean harmless_missile(struct obj *) NONNULLARG1; extern boolean throwing_weapon(struct obj *) NONNULLARG1; +extern boolean throwit_mon_hit(struct obj *, struct monst *) NONNULLARG1; extern void throwit(struct obj *, long, boolean, struct obj *) NONNULLARG1; extern int omon_adj(struct monst *, struct obj *, boolean) NONNULLPTRS; extern boolean should_mulch_missile(struct obj *); diff --git a/src/dothrow.c b/src/dothrow.c index 35b52aa6c..952913a43 100644 --- a/src/dothrow.c +++ b/src/dothrow.c @@ -1474,6 +1474,37 @@ swallowit(struct obj *obj) throwit_return(TRUE); } +/* thrown object hits a monster. + mon may be NULL. + returns TRUE if shopkeeper caught the object. + may delete object, clearing gt.thrownobj */ +boolean +throwit_mon_hit(struct obj *obj, struct monst *mon) +{ + if (mon) { + boolean obj_gone; + + if (mon->isshk && obj->where == OBJ_MINVENT && obj->ocarry == mon) { + return TRUE; + } + (void) snuff_candle(obj); + gn.notonhead = (gb.bhitpos.x != mon->mx || gb.bhitpos.y != mon->my); + obj_gone = thitmonst(mon, obj); + /* Monster may have been tamed; this frees old mon [obsolete] */ + mon = m_at(gb.bhitpos.x, gb.bhitpos.y); + + /* [perhaps this should be moved into thitmonst or hmon] */ + if (mon && mon->isshk + && (!inside_shop(u.ux, u.uy) + || !strchr(in_rooms(mon->mx, mon->my, SHOPBASE), *u.ushops))) + hot_pursuit(mon); + + if (obj_gone) + gt.thrownobj = (struct obj *) 0; + } + return FALSE; +} + /* throw an object, NB: obj may be consumed in the process */ void throwit( @@ -1661,27 +1692,9 @@ throwit( } } - if (mon) { - boolean obj_gone; - - if (mon->isshk && obj->where == OBJ_MINVENT && obj->ocarry == mon) { - throwit_return(TRUE); /* alert shk caught it */ - return; - } - (void) snuff_candle(obj); - gn.notonhead = (gb.bhitpos.x != mon->mx || gb.bhitpos.y != mon->my); - obj_gone = thitmonst(mon, obj); - /* Monster may have been tamed; this frees old mon [obsolete] */ - mon = m_at(gb.bhitpos.x, gb.bhitpos.y); - - /* [perhaps this should be moved into thitmonst or hmon] */ - if (mon && mon->isshk - && (!inside_shop(u.ux, u.uy) - || !strchr(in_rooms(mon->mx, mon->my, SHOPBASE), *u.ushops))) - hot_pursuit(mon); - - if (obj_gone) - gt.thrownobj = (struct obj *) 0; + if (throwit_mon_hit(obj, mon)) { + throwit_return(TRUE); /* alert shk caught it */ + return; } if (!gt.thrownobj) { diff --git a/src/zap.c b/src/zap.c index d3fdf72eb..52697a4ae 100644 --- a/src/zap.c +++ b/src/zap.c @@ -4139,6 +4139,7 @@ boomhit(struct obj *obj, coordxy dx, coordxy dy) int boom; /* showsym[] index */ struct monst *mtmp; boolean counterclockwise = URIGHTY; /* ULEFTY => clockwise */ + int nhits = (obj->spe + 1); /* counterclockwise traversal patterns, from @ to 1 then on through to 9 * ..........................54................................. @@ -4173,8 +4174,12 @@ boomhit(struct obj *obj, coordxy dx, coordxy dy) } if ((mtmp = m_at(gb.bhitpos.x, gb.bhitpos.y)) != 0) { m_respond(mtmp); - tmp_at(DISP_END, 0); - return mtmp; + if (nhits-- < 0) { + tmp_at(DISP_END, 0); + return mtmp; + } else if (throwit_mon_hit(obj, mtmp) || !gt.thrownobj) { + break; + } } if (!ZAP_POS(levl[gb.bhitpos.x][gb.bhitpos.y].typ) || closed_door(gb.bhitpos.x, gb.bhitpos.y)) { From 79c688cc6bc485cb070a6a36a63cd0bed5e122b5 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Fri, 6 Feb 2026 17:26:54 +0200 Subject: [PATCH 251/442] Code style nit --- src/windows.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/windows.c b/src/windows.c index d816c60ca..b8cc8ea8d 100644 --- a/src/windows.c +++ b/src/windows.c @@ -1852,7 +1852,8 @@ get_menu_coloring(const char *str, int *color, int *attr) return FALSE; } -int select_menu(winid window, int how, menu_item **menu_list) +int +select_menu(winid window, int how, menu_item **menu_list) { int reslt; boolean old_bot_disabled = gb.bot_disabled; From f021aca3f7d9c32248637edbcbac575d34e1f2a5 Mon Sep 17 00:00:00 2001 From: "G. Branden Robinson" Date: Sat, 25 Oct 2025 19:17:12 -0500 Subject: [PATCH 252/442] doc/makedefs.6: Fix formatting error Starting a text line with leading spaces causes a break when filling. Usually this is not what is intended. Fixes: $ nroff -ww -z -rCHECKSTYLE=4 -man doc/*.[67] an.tmac:doc/makedefs.6:102: style: 1 leading space(s) on input line --- doc/makedefs.6 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/makedefs.6 b/doc/makedefs.6 index 136084473..5e24242fa 100644 --- a/doc/makedefs.6 +++ b/doc/makedefs.6 @@ -99,7 +99,7 @@ only if it is present, to obtain .B githash= and .B gitbranch= - info and include related preprocessor #defines in +info and include related preprocessor #defines in .I date.h file. .br From 6a8edddcb9e9871dc5114c7a088371882e011419 Mon Sep 17 00:00:00 2001 From: "G. Branden Robinson" Date: Sat, 7 Feb 2026 04:07:50 -0600 Subject: [PATCH 253/442] doc/tmac.nh: Drop space after `\c` escape sequence Fixes warning in category "syntax" from the forthcoming groff 1.24. troff::790: warning: ignoring a space on input line after output line continuation escape sequence [94 more occurrences] --- doc/tmac.nh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tmac.nh b/doc/tmac.nh index 86bc643b4..f8c30c2d0 100644 --- a/doc/tmac.nh +++ b/doc/tmac.nh @@ -29,7 +29,7 @@ .\" labeled paragraph label (and first line) .de PL .br -\\h'|-\\n(PYu'\\$1\\h'|-\\n(PXu'\ -\ \\c \" back up, output the label, then +\\h'|-\\n(PYu'\\$1\\h'|-\\n(PXu'\ -\ \\c\" back up, output the label, then . \" skip to width-of(" - ") before the . \" normal indentation, output the " - " . \" then attach the next line (the From b4bd6fb2b86072ab8c7da6dfdc93f13c67787009 Mon Sep 17 00:00:00 2001 From: "G. Branden Robinson" Date: Fri, 6 Feb 2026 09:02:42 -0600 Subject: [PATCH 254/442] Guidebook: Improve package loading for groff 1.24 groff 1.24 is in its second release candidate of this writing and features a noteworthy revision to its syntax. ---snip--- NEWS: * If your roff(7) documents follow any of the requests ... `so`, ... with a comment after their file name argument, and did not place that comment immediately after the file name, you are likely to get a diagnostic message resembling the following. warning: cannot open macro file 'e.tmac ': No such file or directory Or, less likely, the formatter will open the wrong file, one with spaces at the end of its name. That is because these requests are now able to process file names containing space characters. (This change also makes the request syntax consistent with that of `ds`, `as`, and others.) A quick fix is to place the comment escape sequence as early as possible. For example, we would change: .mso e.tmac \" Load Eric Allman's package. to: .mso e.tmac\" Load Eric Allman's package. to tell the formatter to load the "e.tmac" file rather than "e.tmac ". See the items below for further details. ---end snip--- Work around this change while maintaining with older groff and other *roff formatters. 1. Stop using space to separate comments from the argument to `so`. 2. Temporarily define a `So` macro to wrap the `soquiet` request (for groff 1.23 and later) or `so` request (for everything else). 3. Abort formatting with an error diagnostic if the `nh` macro package the Guidebook requires cannot be located. Fixes: $ (cd doc && rm -f Guidebook && make Guidebook) troff::34: error: cannot open 'tmac.nh ': No such file or directory troff::35: error: cannot open 'doc/tmac.nh': No such file or directory --- doc/Guidebook.mn | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index a8f739d5a..89f581285 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -31,8 +31,21 @@ .lt 70n .\} . -.so tmac.nh \" extra macros which aren't in tmac.n -.if !\n(nH .so doc/tmac.nh +.\" Load extra macros that "tmac.n" doesn't define. +.de So +.ie \n(.g&(\n(.x=1)&(\n(.y>22) .soquiet \\$1 +.el .so \\$1 +.. +. +.So tmac.nh +.if !\n(nH So doc/tmac.nh +. +.if !\n(nH \{\ +.tm fatal error: cannot locate "tmac.nh" macro package +.ab +.\} +. +.rm So . .\" building Guidebook.txt doesn't have CR font available; groff 1.23 issues .\" a warning each time any font can't be loaded; earlier versions silently From a8253dda0008f4ba04c7b2c2101c474a6dcaf596 Mon Sep 17 00:00:00 2001 From: "G. Branden Robinson" Date: Sat, 7 Feb 2026 04:30:06 -0600 Subject: [PATCH 255/442] Update Unix hints to revise groff detection The existing detection logic was not working on any groff since at least 1.22.3 (November 2014), as could be seen by uncommenting the "$(info ...)" line. The regex used to match "nroff --version" output was insufficiently flexible. Fixes: $ (cd doc && rm -f Guidebook && PATH=$HOME/groff-1.22.3/usr/bin:/bin make Guidebook) | grep NROFF NROFFISGROFF= $ (cd doc && rm -f Guidebook && PATH=$HOME/groff-1.22.4/usr/bin:/bin make Guidebook) | grep NROFF NROFFISGROFF= $ (cd doc && rm -f Guidebook && PATH=$HOME/groff-1.23.0/usr/bin:/bin make Guidebook) | grep NROFF NROFFISGROFF= $ (cd doc && rm -f Guidebook && PATH=$HOME/groff-HEAD/usr/bin:/bin make Guidebook) | grep NROFF NROFFISGROFF= Use a different approach in Make to recording groff detection. Use "grep -c" (which is POSIX-conforming) to count the number of matches so that we can use the contents of the Make macro `NROFFISGROFF` as a sort of Boolean, which reads more idiomatically (in my opinion). Further, instead of trying to lexically analyze a matched line in the output of "nroff --version" and parse components of a version number out of it, use GNU troff's built-in facility for extracting its minor version number by storing the output of a tiny *roff document that reports that datum (and nothing else). Ignore warnings in category "scale" in any version of groff, because the `tmac.n` macro package provokes them. Clarify comments. --- sys/unix/hints/include/misc.370 | 38 ++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/sys/unix/hints/include/misc.370 b/sys/unix/hints/include/misc.370 index 6608cf45a..7f57697b4 100644 --- a/sys/unix/hints/include/misc.370 +++ b/sys/unix/hints/include/misc.370 @@ -70,34 +70,38 @@ NROFF = mandoc MAN2TXTPRE = -T ascii MAN2TXTPOST= | col -b else -#detection of groff -NROFFISGROFF := $(shell echo `nroff --version | grep "GNU groff version"`) +# +# Detect groff. +NROFFISGROFF := $(shell echo `nroff --version | grep -c 'GNU.*groff.*version'`) #$(info NROFFISGROFF=$(NROFFISGROFF)) -ifneq "$(NROFFISGROFF)" "" -# get the version of groff and flag if it is gt or eq to 1.23 -GROFFGE123 := $(shell expr `echo $(NROFFISGROFF) | cut -f2 -d.` \>= 23) -# or less than 1.24 -GROFFLT124 := $(shell expr `echo $(NROFFISGROFF) | cut -f2 -d.` \< 24) -# -Wtab -Wrange are for the sake of tmac.n. -NROFF_FLAGS := -wall -Wtab -Wrange -ifneq "$(GROFFLT124)" "" -NROFF_FLAGS += -Wel -Wscale -endif -endif # NROFFISGROFF +ifneq "$(NROFFISGROFF)" "0" +# Gather groff's minor version number (register `.y`). +GROFFMINORVERSION := $(shell printf '.tm \\n[.y]\n' | nroff 2>&1) +#$(info GROFFMINORVERSION=$(GROFFMINORVERSION)) +# Silence warnings produced by tmac.n, which NetHack does not modify. +NROFF_FLAGS := -wall -Wrange -Wscale -Wtab +# groff <= 1.23 also supported an "el" warning category that was buggy. +GROFFLE123 := $(shell expr $(GROFFMINORVERSION) \<= 23) +#$(info GROFFLE123=$(GROFFLE123)) +ifeq "$(GROFFLE123)" "1" +NROFF_FLAGS += -Wel +endif # end groff less than 1.23 +endif # end NROFFISGROFF +# $(info NROFF_FLAGS=$(NROFF_FLAGS)) ifneq "$(NROFFISGROFF)" "" # It's groff # add the -Tascii flag used by groff MAN2TXTPRE += -Tascii -ifneq "$(GROFFGE123)" "" # It's groff 1.23 or greater -#$(info GROFFGE123=$(GROFFGE123)) # nroff in groff 1.23 supports the -P option to pass arguments to the # output driver. -cbou are flags to grotty(1). +GROFFGE123 := $(shell expr $(GROFFMINORVERSION) \>= 23) +#$(info GROFFGE123=$(GROFFGE123)) +ifeq "$(GROFFGE123)" "1" MAN2TXTPRE += -P -cbou MAN2TXTPOST= else MAN2TXTPRE += -c -# groff less than 1.23 -endif +endif # end groff less than 1.23 endif # end groff-specific endif # not USE_MANDOC From 74390fb69056e9c9eda9152533284187c00b6f33 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sat, 7 Feb 2026 14:50:41 +0200 Subject: [PATCH 256/442] Split ray bouncing dir into separate function --- src/zap.c | 82 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 49 insertions(+), 33 deletions(-) diff --git a/src/zap.c b/src/zap.c index 52697a4ae..33b532f79 100644 --- a/src/zap.c +++ b/src/zap.c @@ -26,6 +26,7 @@ staticfn void skiprange(int, int *, int *) NONNULLPTRS; staticfn void maybe_explode_trap(struct trap *, struct obj *, boolean *) NONNULLARG3; staticfn void zap_map(coordxy, coordxy, struct obj *) NONNULLARG3; +staticfn void bounce_dir(coordxy, coordxy, int *, int *, int) NO_NNARGS; staticfn int zap_hit(int, int); staticfn void disintegrate_mon(struct monst *, int, const char *) NONNULLARG1; staticfn int adtyp_to_prop(int); @@ -4641,6 +4642,51 @@ burn_floor_objects( return cnt; } +/* which direction a ray bounces. + current location is sx,sy, direction is ddx, ddy. + bounceback is 1/n chance of bouncing back. + caller must ensure sx,sy is a bouncing location: !ZAP_POS or closed_door + */ +staticfn void +bounce_dir(coordxy sx, coordxy sy, + int *ddx, int *ddy, + int bounceback) +{ + if (!*ddx || !*ddy || (bounceback > 0 && !rn2(bounceback))) { + *ddx = -(*ddx); + *ddy = -(*ddy); + } else { + uchar rmn; + int bounce = 0; + coordxy lsy = sy - *ddy; + coordxy lsx = sx - *ddx; + + if (isok(sx, lsy) && ZAP_POS(rmn = levl[sx][lsy].typ) + && !closed_door(sx, lsy) + && (IS_ROOM(rmn) || (isok(sx + *ddx, lsy) + && ZAP_POS(levl[sx + *ddx][lsy].typ)))) + bounce = 1; + if (isok(lsx, sy) && ZAP_POS(rmn = levl[lsx][sy].typ) + && !closed_door(lsx, sy) + && (IS_ROOM(rmn) || (isok(lsx, sy + *ddy) + && ZAP_POS(levl[lsx][sy + *ddy].typ)))) + if (!bounce || rn2(2)) + bounce = 2; + switch (bounce) { + case 0: + *ddx = -(*ddx); + FALLTHROUGH; + /*FALLTHRU*/ + case 1: + *ddy = -(*ddy); + break; + case 2: + *ddx = -(*ddx); + break; + } + } +} + /* will zap/spell/breath attack score a hit against armor class `ac'? */ staticfn int zap_hit( @@ -4937,14 +4983,12 @@ dobuzz( if (!ZAP_POS(levl[sx][sy].typ) || (closed_door(sx, sy) && range >= 0)) { - int bounce, bchance; - uchar rmn; + int bchance; make_bounce: bchance = (!isok(sx, sy) || levl[sx][sy].typ == STONE) ? 10 : (In_mines(&u.uz) && IS_WALL(levl[sx][sy].typ)) ? 20 : 75; - bounce = 0; if ((--range > 0 && isok(lsx, lsy) && cansee(lsx, lsy)) || fireball) { if (Is_airlevel(&u.uz)) { /* nothing to bounce off of */ @@ -4960,36 +5004,8 @@ dobuzz( } else pline_The("%s bounces!", flash_str(fltyp, FALSE)); } - if (!dx || !dy || !rn2(bchance)) { - dx = -dx; - dy = -dy; - } else { - if (isok(sx, lsy) && ZAP_POS(rmn = levl[sx][lsy].typ) - && !closed_door(sx, lsy) - && (IS_ROOM(rmn) || (isok(sx + dx, lsy) - && ZAP_POS(levl[sx + dx][lsy].typ)))) - bounce = 1; - if (isok(lsx, sy) && ZAP_POS(rmn = levl[lsx][sy].typ) - && !closed_door(lsx, sy) - && (IS_ROOM(rmn) || (isok(lsx, sy + dy) - && ZAP_POS(levl[lsx][sy + dy].typ)))) - if (!bounce || rn2(2)) - bounce = 2; - - switch (bounce) { - case 0: - dx = -dx; - FALLTHROUGH; - /*FALLTHRU*/ - case 1: - dy = -dy; - break; - case 2: - dx = -dx; - break; - } - tmp_at(DISP_CHANGE, zapdir_to_glyph(dx, dy, hdmgtype)); - } + bounce_dir(sx, sy, &dx, &dy, bchance); + tmp_at(DISP_CHANGE, zapdir_to_glyph(dx, dy, hdmgtype)); } } tmp_at(DISP_END, 0); From 63dd80f5ec1719b60c26598b527af306a5081d2c Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sat, 7 Feb 2026 17:03:54 +0200 Subject: [PATCH 257/442] More code style nits --- src/files.c | 9 ++++++--- src/glyphs.c | 9 ++++++--- src/mhitu.c | 3 ++- src/utf8map.c | 3 ++- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/files.c b/src/files.c index 3215449e7..abdb728f5 100644 --- a/src/files.c +++ b/src/files.c @@ -2046,13 +2046,15 @@ doconvert_file(const char *filename, int sfstatus, boolean unconvert) } /* convert file */ -void nh_sfconvert(const char *filename) +void +nh_sfconvert(const char *filename) { (void) doconvert_file(filename, 0, FALSE); } /* unconvert file if it exists */ -void nh_sfunconvert(const char *filename) +void +nh_sfunconvert(const char *filename) { (void) doconvert_file(filename, 0, TRUE); } @@ -2140,7 +2142,8 @@ delete_convertedfile(const char *basefilename) return 0; } -void free_convert_filenames(void) +void +free_convert_filenames(void) { if (converted_filename) free((genericptr_t) converted_filename), converted_filename = 0; diff --git a/src/glyphs.c b/src/glyphs.c index bf8edf314..778cb4aeb 100644 --- a/src/glyphs.c +++ b/src/glyphs.c @@ -299,7 +299,8 @@ glyph_find_core( */ -void fill_glyphid_cache(void) +void +fill_glyphid_cache(void) { int reslt = 0; @@ -350,7 +351,8 @@ init_glyph_cache(void) } } -void free_glyphid_cache(void) +void +free_glyphid_cache(void) { size_t idx; @@ -1173,7 +1175,8 @@ clear_all_glyphmap_colors(void) } } -void reset_customcolors(void) +void +reset_customcolors(void) { clear_all_glyphmap_colors(); apply_customizations(gc.currentgraphics, do_custom_colors); diff --git a/src/mhitu.c b/src/mhitu.c index 79b12a737..129637152 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -2399,7 +2399,8 @@ mon_avoiding_this_attack(struct monst *mtmp, int attkidx) * ranged_attk_assessed(mtmp, mon_avoiding_this_attack) * but without the added assessment function call overhead. */ -boolean ranged_attk_available(struct monst *mtmp) +boolean +ranged_attk_available(struct monst *mtmp) { int i, typ = -1; struct permonst *ptr = mtmp->data; diff --git a/src/utf8map.c b/src/utf8map.c index 87688b9ce..19f3b18e4 100644 --- a/src/utf8map.c +++ b/src/utf8map.c @@ -207,7 +207,8 @@ add_custom_urep_entry( } #endif /* ENHANCED_SYMBOLS */ -void reset_customsymbols(void) +void +reset_customsymbols(void) { #ifdef ENHANCED_SYMBOLS free_all_glyphmap_u(); From 525ca734ae80d04ba6d6dc9682dafa2e15a2ce82 Mon Sep 17 00:00:00 2001 From: nhkeni Date: Sat, 7 Feb 2026 21:39:03 -0500 Subject: [PATCH 258/442] add checksum for lua 5.5.0 --- submodules/CHKSUMS | 1 + 1 file changed, 1 insertion(+) diff --git a/submodules/CHKSUMS b/submodules/CHKSUMS index e69f3f9b9..c40043d77 100644 --- a/submodules/CHKSUMS +++ b/submodules/CHKSUMS @@ -7,3 +7,4 @@ 7d5ea1b9cb6aa0b59ca3dde1c6adcb57ef83a1ba8e5432c0ecd06bf439b3ad88 lua-5.4.6.tar.gz 9fbf5e28ef86c69858f6d3d34eccc32e911c1a28b4120ff3e84aaa70cfbf1e30 lua-5.4.7.tar.gz 4f18ddae154e793e46eeab727c59ef1c0c0c2b744e7b94219710d76f530629ae lua-5.4.8.tar.gz +57ccc32bbbd005cab75bcc52444052535af691789dba2b9016d5c50640d68b3d lua-5.5.0.tar.gz From 3877e142e7e7709b4c03c2f78d9eb00fd69ea722 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 8 Feb 2026 17:19:28 +0200 Subject: [PATCH 259/442] Add more bustle to bustling town --- dat/minetn-6.lua | 8 ++++++++ doc/fixes3-7-0.txt | 1 + 2 files changed, 9 insertions(+) diff --git a/dat/minetn-6.lua b/dat/minetn-6.lua index a31d9fcd8..2827d1947 100644 --- a/dat/minetn-6.lua +++ b/dat/minetn-6.lua @@ -86,6 +86,14 @@ des.monster("gnome lord") des.monster("dwarf") des.monster("dwarf") des.monster("dwarf") +des.monster({ id = "dwarf", peaceful = 1 }) +des.monster({ id = "dwarf", peaceful = 1 }) +des.monster({ id = "gnome", peaceful = 1 }) +des.monster({ id = "gnome", peaceful = 1 }) +des.monster({ id = "hobbit", peaceful = 1 }) +des.monster({ id = "goblin", peaceful = 1 }) +des.monster({ id = "kobold", peaceful = 1 }) +des.monster({ id = "dog", peaceful = 1 }) des.monster({ id = "watchman", peaceful = 1 }) des.monster({ id = "watchman", peaceful = 1 }) des.monster({ id = "watchman", peaceful = 1 }) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 5af951add..afeb8c249 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1577,6 +1577,7 @@ the "totally digested" instadeath timer is much faster slow monster effects are more effective against faster enemies light-spell is clerical, if playing a priest boomerang can hit multiple monsters +the "bustling town" minetown has more peacefuls Fixes to 3.7.0-x General Problems Exposed Via git Repository From 4b30e3fab86d80aa0bfae1699d8bd1f822e5b9e8 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 8 Feb 2026 18:11:02 +0200 Subject: [PATCH 260/442] Healers may get tiny damage increase with knives Styled as anatomy knowledge, a healer attacking a monster in melee with a knife gets +1 damage per 6 dead monsters of that type, capped at +3 damage. Handwaving away the fact we're not keeping track who actually killed the monsters... Idea from aosdict. --- doc/fixes3-7-0.txt | 1 + src/weapon.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index afeb8c249..3bcb9e172 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1578,6 +1578,7 @@ slow monster effects are more effective against faster enemies light-spell is clerical, if playing a priest boomerang can hit multiple monsters the "bustling town" minetown has more peacefuls +healers may get tiny damage increase when attacking with knives Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/weapon.c b/src/weapon.c index bd21b93b2..9a12df652 100644 --- a/src/weapon.c +++ b/src/weapon.c @@ -319,6 +319,10 @@ dmgval(struct obj *otmp, struct monst *mon) } } + /* Healer with anatomy knowledge */ + if (Role_if(PM_HEALER) && objects[otmp->otyp].oc_skill == P_KNIFE) + tmp += min(3, svm.mvitals[monsndx(ptr)].died / 6); + /* Put weapon vs. monster type damage bonuses in below: */ if (Is_weapon || otmp->oclass == GEM_CLASS || otmp->oclass == BALL_CLASS || otmp->oclass == CHAIN_CLASS) { From b76d4338beea6a5a744ceed06fde134833d4ef14 Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 9 Feb 2026 08:39:21 -0500 Subject: [PATCH 261/442] avoid a reported obuf reuse in use_misc() Report: https://github.com/NetHack/NetHack/issues/1486 Resolves #1486 --- src/muse.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/muse.c b/src/muse.c index 0f02778be..37334df7c 100644 --- a/src/muse.c +++ b/src/muse.c @@ -2520,7 +2520,7 @@ use_misc(struct monst *mtmp) int where_to = rn2(4); struct obj *obj = uwep; const char *hand; - char the_weapon[BUFSZ]; + char the_weapon[BUFSZ], hand_buf[BUFSZ]; if (!obj || !canletgo(obj, "") || (u.twoweap && canletgo(uswapwep, "") && rn2(2))) @@ -2532,10 +2532,12 @@ use_misc(struct monst *mtmp) hand = body_part(HAND); if (bimanual(obj)) hand = makeplural(hand); + (void) strncpy(hand_buf, hand, sizeof hand_buf - 1); + hand_buf[sizeof hand_buf - 1] = '\0'; if (vismon) pline_mon(mtmp, "%s flicks a bullwhip towards your %s!", - Monnam(mtmp), hand); + Monnam(mtmp), hand_buf); if (obj->otyp == HEAVY_IRON_BALL) { pline("%s fails to wrap around %s.", The_whip, the_weapon); return 1; @@ -2544,7 +2546,7 @@ use_misc(struct monst *mtmp) the_weapon); if (welded(obj)) { pline("%s welded to your %s%c", - !is_plural(obj) ? "It is" : "They are", hand, + !is_plural(obj) ? "It is" : "They are", hand_buf, !obj->bknown ? '!' : '.'); /* obj->bknown = 1; */ /* welded() takes care of this */ where_to = 0; @@ -2563,7 +2565,7 @@ use_misc(struct monst *mtmp) switch (where_to) { case 1: /* onto floor beneath mon */ pline_mon(mtmp, "%s yanks %s from your %s!", Monnam(mtmp), - the_weapon, hand); + the_weapon, hand_buf); place_object(obj, mtmp->mx, mtmp->my); break; case 2: /* onto floor beneath you */ From 1135f5ffe583081cbd329f78a54a7db0f5cc0429 Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 10 Feb 2026 13:50:01 -0800 Subject: [PATCH 262/442] Guidebook 'Relics' List the three invocation items in the same order as the oracularity about them does. --- doc/Guidebook.mn | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 89f581285..73ccaaa92 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -60,7 +60,7 @@ .ds f0 \*(vr .ds f1 \" empty .\"DO NOT REMOVE NH_DATESUB .ds f2 Date(%B %-d, %Y) -.ds f2 January 24, 2026 +.ds f2 February 10, 2026 . .\" A note on some special characters: .\" \(lq = left double quote @@ -2637,11 +2637,13 @@ There are three unique items that are named and have limited special powers but aren't classified as artifacts. Each is guarded by a particular monster and you'll need to collect all three for use late in the game. +.\" The relics are listed in the same order as the Oracle's message about +.\" them rather than in the order they need to be used for the invocation. They are \fIthe Bell of Opening\fP, -\fIthe Candelabrum of Invocation\fP, and -\fIthe Book of the Dead\fP. +\fIthe Book of the Dead\fP, and +\fIthe Candelabrum of Invocation\fP. Their corresponding descriptions when not yet identified are -silver bell, candelabrum, and papyrus spellbook. +silver bell, papyrus spellbook, and candelabrum. .hn 2 Weapons (\(oq)\(cq) .pg From db36ee35dea9aa16206f60caaa54c458e6fe2c9a Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Wed, 11 Feb 2026 08:17:34 +0200 Subject: [PATCH 263/442] Fix the healer knife bonus dmgval is also used for monster attacks; move the healer knife bonus to actual hero-specific code, and make sure it only applies to hand-to-hand combat. --- src/uhitm.c | 7 +++++++ src/weapon.c | 4 ---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/uhitm.c b/src/uhitm.c index 8b00bcdc0..1800d0e46 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -929,6 +929,13 @@ hmon_hitmon_weapon_melee( hmd->dmg = dmgval(obj, mon); /* a minimal hit doesn't exercise proficiency */ hmd->train_weapon_skill = (hmd->dmg > 1); + + /* Healer with anatomy knowledge */ + if (Role_if(PM_HEALER) && hmd->hand_to_hand + && obj->oclass == WEAPON_CLASS + && objects[obj->otyp].oc_skill == P_KNIFE) + hmd->dmg += min(3, svm.mvitals[monsndx(mon->data)].died / 6); + /* special attack actions */ if (!hmd->train_weapon_skill || mon == u.ustuck || u.twoweap /* Cleaver can hit up to three targets at once so don't diff --git a/src/weapon.c b/src/weapon.c index 9a12df652..bd21b93b2 100644 --- a/src/weapon.c +++ b/src/weapon.c @@ -319,10 +319,6 @@ dmgval(struct obj *otmp, struct monst *mon) } } - /* Healer with anatomy knowledge */ - if (Role_if(PM_HEALER) && objects[otmp->otyp].oc_skill == P_KNIFE) - tmp += min(3, svm.mvitals[monsndx(ptr)].died / 6); - /* Put weapon vs. monster type damage bonuses in below: */ if (Is_weapon || otmp->oclass == GEM_CLASS || otmp->oclass == BALL_CLASS || otmp->oclass == CHAIN_CLASS) { From fcd9f5468c57f180a1e511c5992d0952462621e6 Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 11 Feb 2026 09:40:25 -0500 Subject: [PATCH 264/442] some coordxy follow-up Return a couple of variables that actually held a direction back to int from coordxy. bhit() takes int params instead of coordxy. boomhit() takes int params instead of coordxy. xytod() renamed to xytodir(), and takes int params (promotion will handle coordxy params). dtoxy(coord *, int) renamed to dirtocoord(coord *, int). --- include/extern.h | 8 ++++---- src/cmd.c | 16 ++++++++-------- src/dig.c | 4 ++-- src/dogmove.c | 6 +++--- src/getpos.c | 2 +- src/hack.c | 6 +++--- src/muse.c | 6 +++--- src/pline.c | 2 +- src/steed.c | 8 ++++---- src/trap.c | 4 ++-- src/uhitm.c | 2 +- src/zap.c | 8 ++++---- 12 files changed, 36 insertions(+), 36 deletions(-) diff --git a/include/extern.h b/include/extern.h index 899f72b4a..5710dfa1a 100644 --- a/include/extern.h +++ b/include/extern.h @@ -449,8 +449,8 @@ extern int enter_explore_mode(void); extern boolean bind_mousebtn(int, const char *); extern boolean bind_key(uchar, const char *); extern void dokeylist(void); -extern int xytod(coordxy, coordxy); -extern void dtoxy(coord *, int); +extern int xytodir(int, int); +extern void dirtocoord(coord *, int); extern int movecmd(char, int); extern int dxdy_moveok(void); extern int getdir(const char *); @@ -3957,11 +3957,11 @@ extern int spell_damage_bonus(int); extern const char *exclam(int force) NONNULL; extern void hit(const char *, struct monst *, const char *) NONNULLPTRS; extern void miss(const char *, struct monst *) NONNULLPTRS; -extern struct monst *bhit(coordxy, coordxy, int, enum bhit_call_types, +extern struct monst *bhit(int, int, int, enum bhit_call_types, int(*)(struct monst *, struct obj *), int(*)(struct obj *, struct obj *), struct obj **) NONNULLARG7; -extern struct monst *boomhit(struct obj *, coordxy, coordxy) NONNULLARG1; +extern struct monst *boomhit(struct obj *, int, int) NONNULLARG1; extern int zhitm(struct monst *, int, int, struct obj **) NONNULLPTRS; extern int burn_floor_objects(coordxy, coordxy, boolean, boolean); extern void ubuzz(int, int); diff --git a/src/cmd.c b/src/cmd.c index 780bca6c3..e069c42ff 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -3615,7 +3615,7 @@ rhack(int key) /* convert an x,y pair into a direction code */ int -xytod(coordxy x, coordxy y) +xytodir(int x, int y) { int dd; @@ -3627,7 +3627,7 @@ xytod(coordxy x, coordxy y) /* convert a direction code into an x,y pair */ void -dtoxy(coord *cc, int dd) +dirtocoord(coord *cc, int dd) { if (dd > DIR_ERR && dd < N_DIRS_Z) { cc->x = xdir[dd]; @@ -3733,7 +3733,7 @@ getdir(const char *s) if (cmdq) { if (cmdq->typ == CMDQ_DIR) { if (!cmdq->dirz) { - dirsym = gc.Cmd.dirchars[xytod(cmdq->dirx, cmdq->diry)]; + dirsym = gc.Cmd.dirchars[xytodir(cmdq->dirx, cmdq->diry)]; } else { dirsym = gc.Cmd.dirchars[(cmdq->dirz > 0) ? DIR_DOWN : DIR_UP]; @@ -4501,7 +4501,7 @@ act_on_act( cmdq_add_dir(CQ_CANNED, dx, dy, 0); break; case MCMD_MOVE_DIR: - dir = xytod(dx, dy); + dir = xytodir(dx, dy); cmdq_add_ec(CQ_CANNED, move_funcs[dir][MV_WALK]); break; case MCMD_RIDE: @@ -4523,7 +4523,7 @@ act_on_act( } break; case MCMD_ATTACK_NEXT2U: - dir = xytod(dx, dy); + dir = xytodir(dx, dy); cmdq_add_ec(CQ_CANNED, move_funcs[dir][MV_WALK]); break; case MCMD_TALK: @@ -4633,7 +4633,7 @@ there_cmd_menu(coordxy x, coordxy y, int mod) if (!K) { /* no menu options, try to move */ if (next2u(x, y) && test_move(u.ux, u.uy, dx, dy, TEST_MOVE)) { - int dir = xytod(dx, dy); + int dir = xytodir(dx, dy); cmdq_add_ec(CQ_CANNED, move_funcs[dir][MV_WALK]); } else if (flags.travelcmd) { @@ -4727,7 +4727,7 @@ domouseaction(void) /* directional commands */ - dir = xytod(x, y); + dir = xytodir(x, y); if (!m_at(u.ux + x, u.uy + y) && !test_move(u.ux, u.uy, x, y, TEST_MOVE)) { if (IS_DOOR(levl[u.ux + x][u.uy + y].typ)) { @@ -4766,7 +4766,7 @@ domouseaction(void) cmdq_add_ec(CQ_CANNED, donull); return ECMD_OK; } - dir = xytod(x, y); + dir = xytodir(x, y); } /* move, attack, etc. */ diff --git a/src/dig.c b/src/dig.c index 579a97013..5e5fc1a74 100644 --- a/src/dig.c +++ b/src/dig.c @@ -1256,7 +1256,7 @@ use_pick_axe2(struct obj *obj) && (trap_with_u = t_at(u.ux, u.uy)) && is_pit(trap->ttyp) && !conjoined_pits(trap, trap_with_u, FALSE)) { - int idx = xytod(u.dx, u.dy); + int idx = xytodir(u.dx, u.dy); if (idx != DIR_ERR) { int adjidx = DIR_180(idx); @@ -1617,7 +1617,7 @@ zap_dig(void) if (u.utrap && u.utraptype == TT_PIT && (trap_with_u = t_at(u.ux, u.uy))) { pitdig = TRUE; - diridx = xytod(u.dx, u.dy); + diridx = xytodir(u.dx, u.dy); } digdepth = rn1(18, 8); tmp_at(DISP_BEAM, cmap_to_glyph(S_digbeam)); diff --git a/src/dogmove.c b/src/dogmove.c index c22eba74b..d0005ab78 100644 --- a/src/dogmove.c +++ b/src/dogmove.c @@ -1325,14 +1325,14 @@ dog_move( if (goodpos(cc.x, cc.y, mtmp, 0)) goto dognext; - i = xytod(nx, ny); + i = xytodir(nx, ny); for (j = DIR_LEFT(i); j < DIR_RIGHT(i); j++) { - dtoxy(&cc, j); + dirtocoord(&cc, j); if (goodpos(cc.x, cc.y, mtmp, 0)) goto dognext; } for (j = DIR_LEFT2(i); j < DIR_RIGHT2(i); j++) { - dtoxy(&cc, j); + dirtocoord(&cc, j); if (goodpos(cc.x, cc.y, mtmp, 0)) goto dognext; } diff --git a/src/getpos.c b/src/getpos.c index bc892a187..ed53d334b 100644 --- a/src/getpos.c +++ b/src/getpos.c @@ -561,7 +561,7 @@ dxdy_to_dist_descr(coordxy dx, coordxy dy, boolean fulldir) if (!dx && !dy) { Sprintf(buf, "here"); - } else if ((dst = xytod(dx, dy)) != -1) { + } else if ((dst = xytodir(dx, dy)) != -1) { /* explicit direction; 'one step' is implicit */ Sprintf(buf, "%s", directionname(dst)); } else { diff --git a/src/hack.c b/src/hack.c index c27e86357..360167541 100644 --- a/src/hack.c +++ b/src/hack.c @@ -1046,7 +1046,7 @@ test_move( else Sprintf(buf, "impossible [background glyph=%d]", glyph); - pline_dir(xytod(dx, dy), "It's %s.", buf); + pline_dir(xytodir(dx, dy), "It's %s.", buf); } } return FALSE; @@ -1197,7 +1197,7 @@ test_move( if (mode != TEST_TRAV && svc.context.run >= 2 && !(Blind || Hallucination) && !could_move_onto_boulder(x, y)) { if (mode == DO_MOVE && flags.mention_walls) - pline_dir(xytod(dx,dy), "A boulder blocks your path."); + pline_dir(xytodir(dx,dy), "A boulder blocks your path."); return FALSE; } if (mode == DO_MOVE) { @@ -2582,7 +2582,7 @@ move_out_of_bounds(coordxy x, coordxy y) dy = 0; } You("have already gone as far %s as possible.", - directionname(xytod(dx, dy))); + directionname(xytodir(dx, dy))); } nomul(0); svc.context.move = 0; diff --git a/src/muse.c b/src/muse.c index 37334df7c..c3c0b39d2 100644 --- a/src/muse.c +++ b/src/muse.c @@ -1348,14 +1348,14 @@ hero_behind_chokepoint(struct monst *mtmp) coordxy x = mtmp->mux + dx; coordxy y = mtmp->muy + dy; - int dir = xytod(dx, dy); + int dir = xytodir(dx, dy); int dir_l = DIR_CLAMP(DIR_LEFT2(dir)); int dir_r = DIR_CLAMP(DIR_RIGHT2(dir)); coord c1, c2; - dtoxy(&c1, dir_l); - dtoxy(&c2, dir_r); + dirtocoord(&c1, dir_l); + dirtocoord(&c2, dir_r); c1.x += x, c2.x += x; c1.y += y, c2.y += y; diff --git a/src/pline.c b/src/pline.c index f79cc6631..79dd8b28b 100644 --- a/src/pline.c +++ b/src/pline.c @@ -83,7 +83,7 @@ putmesg(const char *line) void set_msg_dir(int dir) { - dtoxy(&a11y.msg_loc, dir); + dirtocoord(&a11y.msg_loc, dir); a11y.msg_loc.x += u.ux; a11y.msg_loc.y += u.uy; } diff --git a/src/steed.c b/src/steed.c index 274fd779d..835a8562f 100644 --- a/src/steed.c +++ b/src/steed.c @@ -471,7 +471,7 @@ landing_spot( (void) memset((genericptr_t) try, 0, sizeof try); n = 0; - j = xytod(u.dx, u.dy); + j = xytodir(u.dx, u.dy); if (reason == DISMOUNT_KNOCKED && j != DIR_ERR) { /* we'll check preferred location first; if viable it'll be picked */ best_j = j; @@ -479,10 +479,10 @@ landing_spot( /* the two next best locations are checked second and third */ i = rn2(2); clockwise_j = DIR_RIGHT(j); /* (j + 1) % 8 */ - dtoxy(&cc, clockwise_j); + dirtocoord(&cc, clockwise_j); try[1 + i].x = cc.x, try[1 + i].y = cc.y; /* [1] or [2] */ counterclk_j = DIR_LEFT(j); /* (j + 8 - 1) % 8 */ - dtoxy(&cc, counterclk_j); + dirtocoord(&cc, counterclk_j); try[2 - i].x = cc.x, try[2 - i].y = cc.y; /* [2] or [1] */ n = 3; debugpline3("knock from saddle: best %s, next %s or %s", @@ -500,7 +500,7 @@ landing_spot( so odd j values are diagonal directions here */ if (reason == DISMOUNT_POLY && NODIAG(u.umonnum) && (j % 1) != 0) continue; - dtoxy(&cc, j); + dirtocoord(&cc, j); try[n++] = cc; } diff --git a/src/trap.c b/src/trap.c index b68df92c6..109c002ae 100644 --- a/src/trap.c +++ b/src/trap.c @@ -6473,7 +6473,7 @@ conjoined_pits( return FALSE; dx = sgn(trap2->tx - trap1->tx); dy = sgn(trap2->ty - trap1->ty); - diridx = xytod(dx, dy); + diridx = xytodir(dx, dy); if (diridx != DIR_ERR) { adjidx = DIR_180(diridx); if ((trap1->conjoined & (1 << diridx)) @@ -6514,7 +6514,7 @@ adj_nonconjoined_pit(struct trap *adjtrap) if (trap_with_u && adjtrap && u.utrap && u.utraptype == TT_PIT && is_pit(trap_with_u->ttyp) && is_pit(adjtrap->ttyp)) { - if (xytod(u.dx, u.dy) != DIR_ERR) + if (xytodir(u.dx, u.dy) != DIR_ERR) return TRUE; } return FALSE; diff --git a/src/uhitm.c b/src/uhitm.c index 1800d0e46..22a6456b3 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -663,7 +663,7 @@ hitum_cleave( int count, umort, x = u.ux, y = u.uy; /* find the direction toward primary target */ - i = xytod(u.dx, u.dy); + i = xytodir(u.dx, u.dy); if (i == DIR_ERR) { impossible("hitum_cleave: unknown target direction [%d,%d,%d]?", u.dx, u.dy, u.dz); diff --git a/src/zap.c b/src/zap.c index 33b532f79..2b2a04f37 100644 --- a/src/zap.c +++ b/src/zap.c @@ -3814,7 +3814,7 @@ zap_map( */ struct monst * bhit( - coordxy ddx, coordxy ddy, int range, /* direction and range */ + int ddx, int ddy, int range, /* direction and range */ enum bhit_call_types weapon, /* defined in hack.h */ int (*fhitm)(MONST_P, OBJ_P), /* fns called when mon/obj hit */ int (*fhito)(OBJ_P, OBJ_P), @@ -4134,7 +4134,7 @@ bhit( * is too obviously silly. */ struct monst * -boomhit(struct obj *obj, coordxy dx, coordxy dy) +boomhit(struct obj *obj, int dx, int dy) { int i, ct; int boom; /* showsym[] index */ @@ -4158,7 +4158,7 @@ boomhit(struct obj *obj, coordxy dx, coordxy dy) gb.bhitpos.x = u.ux; gb.bhitpos.y = u.uy; boom = counterclockwise ? S_boomleft : S_boomright; - i = xytod(dx, dy); + i = xytodir(dx, dy); tmp_at(DISP_FLASH, cmap_to_glyph(boom)); for (ct = 0; ct < 10; ct++) { i = DIR_CLAMP(i); @@ -4947,7 +4947,7 @@ dobuzz( goto buzzmonst; } else if (zap_hit((int) u.uac, 0)) { range -= 2; - pline_dir(xytod(-dx, -dy), "%s hits you!", + pline_dir(xytodir(-dx, -dy), "%s hits you!", The(flash_str(fltyp, FALSE))); if (Reflecting) { if (!Blind) { From 3e24fbcc4e4bdd398ae129d354822000719c07ce Mon Sep 17 00:00:00 2001 From: PatR Date: Wed, 11 Feb 2026 15:30:40 -0800 Subject: [PATCH 265/442] Guidebook.tex Artifacts and Relics Match recent Guidebook.mn addition of two new subsections for Objects. Untested. --- doc/Guidebook.mn | 14 +++++------ doc/Guidebook.tex | 62 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 67 insertions(+), 9 deletions(-) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 73ccaaa92..20caca85a 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -1,4 +1,4 @@ -.\" $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.611 $ $NHDT-Date: 1769271623 2026/01/24 08:20:23 $ +.\" $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.614 $ $NHDT-Date: 1770881338 2026/02/11 23:28:58 $ .\" .\" This is an excerpt from the 'roff' man page from the 'groff' package. .\"+-- @@ -60,7 +60,7 @@ .ds f0 \*(vr .ds f1 \" empty .\"DO NOT REMOVE NH_DATESUB .ds f2 Date(%B %-d, %Y) -.ds f2 February 10, 2026 +.ds f2 February 11, 2026 . .\" A note on some special characters: .\" \(lq = left double quote @@ -2608,8 +2608,8 @@ Some confer defensive capabilities when wielded or have other powers that aren't listed here. .pg You might find them simply lying on the floor, including but not limited -to inside shops, or be granted as a reward for \(lq#offer\(rq on an altar -to your patron deity. +to inside shops, or be granted as a reward for \(lq#offer\(rq on an +altar to your patron deity. A few might be dropped by monsters, or might be converted from an ordinary object of the same type via assigning the right name. .\" should we mention dipping for Excalibur here? @@ -2627,9 +2627,9 @@ These are commonly known as quest artifacts. All are aligned and most are non-weapons. They won't be found randomly. .pg -The \(oq\f(CR\\\fP\(cq and \(oq\f(CR\`a\fP\(cq commands will list -artifacts that you have fully identified (knowing the name and item type -isn't sufficient). +The \(oq\f(CR\\\fP\(cq and \(oq\f(CR\`a\fP\(cq commands will +list artifacts that you have fully identified (knowing the name and item +type isn't sufficient). .hn 2 Relics .pg diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 0ffb5fdfe..61785b1f0 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -1,4 +1,4 @@ -% NetHack 3.7 Guidebook.tex $NHDT-Date: 1745139202 2025/04/20 00:53:22 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.579 $ */ +% NetHack 3.7 Guidebook.tex $NHDT-Date: 1770881338 2026/02/11 23:28:58 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.596 $ */ \documentclass[titlepage]{article} \usepackage[hidelinks]{hyperref} \usepackage{longtable} @@ -25,7 +25,7 @@ \author{Original version - Eric S. Raymond\\ (Edited and expanded for 3.7.0 by Mike Stephenson and others)} %DO NOT REMOVE NH_DATESUB \date{Date(%B %-d, %Y)} -\date{January 3, 2026} +\date{February 11, 2026} \maketitle @@ -2790,6 +2790,64 @@ Sometimes the bless or curse state of objects is referred to as their or ``\texttt{BUCX}'' for Blessed, Uncursed, Cursed, or unknown. (The term \textit{beatitude} is occasionally used as well.) +%.hn 2 +\subsection*{Artifacts} + +%.pg +Some objects have been imbued with special powers and are known as +\textit{Artifacts}. +They have specific types (such as long sword or orcish dagger) and distinct +names such as \textit{Giantslayer} or \textit{Grimtooth}. +Artifact weapons typically do more damage than their ordinary counterparts. +Some do extra damage against all monsters, others only against specific +types of monsters so aren't better than regular weapons against other types. +Some confer defensive capabilities when wielded or have other powers that +aren't listed here. + +%.pg +You might find them simply lying on the floor, including but not limited +to inside shops, or be granted as a reward for ``\texttt{\#offer}'' on an +altar to your patron deity. +A few might be dropped by monsters, or might be converted from an ordinary +object of the same type via assigning the right name. +% should we mention dipping for Excalibur here? +Or you might wish for them, if you happen to be granted a wish, but such +wishes can fail. + +%.pg +Some artifacts have a specific alignment, others don't. +You won't obtain aligned ones that have a different alignment from yours +via offering and might get a shock if you attempt to wish for any of those +or find one and attempt to use it. + +%pg +Each role has a distinct artifact that is contained in the \textit{Quest} +dungeon branch. +These are commonly known as quest artifacts. +All are aligned and most are non-weapons. +They won't be found randomly. + +%.pg +The `\texttt{\textbackslash}' and `\texttt{\textasciigrave a}' commands will +list artifacts that you have fully identified (knowing the name and item +type isn't sufficient). + +%.hn 2 +\subsection*{Relics} + +%.pg +There are three unique items that are named and have limited special +powers but aren't classified as artifacts. +Each is guarded by a particular monster and you'll need to collect all +three for use late in the game. +% The relics are listed in the same order as the Oracle's message about +% them rather than in the order they need to be used for the invocation. +They are \textit{the Bell of Opening}, +\textit{the Book of the Dead}, and +\textit{the Candelabrum of Invocation}. +Their corresponding descriptions when not yet identified are +silver bell, papyrus spellbook, and candelabrum. + %.hn 2 \subsection*{Weapons (`\texttt{)}')} From 2487be72f6233c7953be72a89ed504a6fbd28f7f Mon Sep 17 00:00:00 2001 From: nhw_cron Date: Sat, 7 Feb 2026 09:16:08 -0500 Subject: [PATCH 266/442] This is cron-daily v1-Jan-12-2026. 005manpages updated: makedefs.txt --- doc/makedefs.txt | 54 ++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/doc/makedefs.txt b/doc/makedefs.txt index e784946c3..38a27f308 100644 --- a/doc/makedefs.txt +++ b/doc/makedefs.txt @@ -31,8 +31,8 @@ SHORT COMMANDS the MDGREP FUNCTIONS section below for details. -m Generate date.h and options file. It will read dat/gitinfo.txt, - only if it is present, to obtain githash= and gitbranch= - info and include related preprocessor #defines in date.h file. + only if it is present, to obtain githash= and gitbranch= info + and include related preprocessor #defines in date.h file. -p Generate pm.h @@ -53,7 +53,7 @@ LONG COMMANDS Show debugging output. --make [command] - Execute a short command. Command is given without preceding + Execute a short command. Command is given without preceding dash. --input file @@ -61,66 +61,66 @@ LONG COMMANDS is - standard input is read. --output file - Specify the output file for the command (if needed). If the + Specify the output file for the command (if needed). If the file is - standard output is written. --svs [delimiter] - Generate a version string to standard output without a trailing - newline. If specified, the delimiter is used between each part + Generate a version string to standard output without a trailing + newline. If specified, the delimiter is used between each part of the version string. - --grep Filter the input file to the output file. See the MDGREP FUNC- + --grep Filter the input file to the output file. See the MDGREP FUNC- TIONS section below for information on controlling the filtering operation. --grep-showvars - Show the name and value for each variable known to the grep + Show the name and value for each variable known to the grep option. --grep-trace - Turn on debug tracing for the grep function ( --grep must be + Turn on debug tracing for the grep function ( --grep must be specified as well). --grep-defined symbol - Exit shell true (0) if symbol is known and defined, otherwise + Exit shell true (0) if symbol is known and defined, otherwise exit shell false (1). --grep-define symbol - Force the value of symbol to be "defined." Symbol must already + Force the value of symbol to be "defined." Symbol must already be known to makedefs. --grep-undef symbol - Force the definition of symbol to be "undefined." Symbol must + Force the definition of symbol to be "undefined." Symbol must already be known to makedefs. MDGREP FUNCTIONS - The --grep command (and certain other commands) filter their input, on - a line-by-line basis, according to control lines embedded in the input - and on information gleaned from the NetHack(6) configuration. This - allows certain changes such as embedding platform-specific documenta- + The --grep command (and certain other commands) filter their input, on + a line-by-line basis, according to control lines embedded in the input + and on information gleaned from the NetHack(6) configuration. This + allows certain changes such as embedding platform-specific documenta- tion into the master documentation files. Rules: - The default conditional state is printing enabled. - - Any line NOT starting with a caret (^) is either suppressed - or passed through unchanged depending on the current condi- + - Any line NOT starting with a caret (^) is either suppressed + or passed through unchanged depending on the current condi- tional state. - - Any line starting with a caret is a control line; as in C, - zero or more spaces may be embedded in the line almost any- - where (except immediately after the caret); however the + - Any line starting with a caret is a control line; as in C, + zero or more spaces may be embedded in the line almost any- + where (except immediately after the caret); however the caret must be in column 1. - Conditionals may be nested. - - Makedefs will exit with an error code if any errors are - detected; processing will continue (if it can) to allow as + - Makedefs will exit with an error code if any errors are + detected; processing will continue (if it can) to allow as many errors as possible to be detected. - - Unknown identifiers are treated as both TRUE and as an - error. Note that --undef or #undef in the NetHack(6) con- + - Unknown identifiers are treated as both TRUE and as an + error. Note that --undef or #undef in the NetHack(6) con- figuration are different from unknown. Control lines: @@ -143,8 +143,8 @@ AUTHOR The NetHack Development Team COPYRIGHT - This file is Copyright (C) Kenneth Lorber, 2024 for version - NetHack-3.7:1.22. NetHack may be freely redistributed. See license + This file is Copyright (C) Kenneth Lorber, 2024 for version + NetHack-3.7:1.22. NetHack may be freely redistributed. See license for details. From a55ca3ab8733bb801151e7164882656779ab3bc4 Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 12 Feb 2026 06:30:54 -0500 Subject: [PATCH 267/442] Guidebook.txt update --- doc/Guidebook.txt | 1918 +++++++++++++++++++++++---------------------- 1 file changed, 992 insertions(+), 926 deletions(-) diff --git a/doc/Guidebook.txt b/doc/Guidebook.txt index 9c25c65e2..1a362eb63 100644 --- a/doc/Guidebook.txt +++ b/doc/Guidebook.txt @@ -15,7 +15,7 @@ Original version - Eric S. Raymond (Edited and expanded for NetHack 3.7.0 by Mike Stephenson and others) - December 11, 2025 + February 11, 2026 @@ -126,7 +126,7 @@ employ to great advantage. - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -192,7 +192,7 @@ NetHack continues this fine tradition. Unlike text adventure games - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -258,7 +258,7 @@ - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -324,7 +324,7 @@ - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -390,7 +390,7 @@ - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -456,7 +456,7 @@ The number of turns elapsed so far, displayed if you have the - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -522,7 +522,7 @@ - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -588,7 +588,7 @@ - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -654,7 +654,7 @@ - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -720,7 +720,7 @@ instead. Only these one-step movement commands cause you to - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -786,7 +786,7 @@ ing . ^ is used as shorthand elsewhere in the - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -852,7 +852,7 @@ - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -918,7 +918,7 @@ - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -984,7 +984,7 @@ - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -1050,7 +1050,7 @@ at an adjacent "remembered, unseen monster" marker. - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -1116,7 +1116,7 @@ - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -1182,7 +1182,7 @@ (R)UNIX is a registered trademark of The Open Group. - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -1248,7 +1248,7 @@ menu_next_page, and menu_last_page keys (`^', `<', `>', `|' by - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -1314,7 +1314,7 @@ doesn't and give that name to the result, while splitting (count - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -1380,7 +1380,7 @@ - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -1446,7 +1446,7 @@ extinct. - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -1512,7 +1512,7 @@ - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -1578,7 +1578,7 @@ - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -1644,7 +1644,7 @@ right away.) Since using this command by accident can cause - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -1710,7 +1710,7 @@ Default key is `M-R'. - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -1776,7 +1776,7 @@ (worn blindfold or towel or lenses, lit lamp(s) and/or candle(s), - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -1842,7 +1842,7 @@ - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -1908,7 +1908,7 @@ line will show "(no travel path)" if your character does not know - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -1974,7 +1974,7 @@ - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -2040,7 +2040,7 @@ Set one or more intrinsic attributes. Autocompletes. Debug mode - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -2106,7 +2106,7 @@ "high"] bit), you can invoke many extended commands by meta-ing the - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -2172,7 +2172,7 @@ - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -2238,7 +2238,7 @@ - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -2304,7 +2304,7 @@ - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -2370,7 +2370,7 @@ them). Some monsters who can open doors can also use unlocking tools. - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -2436,7 +2436,7 @@ been nullified, giving access to whatever is beyond them. In the - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -2502,7 +2502,7 @@ play. - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -2568,7 +2568,7 @@ - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -2634,7 +2634,7 @@ attack, when guessing where an unseen monster is or when deliberately - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -2700,7 +2700,7 @@ noid_confirmation:attack option to require a response of "yes" - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -2766,7 +2766,7 @@ - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -2832,7 +2832,7 @@ are encumbered, one of the conditions Burdened, Stressed, Strained, - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -2898,7 +2898,7 @@ - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 @@ -2922,7 +2922,59 @@ "BUCX" for Blessed, Uncursed, Cursed, or unknown. (The term beatitude is occasionally used as well.) - 7.2. Weapons (`)') + 7.2. Artifacts + + Some objects have been imbued with special powers and are known + as Artifacts. They have specific types (such as long sword or orcish + dagger) and distinct names such as Giantslayer or Grimtooth. Artifact + weapons typically do more damage than their ordinary counterparts. + Some do extra damage against all monsters, others only against spe- + cific types of monsters so aren't better than regular weapons against + other types. Some confer defensive capabilities when wielded or have + other powers that aren't listed here. + + You might find them simply lying on the floor, including but not + limited to inside shops, or be granted as a reward for "#offer" on an + altar to your patron deity. A few might be dropped by monsters, or + might be converted from an ordinary object of the same type via + assigning the right name. Or you might wish for them, if you happen + to be granted a wish, but such wishes can fail. + + Some artifacts have a specific alignment, others don't. You + won't obtain aligned ones that have a different alignment from yours + via offering and might get a shock if you attempt to wish for any of + those or find one and attempt to use it. + + Each role has a distinct artifact that is contained in the Quest + dungeon branch. These are commonly known as quest artifacts. All are + aligned and most are non-weapons. They won't be found randomly. + + The `\' and ``a' commands will list artifacts that you have fully + identified (knowing the name and item type isn't sufficient). + + 7.3. Relics + + There are three unique items that are named and have limited spe- + cial powers but aren't classified as artifacts. Each is guarded by a + particular monster and you'll need to collect all three for use late + in the game. They are the Bell of Opening, the Book of the Dead, and + the Candelabrum of Invocation. Their corresponding descriptions when + not yet identified are silver bell, papyrus spellbook, and cande- + labrum. + + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 46 + + + + 7.4. Weapons (`)') Given a chance, most monsters in the Mazes of Menace will gratu- itously try to kill you. You need weapons for self-defense (killing @@ -2962,18 +3014,6 @@ And if you have proficiency in the "two weapon combat" skill, you may wield both weapons simultaneously as primary and secondary; use the `X' command to engage or disengage that. Only some types of charac- - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 46 - - - ters (barbarians, for instance) have the necessary skill available. Even with that skill, using two weapons at once incurs a penalty in the chance to hit your target compared to using just one weapon at a @@ -2988,13 +3028,25 @@ each weapon which existed in AD&D does roughly the same damage to mon- sters in NetHack. Some of the more obscure weapons (such as the aklys, lucern hammer, and bec-de-corbin) are defined in an appendix to + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 47 + + + Unearthed Arcana, an AD&D supplement. The commands to use weapons are `w' (wield), `t' (throw), `f' (fire), `Q' (quiver), `x' (exchange), `X' (twoweapon), and "#enhance" (see below). - 7.2.1. Throwing and shooting + 7.4.1. Throwing and shooting You can throw just about anything via the `t' command. It will prompt for the item to throw; picking `?' will list things in your @@ -3027,19 +3079,6 @@ quiver. The fire command also has extra assistance, if fireassist is on it will try to wield a launcher matching the ammo in the quiver. - - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 47 - - - Some characters have the ability to throw or shoot a volley of multiple items (from the same stack) in a single action. Knowing how to load several rounds of ammunition at once--or hold several missiles @@ -3055,11 +3094,23 @@ most 2 arrows are shot even if you could have fired 3. If you specify a larger number than would have been shot ("4f" in this example), you'll just end up shooting the same number (3, here) as if no limit + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 48 + + + had been specified. Once the volley is in motion, all of the items will travel in the same direction; if the first ones kill a monster, the others can still continue beyond that spot. - 7.2.2. Weapon proficiency + 7.4.2. Weapon proficiency You will have varying degrees of skill in the weapons available. Weapon proficiency, or weapon skills, affect how well you can use par- @@ -3094,24 +3145,12 @@ the next skill level (unless you've already reached the limit for this skill). Once such training reaches the threshold for that next level, you'll be told that you feel more confident in your skills. At that - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 48 - - - point you can use "#enhance" to increase one or more skills. Such skills are not increased automatically because there is a limit to your total overall skills, so you need to actively choose which skills to enhance and which to ignore. - 7.2.3. Two-Weapon combat + 7.4.3. Two-Weapon combat Some characters can use two weapons at once. Setting things up to do so can seem cumbersome but becomes second nature with use. To @@ -3121,6 +3160,18 @@ with is considered primary and the other one is considered secondary. The most noticeable difference is after you stop--or before you begin, for that matter--wielding two weapons at once. The primary is your + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 49 + + + wielded weapon and the secondary is just an item in your inventory that's been designated as alternate weapon.) @@ -3145,7 +3196,7 @@ ons or having one of them be stolen or destroyed will also make you revert to single-weapon combat. - 7.3. Armor (`[') + 7.5. Armor (`[') Lots of unfriendly things lurk about; you need armor to protect yourself from their blows. Some types of armor offer better protec- @@ -3159,19 +3210,6 @@ Dragon scale mail 1 Plate mail, Crystal plate mail 3 Bronze plate mail, Splint mail, - - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 49 - - - Banded mail, Dwarvish mithril-coat 4 Chain mail, Elven mithril-coat 5 Scale mail, Orcish chain mail 6 @@ -3188,6 +3226,18 @@ also be enchanted. Shirts are an exception; they don't provide any protection unless enchanted. Some cloaks also don't improve AC when unenchanted but all cloaks offer some protection against rust or cor- + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 50 + + + rosion to suits worn under them and against some monster touch attacks. @@ -3214,7 +3264,7 @@ can be used for armor, but pieces of armor won't be shown as likely candidates in a prompt for choosing what to put on or remove. - 7.4. Food (`%') + 7.6. Food (`%') Food is necessary to survive. If you go too long without eating you will faint, and eventually die of starvation. Some types of food @@ -3227,17 +3277,6 @@ special powers when you eat them. A good rule of thumb is "you are what you eat." - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 50 - - - Some character roles and some monsters are vegetarian. Vegetar- ian monsters will typically never eat animal corpses, while vegetarian players can, but with some rather unpleasant side-effects. @@ -3247,13 +3286,24 @@ The command to eat food is `e'. - 7.5. Scrolls (`?') + 7.7. Scrolls (`?') Scrolls are labeled with various titles, probably chosen by ancient wizards for their amusement value (for example "READ ME," or "THANX MAUD" backwards). Scrolls disappear after you read them (except for blank ones, without magic spells on them). + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 51 + + + One of the most useful of these is the scroll of identify, which can be used to determine what another object is, whether it is cursed or blessed, and how many uses it has left. Some objects of subtle @@ -3281,7 +3331,7 @@ The command to read a scroll is `r'. - 7.6. Potions (`!') + 7.8. Potions (`!') Potions are distinguished by the color of the liquid inside the flask. They disappear after you quaff them. @@ -3292,21 +3342,9 @@ at them. It is also sometimes very useful to dip ("#dip") an object into a potion. - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 51 - - - The command to drink a potion is `q' (quaff). - 7.7. Wands (`/') + 7.9. Wands (`/') Wands usually have multiple magical charges. Some types of wands require a direction in which to zap them. You can also zap them at @@ -3320,6 +3358,18 @@ ally, however, it may be possible to squeeze the last few mana points from an otherwise spent wand, destroying it in the process. A wand may be recharged by using suitable magic, but doing so runs the risk + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 52 + + + of causing it to explode. The chance for such an explosion starts out very small and increases each time the wand is recharged. @@ -3337,7 +3387,7 @@ The command to use a wand is `z' (zap). To break one, use the `a' (apply) command. - 7.8. Rings (`=') + 7.10. Rings (`=') Rings are very useful items, since they are relatively permanent magic, unlike the usually fleeting effects of potions, scrolls, and @@ -3358,19 +3408,7 @@ The commands to use rings are `P' (put on) and `R' (remove). `A', `W', and `T' can also be used; see Amulets. - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 52 - - - - 7.9. Spellbooks (`+') + 7.11. Spellbooks (`+') Spellbooks are tomes of mighty magic. When studied with the `r' (read) command, they transfer to the reader the knowledge of a spell @@ -3386,6 +3424,18 @@ Casting a spell calls forth magical energies and focuses them with your naked mind. Some of the magical energy released comes from + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 53 + + + within you. Casting temporarily drains your magical power, which will slowly be recovered, and causes you to need additional food. Casting of spells also requires practice. With practice, your skill in each @@ -3417,25 +3467,13 @@ of how strongly it is remembered. The `Z' (cast) command casts a spell. - 7.10. Tools (`(') + 7.12. Tools (`(') Tools are miscellaneous objects with various purposes. Some tools have a limited number of uses, akin to wand charges. For exam- ple, lamps burn out after a while. Other tools are containers, which objects can be placed into or taken out of. - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 53 - - - Some tools (such as a blindfold) can be worn and can be put on and removed like other accessories (rings, amulets); see Amulets. Other tools (such as pick-axe) can be wielded as weapons in addition @@ -3448,10 +3486,22 @@ The command to use a tool is `a' (apply). - 7.10.1. Containers + 7.12.1. Containers You may encounter bags, boxes, and chests in your travels. A tool of this sort can be opened with the "#loot" extended command when + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 54 + + + you are standing on top of it (that is, on the same floor spot), or with the `a' (apply) command when you are carrying it. However, chests are often locked, and are in any case unwieldy objects. You @@ -3487,22 +3537,7 @@ tents into another container. (As of this writing, the other con- tainer must be carried rather than on the floor.) - - - - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 54 - - - - 7.11. Amulets (`"') + 7.13. Amulets (`"') Amulets are very similar to rings, and often more powerful. Like rings, amulets have various magical properties, some beneficial, some @@ -3519,7 +3554,21 @@ and eyewear), but accessories won't be shown as likely candidates in a prompt for choosing what to wear or take off. - 7.12. Gems (`*') + + + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 55 + + + + 7.14. Gems (`*') Some gems are valuable, and can be sold for a lot of gold. They are also a far more efficient way of carrying your riches. Valuable @@ -3530,7 +3579,7 @@ (if you have a sling). In the most desperate of cases, you can still throw them by hand. - 7.13. Large rocks (``') + 7.15. Large rocks (``') Statues and boulders are not particularly useful, and are gener- ally heavy. It is rumored that some statues are not what they seem. @@ -3556,19 +3605,7 @@ shown as ``' but by the letter representing the monster they depict instead. - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 55 - - - - 7.14. Gold (`$') + 7.16. Gold (`$') Gold adds to your score, and you can buy things in shops with it. There are a number of monsters in the dungeon that may be influenced @@ -3582,7 +3619,22 @@ matters when you're using an object selection prompt that can filter by "BUCX" state. - 7.15. Persistence of Objects + + + + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 56 + + + + 7.17. Persistence of Objects Normally, if you have seen an object at a particular map location and move to another location where you can't directly see that object @@ -3623,17 +3675,6 @@ your god for help with starvation does not violate any food challenges either. - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 56 - - - A strict vegan diet is one which avoids any food derived from animals. The primary source of nutrition is fruits and vegetables. The corpses and tins of blobs (`b'), jellies (`j'), and fungi (`F') @@ -3647,6 +3688,18 @@ Vegetarians do not eat animals; however, they are less selective about eating animal byproducts than vegans. In addition to the vegan items listed above, they may eat any kind of pudding (`P') other than + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 57 + + + the black puddings, eggs and food made from eggs (fortune cookies and pancakes), food made with milk (cream pies and candy bars), and lumps of royal jelly. Monks are expected to observe a vegetarian diet. @@ -3689,17 +3742,6 @@ and kick weapons; use a wand, spell, or other type of item; or fight with your hands and feet. - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 57 - - - In NetHack, a pacifist refuses to cause the death of any other monster (i.e. if you would get experience for the death). This is a particularly difficult challenge, although it is still possible to @@ -3712,6 +3754,18 @@ engraving, or any item that is absolutely necessary to win the game, is not counted against this conduct. The identity of scrolls and spellbooks (and knowledge of spells) in your starting inventory is + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 58 + + + assumed to be learned from your teachers prior to the start of the game and isn't counted. @@ -3753,19 +3807,6 @@ to make a wish for an item, you may choose "nothing" if you want to decline. - - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 58 - - - 8.1. Achievements End of game disclosure will also display various achievements @@ -3778,6 +3819,19 @@ Rank - Attained rank title Rank. Shop - Entered a shop. + + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 59 + + + Temple - Entered a temple. Mines - Entered the Gnomish Mines. Town - Entered Mine Town. @@ -3820,18 +3874,6 @@ to revert to lower rank(s) does not discard the corresponding achieve- ment(s). - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 59 - - - There's no guaranteed Novel so the achievement to read one might not always be attainable (except perhaps by wishing). Similarly, the Big Room level is not always present. Unlike with the Novel, there's @@ -3843,6 +3885,19 @@ other instances of the same objects doesn't record the corresponding achievement. + + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 60 + + + The Medusa achievement is recorded if she dies for any reason, even if you are not directly responsible, and only if she dies. @@ -3886,18 +3941,6 @@ "%USERPROFILE%\NetHack\". The file may not exist, but it is a normal ASCII text file can can be created with any text editor. After run- ning NetHack for the first time, you should find a default template - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 60 - - - for the configuration file named ".nethackrc.template" in "%USERPROFILE%\NetHack\". If you have not created the configuration file, NetHack will create one for you using the default template file. @@ -3909,6 +3952,18 @@ Any line beginning with `[' and ending in `]' is a section marker (the closing `]' can be followed by whitespace and then an arbitrary + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 61 + + + comment beginning with `#'). The text between the square brackets is the section name. Section markers are only valid after a CHOOSE directive and their names are case-insensitive. Lines after a section @@ -3952,18 +4007,6 @@ The location where saved games are kept. Defaults to HACKDIR, must be writable. - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 61 - - - BONESDIR The location that bones files are kept. Defaults to HACKDIR, must be writable. @@ -3976,6 +4019,17 @@ The location that a record of game aborts and self-diagnosed game problems is kept. Defaults to HACKDIR, must be writable. + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 62 + + + AUTOCOMPLETE Enable or disable an extended command autocompletion. Autocomple- tion has no effect for the X11 windowport. You can specify multiple @@ -4018,18 +4072,6 @@ If [] is present, the preceding section is closed and no new section begins; whatever follows will be common to all sections. Otherwise - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 62 - - - the last section extends to the end of the options file. MENUCOLOR @@ -4043,6 +4085,17 @@ ROGUESYMBOLS Custom symbols for the rogue level's symbol set. See SYMBOLS below. + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 63 + + + SOUND Define a sound mapping. See the "Configuring User Sounds" section. @@ -4074,28 +4127,6 @@ Here is an example of configuration file contents: - - - - - - - - - - - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 63 - - - # Set your character's role, race, gender, and alignment. OPTIONS=role:Valkyrie, race:Human, gender:female, align:lawful # @@ -4119,6 +4150,18 @@ The NETHACKOPTIONS variable is a comma-separated list of initial values for the various options. Some can only be turned on or off. + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 64 + + + You turn one of these on by adding the name of the option to the list, and turn it off by typing a `!' or "no" before the name. Others take a character string as a value. You can set string options by typing @@ -4150,18 +4193,6 @@ be set to the full name of a configuration file you want to use. If that full name doesn't start with a slash, precede it with `@' (at- sign) to let NetHack know that the rest is intended as a file name. - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 64 - - - If it does start with `/', the at-sign is optional. 9.4. Customization options @@ -4185,6 +4216,18 @@ alignment Your starting alignment (align:lawful, align:neutral, or align:chaotic). You may specify just the first letter. Many roles + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 65 + + + and the non-human races restrict which alignments are allowed. See role for a description of how to use negation to exclude choices. @@ -4216,18 +4259,6 @@ autoquiver This option controls what happens when you attempt the `f' (fire) - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 65 - - - command when nothing is quivered or readied (default false). When true, the computer will fill your quiver or quiver sack or make ready some suitable weapon. Note that it will not take into account @@ -4251,6 +4282,18 @@ using it; Kick - kick the door (if you omit untrap or decline to attempt untrap and you omit apply-key or you lack a key or you + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 66 + + + decline to use the key; has no effect on containers); Force - try to force a container's lid with your currently wielded weapon (if you omit untrap or decline to attempt @@ -4282,18 +4325,6 @@ catname Name your starting cat (for example "catname:Morris"). Cannot be - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 66 - - - set with the `O' command. character @@ -4318,6 +4349,17 @@ deaf Start the character permanently deaf (default false). Persistent. + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 67 + + + dropped_nopick If this option is on, items you dropped will not be automatically picked up, even if autopickup is also on and they are in @@ -4348,18 +4390,6 @@ The listings of vanquished monsters and of genocided types can be sorted, so there are two additional choices for `v' and `g': - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 67 - - - ? - prompt you and default to ask on the prompt; # - disclose it without prompting, ask for sort order. @@ -4384,6 +4414,18 @@ shows all levels you had visited but does not reveal things about them that you hadn't discovered. + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 68 + + + dogname Name your starting dog (for example "dogname:Fang"). Cannot be set with the `O' command. @@ -4414,18 +4456,6 @@ on). If this is off, dropping an object shifts all the remaining inventory letters. Persistent. - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 68 - - - force_invmenu Commands asking for an inventory item show a menu instead of a text query with possible menu letters. Default is off. @@ -4449,6 +4479,19 @@ be prompted unless role and/or race forces a choice for gender. Cannot be set with the `O' command. Persistent. + + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 69 + + + goldX When filtering objects based on bless/curse state (BUCX), whether to treat gold pieces as X (unknown bless/curse state, when "on") or U @@ -4479,19 +4522,6 @@ highlight pets and setting it will turn the hilite_pet option on or off as warranted. - - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 69 - - - hilite_pile Visually distinguish piles of objects from individual objects (default off). The behavior of this option depends on the type of @@ -4516,6 +4546,18 @@ The "Qt" interface also supports hitpointbar, by drawing a solid bar above the name and title with a hard-coded color scheme. (As of + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 70 + + + this writing, having the bar enabled unintentionally inhibits resiz- ing the status panel. To resize that, use the #optionsfull command to toggle the hitpointbar option off, perform the resize while it's @@ -4546,18 +4588,6 @@ lootabc When using a menu to interact with a container, use the old `a', `b', and `c' keyboard shortcuts rather than the mnemonics `o', `i', - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 70 - - - and `b' (default off). Persistent. mail @@ -4582,6 +4612,18 @@ menucolors Enable coloring menu lines (default off). See "Configuring Menu + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 71 + + + Colors" on how to configure the colors. menustyle @@ -4612,18 +4654,6 @@ menu_first_page Key to jump to the first page in a menu. Default `^'. - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 71 - - - menu_headings Controls how the headings in a menu are highlighted. Takes a text attribute, or text color and attribute separated by ampersand. For @@ -4648,6 +4678,18 @@ beginning of each group. You can have menus add the display symbol for the class of objects for each header line. You can also add the display symbol for the individual item in each menu entry. For a + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 72 + + + tiles map, that would be a small rendition of an object's tile. For a text map, it is the same character as is used for the object's class, which would be most useful when there are no headers separat- @@ -4678,18 +4720,6 @@ Key to search for some text and toggle selection state of matching menu items. Default `:'. - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 72 - - - menu_select_all Key to select all items in a menu. Default `.'. @@ -4714,6 +4744,18 @@ Prompt for new form whenever any monster changes shape (default off). Debug mode only. + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 73 + + + montelecontrol Prompt for destination whenever any monster gets teleported (default off). Debug mode only. @@ -4744,18 +4786,6 @@ For backward compatibility, no value needs to be specified (which defaults to "full"), or it can be negated (which defaults to "sin- - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 73 - - - gle"). name @@ -4781,6 +4811,17 @@ null Send padding nulls to the terminal (default on). Persistent. + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 74 + + + number_pad Use digit keys instead of letters to move (default 0 or off). Valid settings are: @@ -4810,18 +4851,6 @@ paranoid_confirmation A space separated list of specific situations where alternate - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 74 - - - prompting is desired. The default is "paranoid_confirmation:pray swim trap". @@ -4847,6 +4876,18 @@ immediately praying; on by default; (to require "yes" rather than just `y', set Confirm too); trap - require `y' to confirm an attempt to move into or onto + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 75 + + + a known trap, unless doing so is considered to be harmless; when enabled, this confirmation is also used for moving into visible gas cloud regions; (to require @@ -4876,18 +4917,6 @@ new entries and remove some old ones, you can use multiple para- noid_confirmation option settings, or you can use the `+' form and list entries to be added by their name and entries to be removed by - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 75 - - - `!' and name. The positive (no `!') and negative (with `!') entries can be intermixed. @@ -4913,6 +4942,18 @@ full - show full inventory including gold; in-use - only show items which are in use (worn, wielded, lit lamp). + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 76 + + + Default is none but if perm_invent gets set to true while it is none it will be changed to all. @@ -4941,19 +4982,6 @@ Loaded), you will be asked if you want to continue. (Default `S'). Persistent. - - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 76 - - - pickup_stolen If this option is on and autopickup is also on, try to pick up things that a monster stole from you, even if they aren't in @@ -4981,6 +5009,17 @@ symbol for venom and you won't come across any venom items so won't unintentionally pick such up. + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 77 + + + pile_limit When walking across a pile of objects on the floor, threshold at which the message "there are few/several/many objects here" is given @@ -5008,18 +5047,6 @@ query_menu Use a menu when asked specific yes/no queries, instead of a prompt. - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 77 - - - quick_farsight When set, usually prevents the "you sense your surroundings" message where play pauses to allow you to browse the map whenever clairvoy- @@ -5047,6 +5074,18 @@ on whether or not you win the game than your actions later in the game). This option exists partly as an acknowledgement that some players will insist on doing so anyway, and partly because rerolling + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 78 + + + may be necessary for certain types of challenge games. rest_on_space @@ -5074,18 +5113,6 @@ If role is not specified, there is no default value; player will be prompted. Cannot be set with the `O' command. Persistent. - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 78 - - - roguesymset This option may be used to select one of the named symbol sets found within "symbols" to alter the symbols displayed on the screen on the @@ -5113,6 +5140,18 @@ depend upon the window port used or on the type of terminal. Per- sistent. + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 79 + + + safe_pet Prevent you from (knowingly) attacking your pets (default on). Per- sistent. @@ -5139,19 +5178,6 @@ Show your accumulated experience points on bottom line (default off). Persistent. - - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 79 - - - showrace Display yourself as the glyph for your race, rather than the glyph for your role (default off). Note that this setting affects only @@ -5179,6 +5205,19 @@ Controls the sorting behavior for the output of the `\' and ``' com- mands. Persistent. + + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 80 + + + The possible values are: o - list object types by class, in discovery order within each @@ -5205,19 +5244,6 @@ with the #loot and pickup commands; none - show lists the traditional way without sorting; default. - - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 80 - - - sortpack Sort the pack contents by type when displaying inventory (default on). Persistent. @@ -5247,6 +5273,17 @@ Allow sounds to be emitted from an integrated sound library (default on). + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 81 + + + sparkle Display a sparkly effect when a monster (including yourself) is hit by an attack to which it is resistant (default on). Persistent. @@ -5271,19 +5308,6 @@ notification messages about feature changes for that and prior ver- sions (for example "suppress_alert:3.3.1"). - - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 81 - - - symset This option may be used to select one of the named symbol sets found within "symbols" to alter the symbols displayed on the screen. Use @@ -5313,6 +5337,19 @@ dow, but it no longer leaves the score list around after game end on a terminal or emulating window. + + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 82 + + + travel Allow the travel command via mouse click (default on). Turning this option off will prevent the game from attempting unintended moves if @@ -5339,20 +5376,9 @@ s - screen [row,column] (row is offset to match tty usage); n - none (no coordinates shown) [default]. - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 82 - - - - The whatis_coord option is also used with the "/m", "/M", "/o", - and "/O" sub-commands of `/', where the "none" setting is over- - ridden with "map". + The whatis_coord option is also used with the "/m", "/M", "/o", and + "/O" sub-commands of `/', where the "none" setting is overridden + with "map". whatis_filter When getting a location on the map, and using the keys to cycle @@ -5378,6 +5404,18 @@ whatis_moveskip When getting a location on the map, and using shifted movement keys or meta-digit keys to fast-move, instead of moving 8 units at a + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 83 + + + time, move by skipping the same glyphs. (default off) windowtype @@ -5402,20 +5440,6 @@ contents. Not all ports support zero-comp compression. It has no effect on reading an existing save file. - - - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 83 - - - 9.5. Window Port Customization options Here are explanations of the various options that are used to @@ -5446,6 +5470,18 @@ IBMgraphics if your display supports them. Setting ascii_map to True forces tiled_map to be False. + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 84 + + + color If NetHack can, it should display color if it can for different mon- sters, objects, and dungeon features (default on). @@ -5471,17 +5507,6 @@ If NetHack can, it should use a font by the chosen name for the sta- tus window. - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 84 - - - font_text If NetHack can, it should use a font by the chosen name for text windows. @@ -5510,6 +5535,19 @@ non-map data (such as menu selector letters). Curses interface only; default is on. + + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 85 + + + large_font If NetHack can, it should use a large font. @@ -5536,18 +5574,6 @@ scroll_margin If NetHack can, it should scroll the display when the hero or cursor - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 85 - - - is this number of cells away from the edge of the window. selectsaved @@ -5575,6 +5601,19 @@ The curses interface does likewise if the align_status option is set to top or bottom but ignores statuslines when set to left or right. + + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 86 + + + The Qt interface already displays more than 3 lines for status so uses the statuslines value differently. A value of 3 renders status in the Qt interface's original format, with the status window spread @@ -5601,19 +5640,6 @@ options to select an alternate tile file. See NetHack.ad, the sam- ple X "application defaults" file. - - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 86 - - - tile_height Specify the preferred height of each tile in a tile capable port. @@ -5642,6 +5668,18 @@ persistent inventory window if enabled. Curses interface only. Acceptable values are + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 87 + + + 0 - off, never show borders 1 - on, always show borders 2 - auto, on if display is at least (24+2)x(80+2) [default] @@ -5667,19 +5705,6 @@ OPTION=windowcolors:style foreground/background - - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 87 - - - where style is one of "menu", "message", "status", or "text", and foreground and background are colors, either numeric (hash sign fol- lowed by three pairs of hexadecimal digits, #rrggbb), one of the @@ -5709,6 +5734,18 @@ OPTION=crash_urlmax:bytes This option is used to limit the length of the URLs generated + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 88 + + + and is only needed if your browser cannot handle arbitrarily long URLs. @@ -5735,17 +5772,6 @@ complete the two character sequence. Type a second ESC to finish cancelling such a count. At other prompts a single ESC suffices. - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 88 - - - BIOS Use BIOS calls to update the screen display quickly and to read the keyboard (allowing the use of arrow keys to move) on machines with @@ -5774,6 +5800,18 @@ 16 colors, a mode that is compatible with all VGA hardware. Third party tilesets will probably not work. Setting "autodetect" attempts "vesa", then "vga", and finally sets "default" if neither + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 89 + + + of those modes works. Cannot be set with the `O' command. video_height @@ -5800,18 +5838,6 @@ Regular expressions are normally POSIX extended regular expres- sions. It is possible to compile NetHack without regular expression support on a platform where there is no regular expression library. - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 89 - - - While this is not true of any modern platform, if your NetHack was built this way, patterns are instead glob patterns; regardless, this document refers to both as `regular expressions.' This applies to @@ -5840,6 +5866,18 @@ The autopickup_exception rules are processed in the order in which they appear in your configuration file, thus allowing a later rule + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 90 + + + to override an earlier rule. Exceptions can be set with the `O' command, but because they are not @@ -5867,17 +5905,6 @@ character ("x"), a control key ("^X", "C-x"), a meta key ("M-x"), a mouse button, or a three-digit decimal ASCII code. - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 90 - - - For example: BIND=^X:getpos.autodescribe @@ -5905,6 +5932,18 @@ text", and if bound to same keys, only one of those commands will be available. Special command can only be bound to a single key. + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 91 + + + count Prefix key to start a count, to repeat a command this many times. With number_pad only. Default is `n'. @@ -5933,17 +5972,6 @@ When asked for a location, the key to toggle autodescribe. Default is `#'. - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 91 - - - getpos.all.next When asked for a location, the key to go to next closest interesting thing. Default is `a'. @@ -5971,6 +5999,17 @@ When asked for a location, the key to go to previous closest mon- ster. Default is `M'. + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 92 + + + getpos.obj.next When asked for a location, the key to go to next closest object. Default is `o'. @@ -5998,18 +6037,6 @@ getpos.pick When asked for a location, the key to choose the location, and pos- sibly ask for more info. When simulating a mouse click after being - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 92 - - - asked for a direction (see getdir.mouse above), the key to use to respond as right click. Default is `.'. @@ -6036,6 +6063,19 @@ When asked for a location, the key to go to next closest unexplored location. Default is `x'. + + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 93 + + + getpos.unexplored.prev When asked for a location, the key to go to previous closest unex- plored location. Default is `X'. @@ -6063,19 +6103,6 @@ type - how the message should be shown; pattern - the pattern to match. - - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 93 - - - The pattern should be a regular expression. Allowed types are: @@ -6103,6 +6130,18 @@ Some platforms allow you to define colors used in menu lines when the line matches a user-defined pattern. At this time the tty, + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 94 + + + curses, win32tty and win32gui interfaces support this. In general, the configuration file entries to describe the menu @@ -6131,17 +6170,6 @@ Here's an example of menu colors using NetHack's internal pattern matching facility: - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 94 - - - MENUCOLOR="* blessed *"=green MENUCOLOR="* cursed *"=red MENUCOLOR="* cursed *(being worn)"=red&underline @@ -6168,6 +6196,18 @@ The following configuration file entries are relevant to mapping user sounds to messages: + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 95 + + + SOUNDDIR The directory that houses the sound files to be played. @@ -6194,20 +6234,6 @@ - - - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 95 - - - 9.14. Configuring Status Hilites Your copy of NetHack may have been compiled with support for @@ -6236,6 +6262,18 @@ Allowed colors are black, red, green, brown, blue, magenta, cyan, gray, orange, light-green, yellow, light-blue, light-magenta, light- + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 96 + + + cyan, and white. And "no-color", the default foreground color on the display, which is not necessarily the same as black or white or any of the other colors. @@ -6261,19 +6299,6 @@ You can adjust the appearance of the following status fields: title dungeon-level experience-level strength gold experience - - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 96 - - - dexterity hitpoints HD constitution hitpoints-max time intelligence power hunger @@ -6303,6 +6328,18 @@ * "always" will set the default attributes for that field. + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 97 + + + * "up", "down" set the field attributes for when the field value changes upwards or downwards. This attribute times out after statushilites turns. @@ -6328,18 +6365,6 @@ is based on the progress from the start of the current experi- ence level to the start of the next level. So if level 2 starts at 20 points and level 3 starts at 40 points, having 30 - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 97 - - - points is 50% and 35 points is 75%. 100% is unattainable for experience because you'll gain a level and the calculations will be reset for that new level, but a rule for =100% is @@ -6368,6 +6393,19 @@ The in-game options menu can help you determine the correct syn- tax for a configuration file. + + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 98 + + + The whole feature can be disabled by setting option statushilites to 0. @@ -6393,19 +6431,6 @@ The options that are used to select a particular symbol set from the symbol file are: - - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 98 - - - symset Set the name of the symbol set that you want to load. @@ -6434,6 +6459,19 @@ [ S_armor (suit or piece of armor) [ S_armour (suit or piece of armor) ^ S_arrow_trap (arrow trap) + + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 99 + + + 0 S_ball (iron ball) # S_bars (iron bars) B S_bat (bat or bird) @@ -6459,19 +6497,6 @@ # S_darkroom (dark room) ^ S_dart_trap (dart trap) & S_demon (major demon) - - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 99 - - - * S_digbeam (dig beam) > S_dnladder (ladder down) > S_dnstair (staircase down) @@ -6500,6 +6525,19 @@ F S_fungus (fungus or mold) * S_gem (gem or rock) S_ghost (ghost) + + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 100 + + + H S_giant (giant humanoid) G S_gnome (gnome) ' S_golem (golem) @@ -6525,19 +6563,6 @@ } S_lava (molten lava) } S_lavawall (wall of lava) l S_leprechaun (leprechaun) - - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 100 - - - ^ S_level_teleporter (level teleporter) L S_lich (lich) y S_light (light) @@ -6566,6 +6591,19 @@ = S_ring (ring) ` S_rock (boulder or statue) r S_rodent (rodent) + + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 101 + + + ^ S_rolling_boulder_trap (rolling boulder trap) . S_room (floor of a room) / S_rslant (diagonal beam [zap animation]) @@ -6591,19 +6629,6 @@ | S_sw_ml (swallow middle left) | S_sw_mr (swallow middle right) - S_sw_tc (swallow top center) - - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 101 - - - / S_sw_tl (swallow top left) \ S_sw_tr (swallow top right) - S_tdwall (wall) @@ -6632,6 +6657,19 @@ ^ S_vibrating_square (vibrating square) . S_vodbridge (vertical lowered drawbridge) - S_vodoor (open door in vertical wall) + + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 102 + + + v S_vortex (vortex) | S_vwall (vertical wall) / S_wand (wand) @@ -6658,18 +6696,6 @@ * S_rock is misleadingly named; rocks and stones use S_gem. Statues and boulders are the rock being referred to, but since version - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 102 - - - 3.6.0, statues are displayed as the monster they depict. So S_rock is only used for boulders and not used at all if overridden by the more specific S_boulder. @@ -6698,6 +6724,18 @@ The list of acceptable glyphid's can be produced by nethack --dumpg- lyphids. Individual NetHack glyphs can be specified using the G_ pre- + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 103 + + + fix, or you can use an S_ symbol for a glyphid and store the custom representation for all NetHack glyphs that would map to that particu- lar symbol. @@ -6725,17 +6763,6 @@ are often useful in giving players a better sense of the overall loca- tion of items on the screen. - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 103 - - - NetHack can also be compiled with support for sending the game messages to an external program, such as a text-to-speech synthesizer. If the "#version" extended command shows "external program as a mes- @@ -6764,6 +6791,17 @@ paranoid_confirmation:swim Prevent walking into water or lava. + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 104 + + + accessiblemsg Adds direction or location information to messages. @@ -6789,19 +6827,6 @@ When targeting with cursor, describe the cursor position with coor- dinates relative to your character. - - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 104 - - - whatis_filter:area When targeting with cursor, filter possible locations so only those in the same area (eg. same room, or same corridor) are considered. @@ -6831,6 +6856,18 @@ WIZARDS = A space-separated list of user names who are allowed to play in debug mode (commonly referred to as wizard mode). A value of a single asterisk (*) allows anyone to start a game in debug + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 105 + + + mode. SHELLERS = A list of users who are allowed to use the shell escape @@ -6855,19 +6892,6 @@ SEDUCE = 0 or 1 to disable or enable, respectively, the SEDUCE option. When disabled, incubi and succubi behave like nymphs. - - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 105 - - - CHECK_PLNAME = Setting this to 1 will make the EXPLORERS, WIZARDS, and SHELLERS check for the player name instead of the user's login name. @@ -6898,6 +6922,18 @@ ity for players to set S_pet_override and S_hero_override symbols in their configuration file. + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 106 + + + PORTABLE_DEVICE_PATHS = 0 or 1 Windows OS only, the game will look for all of its external files, and write to all of its output files in one place rather than at the standard locations. @@ -6922,18 +6958,6 @@ panying the program contains a comment which lists the meaning of the various bits used. Intended for server systems supporting simultaneous play by multiple players (to be clear, each one running - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 106 - - - a separate single player game), for displaying their game progress to observers. Only relevant if the program was built with LIVELOG enabled. When available, it should be left commented out on single @@ -6964,6 +6988,18 @@ only hear about 90% of your gold when your corpse is discovered (adventurers have been known to collect finder's fees). So, consider whether you want to take one last hit at that monster and possibly + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 107 + + + live, or quit and stop with whatever you have. If you quit, you keep all your gold, but if you swing and live, you might find more. @@ -6986,20 +7022,6 @@ switching during play does not. The other benefits of explore mode are left for the trepid reader to discover. - - - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 107 - - - 11.1. Debug mode Debug mode, also known as wizard mode, is undocumented aside from @@ -7032,6 +7054,18 @@ Jay Fenlason wrote the original Hack, with help from Kenny Wood- land, Mike Thome, and Jon Payne. + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 108 + + + Andries Brouwer did a major re-write while at Stichting Mathema- tisch Centrum (now Centrum Wiskunde & Informatica), transforming Hack into a very different game. He published the Hack source code for use @@ -7054,18 +7088,6 @@ Mike Stephenson merged these various versions back together, incorporating many of the added features, and produced NetHack version 1.4 in 1987. He then coordinated a cast of thousands in enhancing and - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 108 - - - debugging NetHack 1.4 and released NetHack versions 2.2 and 2.3. Like Hack, they were released by posting their source code to Usenet where they remained available in various archives accessible via ftp and @@ -7098,6 +7120,18 @@ Janet Walz, the NetHack Development Team which now included Ken Arromdee, David Cohrs, Jean-Christophe Collet, Kevin Darcy, Matt Day, Timo Hakulinen, Steve Linhart, Dean Luick, Pat Rankin, Eric Raymond, + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 109 + + + and Eric Smith undertook a radical revision of 3.0. They re-struc- tured the game's design, and re-wrote major parts of the code. They added multiple dungeons, a new display, special individual character @@ -7120,18 +7154,6 @@ Timo Hakulinen ported NetHack 3.1 to OS/2. Eric Smith ported NetHack 3.1 to the Atari. Pat Rankin, with help from Joshua - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 109 - - - Delahunty, was responsible for the VMS version of NetHack 3.1. Michael Allison ported NetHack 3.1 to Windows NT. @@ -7164,6 +7186,18 @@ thirteen members of the original NetHack Development Team remained on the team at the start of work on that release. During the interval between the release of 3.1.3 and 3.2.0, one of the founding members of + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 110 + + + the NetHack Development Team, Dr. Izchak Miller, was diagnosed with cancer and passed away. That release of the game was dedicated to him by the development and porting teams. @@ -7186,18 +7220,6 @@ Warren Cheung combined SLASH with the Wizard Patch to produce Slash'EM, and with the help of Kevin Hugo, added more features. Kevin - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 110 - - - later joined the NetHack Development Team and incorporated the best of these ideas into NetHack 3.3. @@ -7230,6 +7252,18 @@ and Ranger roles joined Archeologists, Barbarians, Cavemen, Healers, Knights, Priests, Rogues, Samurai, Tourists, Valkyries and of course, Wizards. It was also the first version to allow you to ride a steed, + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 111 + + + and was the first version to have a publicly available web-site list- ing all the bugs that had been discovered. Despite that constantly growing bug list, 3.3 proved stable enough to last for more than a @@ -7253,17 +7287,6 @@ Dean Luick, Mark Modrall, and Kevin Hugo maintained and enhanced the Macintosh port of 3.4. - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 111 - - - Michael Allison, David Cohrs, Alex Kompel, Dion Nicolaas, and Yitzhak Sapir maintained and enhanced 3.4 for the Microsoft Windows platform. Alex Kompel contributed a new graphical interface for the @@ -7295,6 +7318,18 @@ In September 2014, an interim snapshot of the code under develop- ment was released publicly by other parties. Since that code was a + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 112 + + + work-in-progress and had not gone through the process of debugging it as a suitable release, it was decided that the version numbers present on that code snapshot would be retired and never used in an official @@ -7318,18 +7353,6 @@ game, author Terry Pratchett, passed away. NetHack 3.6.0 introduced a tribute to him. - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 112 - - - 3.6.0 was released in December 2015, and merged work done by the development team since the release of 3.4.3 with some of the beloved community patches. Many bugs were fixed and some code was restruc- @@ -7362,6 +7385,17 @@ Rankin, Derek S. Ray, Alex Smith, Mike Stephenson, Janet Walz, and Paul Winner. + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 113 + + + In early May 2019, another 320 bug fixes along with some enhance- ments and the adopted curses window port, were released as 3.6.2. @@ -7384,18 +7418,6 @@ NetHack 3.6.7 was released on February 16, 2023 containing a security fix and some bug fixes. - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 113 - - - The official NetHack web site is maintained by Ken Lorber at https://www.nethack.org/. @@ -7430,12 +7452,13 @@ + NetHack 3.7.0 February 11, 2026 - + NetHack Guidebook 114 @@ -7452,13 +7475,40 @@ - NetHack 3.7.0 December 11, 2025 - NetHack Guidebook 114 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -7466,6 +7516,18 @@ From time to time, some depraved individual out there in netland sends a particularly intriguing modification to help out with the + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 115 + + + game. The NetHack Development Team sometimes makes note of the names of the worst of these miscreants in this, the list of Dungeoneers: @@ -7515,23 +7577,23 @@ Andy Church Jeff Bailey Pasi Kallinen Andy Swanson Jochen Erwied Pat Rankin Andy Thomson John Kallen Patric Mueller - - - - NetHack 3.7.0 December 11, 2025 - - - - - - NetHack Guidebook 115 - - - Ari Huttunen John Rupley Paul Winner Bart House John S. Bien Pierre Martineau Benson I. Margulies Johnny Lee Ralf Brown Bill Dyer Jon W{tte Ray Chason + + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 116 + + + Boudewijn Waijers Jonathan Handler Richard Addison Bruce Cox Joshua Delahunty Richard Beigel Bruce Holloway Karl Garrison Richard P. Hughey @@ -7584,13 +7646,17 @@ - NetHack 3.7.0 December 11, 2025 + + + + + NetHack 3.7.0 February 11, 2026 - NetHack Guidebook 116 + NetHack Guidebook 117 @@ -7650,7 +7716,7 @@ - NetHack 3.7.0 December 11, 2025 + NetHack 3.7.0 February 11, 2026 From cccb0303ed80ebc193a9e6366ebeda8bf6dcb96a Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 12 Feb 2026 07:01:52 -0500 Subject: [PATCH 268/442] update tested versions of Visual Studio 2026-02-12 --- 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 aa713f1c7..074a0c15f 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.25 -# - Microsoft Visual Studio Community 2026 v 18.2.1 +# - Microsoft Visual Studio Community 2022 v 17.14.26 +# - Microsoft Visual Studio Community 2026 v 18.3.0 # #============================================================================== # This is used for building two distinct executables of NetHack: @@ -1189,7 +1189,7 @@ rc=Rc.exe # # Recently tested versions: TESTEDVS2022 = 14.44.35222.0 -TESTEDVS2026 = 14.50.35723.0 +TESTEDVS2026 = 14.50.35724.0 VS20261ST = 1450000000 VS2026CUR = $(TESTEDVS2026:.=) From bbc3eea06638529305b2137887a19e004d1d459e Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Thu, 12 Feb 2026 14:32:58 +0200 Subject: [PATCH 269/442] Make sure negatively enchanted boomerang hits anything --- src/zap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zap.c b/src/zap.c index 2b2a04f37..b3540afba 100644 --- a/src/zap.c +++ b/src/zap.c @@ -4140,7 +4140,7 @@ boomhit(struct obj *obj, int dx, int dy) int boom; /* showsym[] index */ struct monst *mtmp; boolean counterclockwise = URIGHTY; /* ULEFTY => clockwise */ - int nhits = (obj->spe + 1); + int nhits = max(1, obj->spe + 1); /* counterclockwise traversal patterns, from @ to 1 then on through to 9 * ..........................54................................. From b074657f2bc162bb28cb4d8acbfa6fe96a3816dd Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 12 Feb 2026 07:37:31 -0800 Subject: [PATCH 270/442] Guidebook.tex tidbit This should eliminate an unwanted space. Untested. --- doc/Guidebook.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 61785b1f0..62fc927a4 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -2828,7 +2828,7 @@ All are aligned and most are non-weapons. They won't be found randomly. %.pg -The `\texttt{\textbackslash}' and `\texttt{\textasciigrave a}' commands will +The `\texttt{\textbackslash}' and `\texttt{\textasciigrave{}a}' commands will list artifacts that you have fully identified (knowing the name and item type isn't sufficient). From a561538c2a79e7624aa61ce584e63d59f7f824a6 Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 12 Feb 2026 10:33:09 -0800 Subject: [PATCH 271/442] cursed potions of invisibility for monsters Extend the recently changed behavior for cursed potion of invisibility. Monsters won't drink potions of invisibility if already invisible so can't accidentally or voluntarily make themselves visible again, but let player make them become visible by hitting them with thrown or wielded cursed potion of invisibility. They don't have any concept of temporary invisibility that might let them remain invisible while losing permanent invisibility, so they just lose the latter and immediately become visible. --- include/extern.h | 4 ++-- src/makemon.c | 4 ++-- src/mcastu.c | 4 ++-- src/mon.c | 4 ++-- src/muse.c | 8 ++++++-- src/potion.c | 19 +++++++++++++------ src/worn.c | 10 ++++++---- src/zap.c | 4 ++-- 8 files changed, 35 insertions(+), 22 deletions(-) diff --git a/include/extern.h b/include/extern.h index 5710dfa1a..2593f9362 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 extern.h $NHDT-Date: 1764044196 2025/11/24 20:16:36 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1509 $ */ +/* NetHack 3.7 extern.h $NHDT-Date: 1770949988 2026/02/12 18:33:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1523 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -3891,7 +3891,7 @@ extern int wornmask_to_armcat(long); extern long armcat_to_wornmask(int); extern long wearslot(struct obj *) NONNULLARG1; extern void check_wornmask_slots(void); -extern void mon_set_minvis(struct monst *) NONNULLARG1; +extern void mon_set_minvis(struct monst *, boolean) NONNULLARG1; extern void mon_adjust_speed(struct monst *, int, struct obj *) NONNULLARG1; extern void update_mon_extrinsics(struct monst *, struct obj *, boolean, boolean) NONNULLARG12; diff --git a/src/makemon.c b/src/makemon.c index c95fe8439..130694401 100644 --- a/src/makemon.c +++ b/src/makemon.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 makemon.c $NHDT-Date: 1720128166 2024/07/04 21:22:46 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.249 $ */ +/* NetHack 3.7 makemon.c $NHDT-Date: 1770949988 2026/02/12 18:33:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.271 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1294,7 +1294,7 @@ makemon( mtmp->seen_resistance = M_SEEN_NOTHING; mtmp->mpeaceful = (mmflags & MM_ANGRY) ? FALSE : peace_minded(ptr); if ((mmflags & MM_MINVIS) != 0) /* for ^G */ - mon_set_minvis(mtmp); /* call after place_monster() */ + mon_set_minvis(mtmp, FALSE); /* call after place_monster() */ switch (ptr->mlet) { case S_MIMIC: diff --git a/src/mcastu.c b/src/mcastu.c index 95e9a1c65..29792429f 100644 --- a/src/mcastu.c +++ b/src/mcastu.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mcastu.c $NHDT-Date: 1726168598 2024/09/12 19:16:38 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.105 $ */ +/* NetHack 3.7 mcastu.c $NHDT-Date: 1770949988 2026/02/12 18:33:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.111 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -566,7 +566,7 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum) if (canseemon(mtmp)) pline_mon(mtmp, "%s suddenly %s!", Monnam(mtmp), !See_invisible ? "disappears" : "becomes transparent"); - mon_set_minvis(mtmp); + mon_set_minvis(mtmp, FALSE); if (cansee(mtmp->mx, mtmp->my) && !canspotmon(mtmp)) map_invisible(mtmp->mx, mtmp->my); dmg = 0; diff --git a/src/mon.c b/src/mon.c index 883db340c..4488bd8fc 100644 --- a/src/mon.c +++ b/src/mon.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mon.c $NHDT-Date: 1753856387 2025/07/29 22:19:47 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.611 $ */ +/* NetHack 3.7 mon.c $NHDT-Date: 1770949988 2026/02/12 18:33:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.621 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1784,7 +1784,7 @@ mon_givit(struct monst *mtmp, struct permonst *ptr) char mtmpbuf[BUFSZ]; Strcpy(mtmpbuf, Monnam(mtmp)); - mon_set_minvis(mtmp); + mon_set_minvis(mtmp, FALSE); if (vis) pline_mon(mtmp, "%s %s.", mtmpbuf, !canspotmon(mtmp) ? "vanishes" diff --git a/src/muse.c b/src/muse.c index c3c0b39d2..8c95e00ec 100644 --- a/src/muse.c +++ b/src/muse.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 muse.c $NHDT-Date: 1737392015 2025/01/20 08:53:35 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.234 $ */ +/* NetHack 3.7 muse.c $NHDT-Date: 1770949988 2026/02/12 18:33:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.241 $ */ /* Copyright (C) 1990 by Ken Arromdee */ /* NetHack may be freely redistributed. See license for details. */ @@ -2426,7 +2426,7 @@ use_misc(struct monst *mtmp) mquaffmsg(mtmp, otmp); /* format monster's name before altering its visibility */ Strcpy(nambuf, mon_nam(mtmp)); - mon_set_minvis(mtmp); + mon_set_minvis(mtmp, !otmp->cursed ? FALSE : TRUE); if (vismon && mtmp->minvis) { /* was seen, now invisible */ if (canspotmon(mtmp)) { pline("%s body takes on a %s transparency.", @@ -2439,6 +2439,10 @@ use_misc(struct monst *mtmp) } if (oseen) makeknown(otmp->otyp); + } else if (!vismon && canseemon(mtmp)) { + /* cursed potion; this won't happen because a monster will only + drink a potion of invisibility when not already invisible */ + pline("%s suddenly appears!", Monnam(mtmp)); } if (otmp->otyp == POT_INVISIBILITY) { if (otmp->cursed) diff --git a/src/potion.c b/src/potion.c index 072d33b29..2ea79fb44 100644 --- a/src/potion.c +++ b/src/potion.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 potion.c $NHDT-Date: 1737605675 2025/01/22 20:14:35 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.274 $ */ +/* NetHack 3.7 potion.c $NHDT-Date: 1770949988 2026/02/12 18:33:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.279 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1777,12 +1777,19 @@ potionhit(struct monst *mon, struct obj *obj, int how) mon->mconf = TRUE; break; case POT_INVISIBILITY: { - boolean sawit = canspotmon(mon); + boolean sawit = canspotmon(mon), + cursed_potion = obj->cursed ? TRUE : FALSE; - angermon = FALSE; - mon_set_minvis(mon); - if (sawit && !canspotmon(mon) && cansee(mon->mx, mon->my)) - map_invisible(mon->mx, mon->my); + angermon = mon->minvis && cursed_potion; + mon_set_minvis(mon, cursed_potion); + if (sawit && !canspotmon(mon)) { + if (cansee(mon->mx, mon->my)) + map_invisible(mon->mx, mon->my); + } else if (!sawit && canspotmon(mon)) { + /* if an invisible mon glyph was present, mon_set_minvis()'s + newsym() has gotten rid of it */ + pline("%s appears!", Monnam(mon)); + } break; } case POT_SLEEPING: diff --git a/src/worn.c b/src/worn.c index 176a7793c..6b2c082c1 100644 --- a/src/worn.c +++ b/src/worn.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 worn.c $NHDT-Date: 1736530208 2025/01/10 09:30:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.116 $ */ +/* NetHack 3.7 worn.c $NHDT-Date: 1770949988 2026/02/12 18:33:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.119 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -463,11 +463,13 @@ check_wornmask_slots(void) } /* check_wornmask_slots() */ void -mon_set_minvis(struct monst *mon) +mon_set_minvis( + struct monst *mon, + boolean cursed_potion) { - mon->perminvis = 1; + mon->perminvis = !cursed_potion ? 1 : 0; if (!mon->invis_blkd) { - mon->minvis = 1; + mon->minvis = mon->perminvis; newsym(mon->mx, mon->my); /* make it disappear */ if (mon->wormno) see_wsegs(mon); /* and any tail too */ diff --git a/src/zap.c b/src/zap.c index b3540afba..ec49363f5 100644 --- a/src/zap.c +++ b/src/zap.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 zap.c $NHDT-Date: 1741793439 2025/03/12 07:30:39 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.564 $ */ +/* NetHack 3.7 zap.c $NHDT-Date: 1770949988 2026/02/12 18:33:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.584 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -353,7 +353,7 @@ bhitm(struct monst *mtmp, struct obj *otmp) seemimic(mtmp); /* format monster's name before altering its visibility */ Strcpy(nambuf, Monnam(mtmp)); - mon_set_minvis(mtmp); + mon_set_minvis(mtmp, FALSE); if (!oldinvis && knowninvisible(mtmp)) { pline("%s turns transparent!", nambuf); reveal_invis = TRUE; From 78217c3ebd36f6a797158dc121bbdabf796fbe79 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Fri, 13 Feb 2026 12:50:27 +0200 Subject: [PATCH 272/442] Allow rogues to backstab sleeping or paralyzed monsters --- doc/fixes3-7-0.txt | 2 ++ src/uhitm.c | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 3bcb9e172..6f801ae22 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1579,6 +1579,8 @@ light-spell is clerical, if playing a priest boomerang can hit multiple monsters the "bustling town" minetown has more peacefuls healers may get tiny damage increase when attacking with knives +allow rogues to also backstab sleeping or paralyzed monsters +rogues cannot backstab monsters that have no backside Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/uhitm.c b/src/uhitm.c index 22a6456b3..af4e8ca8a 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -26,6 +26,7 @@ staticfn void hmon_hitmon_barehands(struct _hitmon_data *, struct monst *) NONNULLARG12; staticfn void hmon_hitmon_weapon_ranged(struct _hitmon_data *, struct monst *, struct obj *) NONNULLARG123; +staticfn boolean backstabbable(struct monst *) NONNULLARG1; staticfn void hmon_hitmon_weapon_melee(struct _hitmon_data *, struct monst *, struct obj *) NONNULLARG123; staticfn void hmon_hitmon_weapon(struct _hitmon_data *, struct monst *, @@ -915,6 +916,20 @@ hmon_hitmon_weapon_ranged( } } +/* can monster be stabbed in the back? */ +staticfn boolean +backstabbable(struct monst *mon) +{ + return !amorphous(mon->data) + && !is_whirly(mon->data) + && !noncorporeal(mon->data) + && mon->data->mlet != S_BLOB + && mon->data->mlet != S_EYE + && mon->data->mlet != S_FUNGUS + && canseemon(mon) + && (mon->mflee || helpless(mon)); +} + staticfn void hmon_hitmon_weapon_melee( struct _hitmon_data *hmd, @@ -942,7 +957,7 @@ hmon_hitmon_weapon_melee( let it also hit from behind or shatter foes' weapons */ || (hmd->hand_to_hand && is_art(obj, ART_CLEAVER))) { ; /* no special bonuses */ - } else if (mon->mflee && Role_if(PM_ROGUE) && !Upolyd + } else if (Role_if(PM_ROGUE) && backstabbable(mon) && !Upolyd /* multi-shot throwing is too powerful here */ && hmd->hand_to_hand) { You("strike %s from behind!", mon_nam(mon)); From f4bb0d93e508a19a73223b61de9894d90ac29088 Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 14 Feb 2026 21:03:58 -0800 Subject: [PATCH 273/442] observed objects This has been laying around for a few weeks. I meant to do more but have forgotten what the rest would have been. Don't mark generic objects as dknown. --- src/o_init.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/o_init.c b/src/o_init.c index 8b81da282..0ba9e1577 100644 --- a/src/o_init.c +++ b/src/o_init.c @@ -440,16 +440,21 @@ restnames(NHFILE *nhfp) void observe_object(struct obj *obj) { - obj->dknown = 1; - discover_object(obj->otyp, FALSE, TRUE, FALSE); + int oindx = obj->otyp; + + /* skip for generic objects and for STRANGE_OBJECT */ + if (oindx >= FIRST_OBJECT && !Hallucination) { + obj->dknown = 1; + discover_object(oindx, FALSE, TRUE, FALSE); + } } void discover_object( - int oindx, - boolean mark_as_known, - boolean mark_as_encountered, - boolean credit_hero) + int oindx, /* type of object */ + boolean mark_as_known, /* discover the type */ + boolean mark_as_encountered, /* mark the type as having been seen/felt */ + boolean credit_hero) /* exercise wisdom */ { if (oindx < FIRST_OBJECT) /* don't discover generic objects */ return; From fe7b3ce09d9ec4f7278cb0a3826ca69333c545b1 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 15 Feb 2026 17:01:49 +0200 Subject: [PATCH 274/442] Empty tin hallu msg --- src/eat.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/eat.c b/src/eat.c index 5060b7b36..e62a2ffea 100644 --- a/src/eat.c +++ b/src/eat.c @@ -1546,7 +1546,12 @@ consume_tin(const char *mesg) if (r != SPINACH_TIN) { mnum = tin->corpsenm; if (mnum == NON_PM) { - pline("It turns out to be empty."); + if (Hallucination) + pline("It's full of %s.", + rn2(2) ? "air elemental souffle" + : "dehydrated water"); + else + pline("It turns out to be empty."); observe_object(tin); tin->known = 1; tin = costly_tin(COST_OPEN); From 651a91f94c24e7596d288ce0de6a1f6bdbe7e3ee Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 15 Feb 2026 11:48:40 -0800 Subject: [PATCH 275/442] explain Amulet wish In wizard mode, I used ^W to wish for the Amulet of Yendor and was immediately prompted for another wish. It was baffling and took a while to figure out. Give a message before initiating the wish. --- doc/fixes3-7-0.txt | 1 + src/allmain.c | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 6f801ae22..ebe99a2bb 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -2208,6 +2208,7 @@ when reading a T-shirt or apron, add trailing period to the slogan unless when reading an engraving, suppress the addition of a trailing period if the text already has one cursed magic whistle could teleport hero on no-teleport levels +give a brief explanation when receving a wish for acquiring the Amulet Fixes to 3.7.0-x Platform and/or Interface Problems Exposed Via git Repository diff --git a/src/allmain.c b/src/allmain.c index 7a431ea27..2db536e5e 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 allmain.c $NHDT-Date: 1744860497 2025/04/16 19:28:17 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.276 $ */ +/* NetHack 3.7 allmain.c $NHDT-Date: 1771213100 2026/02/15 19:38:20 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.286 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -446,6 +446,8 @@ moveloop_core(void) /* the Amulet of Yendor gives a wish when initially picked up */ if (u.uhave.amulet && !u.uevent.amulet_wish) { u.uevent.amulet_wish = 1; + display_nhwindow(WIN_MESSAGE, TRUE); + urgent_pline("The Amulet is bestowing a wish upon you!"); makewish(); } From 1bb7146cbda387d74c5e8e55ed3265eb91c01b8a Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 15 Feb 2026 12:37:55 -0800 Subject: [PATCH 276/442] \#knownclass (back-tick) of relics The ` command already supports selecting 'u' for unique items as a pseudo-class. Add support for 'r' as synonym to match the recent addition of 'relics' to the Guidebook. The unique item category includes the Amulet of Yendor in addition to the three invocation items. 'r' shows it too, once it has been fully IDed. I'm ambivalent as to whether the Guidebook's Relics section should mention it. --- src/o_init.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/o_init.c b/src/o_init.c index 0ba9e1577..07b744d3e 100644 --- a/src/o_init.c +++ b/src/o_init.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 o_init.c $NHDT-Date: 1756520041 2025/08/29 18:14:01 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.96 $ */ +/* NetHack 3.7 o_init.c $NHDT-Date: 1771216675 2026/02/15 20:37:55 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.101 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -194,7 +194,7 @@ init_objects(void) bases[class] through bases[class+1]-1 for all classes (except for ILLOBJ_CLASS which is separated from WEAPON_CLASS by generic objects); second extra entry is to prevent an - explained crash in doclassdisco(), where the code ended up + unexplained crash in doclassdisco(), where the code ended up attempting to process non-existent class MAXOCLASSES; the [MAXOCLASSES+1] element gives that non-class 0 objects when traversing objects[] from bases[X] through bases[X+1]-1 */ @@ -766,7 +766,8 @@ dodiscovered(void) /* free after Robert Viduya */ for (i = dis = 0; i < SIZE(uniq_objs); i++) if (objects[uniq_objs[i]].oc_name_known) { if (!dis++) - putstr(tmpwin, iflags.menu_headings.attr, "Unique items"); + putstr(tmpwin, iflags.menu_headings.attr, + "Unique items or Relics"); ++uniq_ct; Sprintf(buf, " %s", OBJ_NAME(objects[uniq_objs[i]])); putstr(tmpwin, 0, buf); @@ -845,20 +846,22 @@ oclass_to_name(char oclass, char *buf) return buf; } -/* the #knownclass command - show discovered object types for one class */ +/* the #knownclass command - show discovered object types for one class; + in addition to actual object classes, supports pseudo-class 'a' for + discovered artifacts and 'u' (or 'r', for "relics") for unique items */ int doclassdisco(void) { static NEARDATA const char prompt[] = "View discoveries for which sort of objects?", havent_discovered_any[] = "haven't discovered any %s yet.", - unique_items[] = "unique items", + unique_items[] = "unique items or relics", artifact_items[] = "artifacts"; winid tmpwin = WIN_ERR; menu_item *pick_list = 0; anything any; char *p, *s, c, oclass, menulet, allclasses[MAXOCLASSES], - discosyms[2 + MAXOCLASSES + 1], buf[BUFSZ], + discosyms[3 + MAXOCLASSES + 1], buf[BUFSZ], *sorted_lines[NUM_OBJECTS]; /* overkill */ int i, ct, dis, xtras, sorted_ct; boolean traditional, alphabetized, lootsort; @@ -884,14 +887,20 @@ doclassdisco(void) any = cg.zeroany; menulet = 'a'; - /* check whether we've discovered any unique objects */ + /* check whether we've discovered any unique objects (primarily the + invocation items; the Guidebook calls unique items "relics" but the + Amulet of Yendor is unique too so we haven't made a blanket change + from 'u' to 'r') */ for (i = 0; i < SIZE(uniq_objs); i++) if (objects[uniq_objs[i]].oc_name_known) { Strcat(discosyms, "u"); if (!traditional) { any.a_int = 'u'; - add_menu(tmpwin, &nul_glyphinfo, &any, menulet++, - 0, ATR_NONE, clr, unique_items, MENU_ITEMFLAGS_NONE); + /* FIXME: having 'r' as an accelerator to provide an unseen + synonym works but doesn't make much sense since the main + selector is 'a' (implicit lootabc) rather than 'u' */ + add_menu(tmpwin, &nul_glyphinfo, &any, menulet++, 'r', + ATR_NONE, clr, unique_items, MENU_ITEMFLAGS_NONE); } break; } @@ -901,8 +910,8 @@ doclassdisco(void) Strcat(discosyms, "a"); if (!traditional) { any.a_int = 'a'; - add_menu(tmpwin, &nul_glyphinfo, &any, menulet++, - 0, ATR_NONE, clr, artifact_items, MENU_ITEMFLAGS_NONE); + add_menu(tmpwin, &nul_glyphinfo, &any, menulet++, 0, + ATR_NONE, clr, artifact_items, MENU_ITEMFLAGS_NONE); } } @@ -942,14 +951,14 @@ doclassdisco(void) /* have player choose a class */ c = '\0'; /* class not chosen yet */ if (traditional) { - char allclasses_plustwo[sizeof allclasses + 2]; + char allclasses_plustwo[sizeof allclasses + 3]; /* we'll prompt even if there's only one viable class; we add all nonviable classes as unseen acceptable choices so player can ask for discoveries of any class whether it has discoveries or not */ - Sprintf(allclasses_plustwo, "%s%c%c", allclasses, 'u', 'a'); + Sprintf(allclasses_plustwo, "%s%c%c%c", allclasses, 'a', 'u', 'r'); for (s = allclasses_plustwo, xtras = 0; *s; ++s) { - c = (*s == 'u' || *s == 'a') ? *s : def_oc_syms[(int) *s].sym; + c = strchr("aur", *s) ? *s : def_oc_syms[(int) *s].sym; if (!strchr(discosyms, c)) { if (!xtras++) (void) strkitten(discosyms, '\033'); @@ -988,6 +997,7 @@ doclassdisco(void) ct = 0; switch (c) { case 'u': + case 'r': putstr(tmpwin, iflags.menu_headings.attr, upstart(strcpy(buf, unique_items))); for (i = 0; i < SIZE(uniq_objs); i++) From b702be4449a53157f4abea5c75b7c838d7674499 Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 16 Feb 2026 14:01:59 -0800 Subject: [PATCH 277/442] more discoveries of unique items Show the invocation items in the unique items section of the discoveries list even when they're only flagged as encountered rather than fully discovered. The Amulet of Yendor is excluded; it has to be fully discovered to be shown in that section. --- src/o_init.c | 68 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 13 deletions(-) diff --git a/src/o_init.c b/src/o_init.c index 07b744d3e..26732a624 100644 --- a/src/o_init.c +++ b/src/o_init.c @@ -14,6 +14,7 @@ staticfn int QSORTCALLBACK discovered_cmp(const genericptr, const genericptr); staticfn char *sortloot_descr(int, char *); staticfn char *disco_typename(int); staticfn void disco_append_typename(char *, int); +staticfn void disco_fmt_uniq(int, char *outbuf) NONNULLARG2; staticfn void disco_output_sorted(winid, char **, int, boolean); staticfn char *oclass_to_name(char, char *); @@ -540,8 +541,11 @@ interesting_to_discover(int i) /* items that should stand out once they're known */ static const short uniq_objs[] = { - AMULET_OF_YENDOR, SPE_BOOK_OF_THE_DEAD, CANDELABRUM_OF_INVOCATION, + AMULET_OF_YENDOR, + /* same order as major oracularity; alphabetical when fully IDed */ BELL_OF_OPENING, + SPE_BOOK_OF_THE_DEAD, + CANDELABRUM_OF_INVOCATION, }; /* discoveries qsort comparison function */ @@ -708,6 +712,22 @@ disco_append_typename(char *buf, int dis) } } +/* minor fixup for Book of the Dead needed in more than one place */ +staticfn void +disco_fmt_uniq(int uidx, char *outbuf) +{ + Sprintf(outbuf, " %s", objects[uidx].oc_name_known + ? OBJ_NAME(objects[uidx]) + : OBJ_DESCR(objects[uidx])); + /* in the spellbooks section of main discoveries list, encountered + but not fully discovered Book of the Dead is shown as + "spellbook (papyrus)" like other encountered but not discovered books; + in the unique/relics section we want "papyrus spellbook" instead */ + if (!objects[uidx].oc_name_known + && objects[uidx].oc_class == SPBOOK_CLASS) + Strcat(outbuf, " spellbook"); +} + /* sort and output sorted_lines to window and free the lines */ staticfn void disco_output_sorted( @@ -739,7 +759,7 @@ dodiscovered(void) /* free after Robert Viduya */ char *s, *p, oclass, prev_class, classes[MAXOCLASSES], buf[BUFSZ], *sorted_lines[NUM_OBJECTS]; /* overkill */ - int i, dis, ct, uniq_ct, arti_ct, sorted_ct; + int i, dis, ct, uniq_ct, arti_ct, sorted_ct, uidx; long sortindx; // should be ptrdiff_t, but we don't require that exists boolean alphabetized, alphabyclass, lootsort; @@ -760,18 +780,27 @@ dodiscovered(void) /* free after Robert Viduya */ putstr(tmpwin, 0, buf); putstr(tmpwin, 0, ""); - /* gather "unique objects" into a pseudo-class; note that they'll - also be displayed individually within their regular class */ + /* + * FIXME? + * relics and artifacts don't obey player's sort order even though + * the header line states that they're shown in such-and-such order. + */ + + /* gather "unique objects", also called "relics", into a pseudo-class; + they'll also be displayed individually within their regular class */ uniq_ct = 0; - for (i = dis = 0; i < SIZE(uniq_objs); i++) - if (objects[uniq_objs[i]].oc_name_known) { + for (i = dis = 0; i < SIZE(uniq_objs); i++) { + uidx = uniq_objs[i]; + if (objects[uidx].oc_name_known + || (objects[uidx].oc_encountered && uidx != AMULET_OF_YENDOR)) { if (!dis++) putstr(tmpwin, iflags.menu_headings.attr, "Unique items or Relics"); ++uniq_ct; - Sprintf(buf, " %s", OBJ_NAME(objects[uniq_objs[i]])); + disco_fmt_uniq(uidx, buf); putstr(tmpwin, 0, buf); } + } /* display any known artifacts as another pseudo-class */ arti_ct = disp_artifact_discoveries(tmpwin); @@ -863,7 +892,7 @@ doclassdisco(void) char *p, *s, c, oclass, menulet, allclasses[MAXOCLASSES], discosyms[3 + MAXOCLASSES + 1], buf[BUFSZ], *sorted_lines[NUM_OBJECTS]; /* overkill */ - int i, ct, dis, xtras, sorted_ct; + int i, ct, dis, xtras, sorted_ct, uidx; boolean traditional, alphabetized, lootsort; int clr = NO_COLOR; @@ -887,12 +916,20 @@ doclassdisco(void) any = cg.zeroany; menulet = 'a'; + /* + * FIXME? + * relics and artifacts don't obey player's sort order even though + * the header line states that they're shown in such-and-such order. + */ + /* check whether we've discovered any unique objects (primarily the invocation items; the Guidebook calls unique items "relics" but the Amulet of Yendor is unique too so we haven't made a blanket change from 'u' to 'r') */ - for (i = 0; i < SIZE(uniq_objs); i++) - if (objects[uniq_objs[i]].oc_name_known) { + for (i = 0; i < SIZE(uniq_objs); i++) { + uidx = uniq_objs[i]; + if (objects[uidx].oc_name_known + || (objects[uidx].oc_encountered && uidx != AMULET_OF_YENDOR)) { Strcat(discosyms, "u"); if (!traditional) { any.a_int = 'u'; @@ -904,6 +941,7 @@ doclassdisco(void) } break; } + } /* check whether we've discovered any artifacts */ if (disp_artifact_discoveries(WIN_ERR) > 0) { @@ -1000,12 +1038,16 @@ doclassdisco(void) case 'r': putstr(tmpwin, iflags.menu_headings.attr, upstart(strcpy(buf, unique_items))); - for (i = 0; i < SIZE(uniq_objs); i++) - if (objects[uniq_objs[i]].oc_name_known) { + for (i = 0; i < SIZE(uniq_objs); i++) { + uidx = uniq_objs[i]; + if (objects[uidx].oc_name_known + || (objects[uidx].oc_encountered + && uidx != AMULET_OF_YENDOR)) { ++ct; - Sprintf(buf, " %s", OBJ_NAME(objects[uniq_objs[i]])); + disco_fmt_uniq(uidx, buf); putstr(tmpwin, 0, buf); } + } if (!ct) You(havent_discovered_any, unique_items); break; From 0d2752d05dfed3356bfdd4977b9c8cde39a0d2ea Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 16 Feb 2026 19:51:04 -0800 Subject: [PATCH 278/442] more cursed potion of invisibility Give feedback if a visible monster drinks a potion of invisibility that happens to be cursed so monster fails to become invisible, or if hero hits a visible monster with a cursed potion of invisibility and it fails to become invisible. --- src/muse.c | 7 +++++++ src/potion.c | 3 +++ 2 files changed, 10 insertions(+) diff --git a/src/muse.c b/src/muse.c index 8c95e00ec..aa0474e4e 100644 --- a/src/muse.c +++ b/src/muse.c @@ -2439,6 +2439,13 @@ use_misc(struct monst *mtmp) } if (oseen) makeknown(otmp->otyp); + } else if (vismon && !mtmp->minvis) { + /* cursed potion; mon tried to make itself invisible but failed */ + pline("%s briefly seems to be transparent.", Monnam(mtmp)); + /* we could call map_invisible() before the pline(), then + newsym() after; unseen monster glyph would be visible during + the pline, but hero would forget any remembered object under + the monster */ } else if (!vismon && canseemon(mtmp)) { /* cursed potion; this won't happen because a monster will only drink a potion of invisibility when not already invisible */ diff --git a/src/potion.c b/src/potion.c index 2ea79fb44..1758d16b8 100644 --- a/src/potion.c +++ b/src/potion.c @@ -1785,6 +1785,9 @@ potionhit(struct monst *mon, struct obj *obj, int how) if (sawit && !canspotmon(mon)) { if (cansee(mon->mx, mon->my)) map_invisible(mon->mx, mon->my); + } else if (sawit && cursed_potion) { + pline("%s briefly seems to be transparent.", Monnam(mon)); + /* see use_misc(muse.c) for comment about map_invisible() */ } else if (!sawit && canspotmon(mon)) { /* if an invisible mon glyph was present, mon_set_minvis()'s newsym() has gotten rid of it */ From 06012e7127c1c3b132e1942dcc5c55a5ec11890c Mon Sep 17 00:00:00 2001 From: Greg Kennedy Date: Sun, 30 Mar 2025 12:25:02 -0500 Subject: [PATCH 279/442] Potion / spell of restore ability resets stat abuse as well --- src/potion.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/potion.c b/src/potion.c index 1758d16b8..10fd55118 100644 --- a/src/potion.c +++ b/src/potion.c @@ -667,6 +667,9 @@ peffect_restore_ability(struct obj *otmp) WEAK or worse, but that's handled via ATEMP(A_STR) now */ if (ABASE(i) < lim) { ABASE(i) = lim; + /* reset stat abuse (but not exercise) to 0 as well */ + AEXE(i) = max(AEXE(i), 0); + disp.botl = TRUE; /* only first found if not blessed */ if (!otmp->blessed) From 822396c2e0edd158a2c2686fb4e2b6553ae1c1de Mon Sep 17 00:00:00 2001 From: Greg Kennedy Date: Sun, 30 Mar 2025 13:46:27 -0500 Subject: [PATCH 280/442] Attribute adjustment resets abuse / exercise counter --- src/attrib.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/attrib.c b/src/attrib.c index 405cbf7da..23e8117c7 100644 --- a/src/attrib.c +++ b/src/attrib.c @@ -187,6 +187,9 @@ adjattrib( return FALSE; } + /* Any successful change also resets abuse / exercise level */ + AEXE(ndx) = 0; + disp.botl = TRUE; if (msgflg <= 0) You_feel("%s%s!", (incr > 1 || incr < -1) ? "very " : "", attrstr); From e19f4552e906a45133bbc7f0603f168c7b80f921 Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 17 Feb 2026 14:56:52 -0800 Subject: [PATCH 281/442] pull request #1403 - reset stat abuse/exercise Pull request from greg-kennedy: when a characteristic is repaired via restore ability, set exercise and abuse for that characteristic to 0. Closes #1403 --- doc/fixes3-7-0.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index ebe99a2bb..37e360600 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -3097,6 +3097,8 @@ applied several source comment spelling or grammar fixes that were linked to an old rec.games.roguelike.nethack post from May 2006 by Arthur J. O'Dwyer modernize code of LaTeX Guidebook (pr #1473 by Daniel Clow) +restore ability for a characteristic also resets exercise/abuse for that + characteristic (pr #1403 by greg-kennedy) Code Cleanup and Reorganization From 398681fd1840328e40aaa7357b899c3498c2a296 Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 20 Feb 2026 19:29:55 -0800 Subject: [PATCH 282/442] fix? #S15277 - Engulfer gets placed over itself Bug report was that engulfer killed hero who was life-saved and expelled onto a rolling boulder trap which resulted in the death of a bystanding monster. That triggered an impossible about placing the engulfer onto the map at an already occupied spot (containing the engulfer itself) while dealing with the dead bystander. This removes the code in mon_leaving_level() that was putting the engulfer on the map. I'm no longer able to reproduce the problem it was intended to solve, and if that problem (message delivery about the swallow attack delivered with the engulfer missing from the map) exists, it would be less severe than the impossible feedback. I didn't attempt to reproduce the actual reported problem since the code removal should make it moot. Once again, the bug report via the web contact form got misclassified as spam. --- src/mon.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/mon.c b/src/mon.c index 4488bd8fc..0900ebe22 100644 --- a/src/mon.c +++ b/src/mon.c @@ -2696,20 +2696,6 @@ mon_leaving_level(struct monst *mon) #endif } if (onmap) { - /* gulpmm() tries to deal with this, but without this extra - place_monster() the messages for exploding engulfed gas spore - are delivered without the engulfer being shown on the map */ - if (gm.mswallower && gm.mswallower != mon) { - if (gm.mswallower != &gy.youmonst) { - place_monster(gm.mswallower, - gm.mswallower->mx, gm.mswallower->my); - } else { - u_on_newpos(u.ux, u.uy); - if (canspotself()) - display_self(); - } - } - mon->mundetected = 0; /* for migration; doesn't matter for death */ /* unhide mimic in case its shape has been blocking line of sight or it is accompanying the hero to another level */ @@ -3441,6 +3427,7 @@ unstuck(struct monst *mtmp) set_ustuck((struct monst *) 0); if (swallowed) { + gm.mswallower = (struct monst *) 0; u.ux = mtmp->mx; u.uy = mtmp->my; if (Punished && uchain->where != OBJ_FLOOR) From ef6e2a9624759a90506b4a9773041c2ea5d2f8b5 Mon Sep 17 00:00:00 2001 From: PatR Date: Wed, 25 Feb 2026 18:30:11 -0800 Subject: [PATCH 283/442] fix #15306b - vision blocking sanity check Update vision affected by invisible mimics if polymorphing hero gains or loses See_invisibls. Avoids triggering a sanity_check impossisble if an invisible mimic is mimicking a boulder. --- src/polyself.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/polyself.c b/src/polyself.c index a990a4d5f..973cc053b 100644 --- a/src/polyself.c +++ b/src/polyself.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 polyself.c $NHDT-Date: 1740534595 2025/02/25 17:49:55 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.223 $ */ +/* NetHack 3.7 polyself.c $NHDT-Date: 1772101811 2026/02/26 02:30:11 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.227 $ */ /* Copyright (C) 1987, 1988, 1989 by Ken Arromdee */ /* NetHack may be freely redistributed. See license for details. */ @@ -201,7 +201,8 @@ polyman(const char *fmt, const char *arg) { boolean sticking = (sticks(gy.youmonst.data) && u.ustuck && !u.uswallow), was_mimicking = (U_AP_TYPE != M_AP_NOTHING); - boolean was_blind = !!Blind; + boolean was_blind = !!Blind, + had_see_invis = !!See_invisible; if (Upolyd) { u.acurr = u.macurr; /* restore old attribs */ @@ -245,6 +246,9 @@ polyman(const char *fmt, const char *arg) done(GENOCIDED); } + if (!!See_invisible ^ had_see_invis) + set_mimic_blocking(); /* See_invisible just toggled */ + if (u.twoweap && !could_twoweap(gy.youmonst.data)) untwoweapon(); From 7261c848c7270f1a61f82a573436c6cb7b6cb4f6 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 1 Mar 2026 09:25:55 -0500 Subject: [PATCH 284/442] update tested versions of Visual Studio 2026-03-01 --- 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 074a0c15f..d89f92d69 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.26 -# - Microsoft Visual Studio Community 2026 v 18.3.0 +# - Microsoft Visual Studio Community 2022 v 17.14.27 +# - Microsoft Visual Studio Community 2026 v 18.3.2 # #============================================================================== # This is used for building two distinct executables of NetHack: @@ -1189,7 +1189,7 @@ rc=Rc.exe # # Recently tested versions: TESTEDVS2022 = 14.44.35222.0 -TESTEDVS2026 = 14.50.35724.0 +TESTEDVS2026 = 14.50.35725.0 VS20261ST = 1450000000 VS2026CUR = $(TESTEDVS2026:.=) From 33a6a9a7651521d7adc96c8bdb89fb1aa80e60c6 Mon Sep 17 00:00:00 2001 From: nhkeni Date: Wed, 4 Mar 2026 16:52:04 -0500 Subject: [PATCH 285/442] add optionally-built file nhlua to .gitignore --- util/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/util/.gitignore b/util/.gitignore index a16d5c640..3e92fc8a5 100644 --- a/util/.gitignore +++ b/util/.gitignore @@ -35,3 +35,4 @@ sf.tags sfdata.c sfctool sftags +nhlua From 98da3a3db6c1c4506d2f56385f7aad627197f4ae Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 5 Mar 2026 12:57:20 -0800 Subject: [PATCH 286/442] fix drawbridge + place_monster impossible Reported via email direct to devteam. If a monster killed by a drawbridge survives via life-saving and a second monster also survives (in the reported case, it was a xorn who will always survive bridge destruction), the second monster will be placed at the same spot. That triggers an impossible "placing <2nd mon> over <1st mon>, at , mstates 0 0 at Dlvl N?". If the first monster survives due to pass_walls rather than due to life-saving and a second survives too, one of them should end up being moved to a different spot and not trigger the impossible (not verified via testing). When a monster survives via life-saving, kill it again even if there is no second monster involved. --- src/dbridge.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/dbridge.c b/src/dbridge.c index 5e739d4fa..72d39be82 100644 --- a/src/dbridge.c +++ b/src/dbridge.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 dbridge.c $NHDT-Date: 1702349063 2023/12/12 02:44:23 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.62 $ */ +/* NetHack 3.7 dbridge.c $NHDT-Date: 1772771734 2026/03/05 20:35:34 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.70 $ */ /* Copyright (c) 1989 by Jean-Christophe Collet */ /* NetHack may be freely redistributed. See license for details. */ @@ -399,7 +399,9 @@ e_survives_at(struct entity *etmp, coordxy x, coordxy y) } staticfn void -e_died(struct entity *etmp, int xkill_flags, int how) +e_died( + struct entity *etmp, + int xkill_flags, int how) { if (is_u(etmp)) { if (how == DROWNING) { @@ -444,6 +446,26 @@ e_died(struct entity *etmp, int xkill_flags, int how) mk_message(xkill_flags), mk_corpse(xkill_flags)); else /* you caused it */ xkilled(etmp->emon, xkill_flags); + + /* if etmp gets life-saved, kill it again; otherwise we might end up + trying to place another monster (probably a xorn) on same spot */ + if (!DEADMONSTER(etmp->emon)) { + int seeit = canspotmon(etmp->emon); + + xkill_flags |= XKILL_NOMSG | XKILL_NOCONDUCT; + if (svc.context.mon_moving) + monkilled(etmp->emon, "", mk_corpse(xkill_flags)); + else /* you caused it */ + xkilled(etmp->emon, xkill_flags); + + if (DEADMONSTER(etmp->emon)) { + if (seeit) + pline("Unfortunately for %s, %s is still crushed.", + mon_nam(etmp->emon), mhe(etmp->emon)); + } else { + ; /* FIXME: still not dead? What should we do now? */ + } + } etmp->edata = (struct permonst *) 0; /* dead long worm handling */ From c47990d75046420d26d058b4c9d74ed3218e554c Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 6 Mar 2026 13:43:51 -0800 Subject: [PATCH 287/442] fixes3-7-0.txt typo --- doc/fixes3-7-0.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 37e360600..c9803c5a2 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -2208,7 +2208,7 @@ when reading a T-shirt or apron, add trailing period to the slogan unless when reading an engraving, suppress the addition of a trailing period if the text already has one cursed magic whistle could teleport hero on no-teleport levels -give a brief explanation when receving a wish for acquiring the Amulet +give a brief explanation when receiving a wish for acquiring the Amulet Fixes to 3.7.0-x Platform and/or Interface Problems Exposed Via git Repository From be0bed8f3c0e95b454c2d0043f361b87b06c45e8 Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 8 Mar 2026 03:14:29 -0700 Subject: [PATCH 288/442] rephrase comment Triviality: add a couple of commas so that a comment won't be misread. --- src/do_wear.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/do_wear.c b/src/do_wear.c index bbbf5fbfd..7a65bbf07 100644 --- a/src/do_wear.c +++ b/src/do_wear.c @@ -1851,7 +1851,7 @@ dotakeoff(void) /* 'i' or 'I[' followed by and then 'T'; plain dotakeoff() would not give any feedback when picking suit - covered by cloak or shirt covered by suit and/or cloak due to the + covered by cloak, or shirt covered by suit and/or cloak, due to the default behavior of equip_ok() (skipping inaccessible items) */ int ia_dotakeoff(void) From d6bff63025df7013a00f0673233bdc26cd4af44d Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 8 Mar 2026 03:17:42 -0700 Subject: [PATCH 289/442] X11 comment thinko Fix a phrasing mistake that was introduced when unmatched apostrophies were removed from the sample NetHack.ad file. --- win/X11/NetHack.ad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/win/X11/NetHack.ad b/win/X11/NetHack.ad index 4c5f9b191..c10d1360f 100644 --- a/win/X11/NetHack.ad +++ b/win/X11/NetHack.ad @@ -117,7 +117,7 @@ NetHack*message*translations: : input() ! Specify the number of rows and columns of the map window. The default ! is the standard 80x21 window. Note: this _does_not_ change the size of -! map levels, only what you see of them. +! level maps, only what you see of them. !NetHack*map*rows: 21 !NetHack*map*columns: 80 From 226157004bb9d095b256af73aa8a52bdd5ba37b3 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Wed, 11 Mar 2026 17:35:08 +0200 Subject: [PATCH 290/442] Make migratemons ignore debug_mongen Same as wizgenesis, migratemons should create monsters even when random monster generation is disabled. --- src/wizcmds.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/wizcmds.c b/src/wizcmds.c index 8c14b98f7..31cb338cc 100644 --- a/src/wizcmds.c +++ b/src/wizcmds.c @@ -1832,6 +1832,7 @@ wiz_migrate_mons(void) struct permonst *ptr; struct monst *mtmp; boolean use_random_mon = TRUE; + boolean mongen_saved = iflags.debug_mongen; #endif d_level tolevel; @@ -1864,6 +1865,7 @@ wiz_migrate_mons(void) else if (mcount > ((COLNO - 1) * ROWNO)) mcount = (COLNO - 1) * ROWNO; + iflags.debug_mongen = FALSE; while (mcount > 0) { if (use_random_mon) { ptr = rndmonst(); @@ -1876,6 +1878,7 @@ wiz_migrate_mons(void) (coord *) 0); mcount--; } + iflags.debug_mongen = mongen_saved; #endif /* DEBUG_MIGRATING_MONS */ return ECMD_OK; } From 7cff1696b757a747879b24eca3e7be3c4bdaed54 Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 12 Mar 2026 09:28:37 -0400 Subject: [PATCH 291/442] update tested versions of Visual Studio 2026-03-12 --- 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 d89f92d69..630575be5 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.27 -# - Microsoft Visual Studio Community 2026 v 18.3.2 +# - Microsoft Visual Studio Community 2022 v 17.14.28 +# - Microsoft Visual Studio Community 2026 v 18.4.0 # #============================================================================== # This is used for building two distinct executables of NetHack: @@ -1189,7 +1189,7 @@ rc=Rc.exe # # Recently tested versions: TESTEDVS2022 = 14.44.35222.0 -TESTEDVS2026 = 14.50.35725.0 +TESTEDVS2026 = 14.50.35726.0 VS20261ST = 1450000000 VS2026CUR = $(TESTEDVS2026:.=) From c30eea477da4d065c172040125b3125d2f5fd985 Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 12 Mar 2026 13:12:42 -0700 Subject: [PATCH 292/442] Shroedinger's dead cat Give experience to the hero if opening Shroedinger's Box reveals a cat corpse rather than releasing a live cat. It feels a bit odd to assess a bonus rather than a penalty. --- doc/fixes3-7-0.txt | 1 + src/pickup.c | 15 +++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index c9803c5a2..54dcaa9fb 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1581,6 +1581,7 @@ the "bustling town" minetown has more peacefuls healers may get tiny damage increase when attacking with knives allow rogues to also backstab sleeping or paralyzed monsters rogues cannot backstab monsters that have no backside +give experience if opening Schroedinger's Box causes death of the cat inside Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/pickup.c b/src/pickup.c index bd1fe41db..73c513135 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 pickup.c $NHDT-Date: 1720074481 2024/07/04 06:28:01 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.374 $ */ +/* NetHack 3.7 pickup.c $NHDT-Date: 1773373633 2026/03/12 19:47:13 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.386 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2860,6 +2860,9 @@ observe_quantum_cat(struct obj *box, boolean makecat, boolean givemsg) } } else { box->spe = 0; /* now an ordinary box (with a cat corpse inside) */ + if (givemsg) + pline_The("%s inside the box is dead!", + Hallucination ? rndmonnam((char *) 0) : "housecat"); if (deadcat) { /* set_corpsenm() will start the rot timer that was removed when makemon() created SchroedingersBox; start it from @@ -2867,10 +2870,14 @@ observe_quantum_cat(struct obj *box, boolean makecat, boolean givemsg) deadcat->age = svm.moves; set_corpsenm(deadcat, PM_HOUSECAT); deadcat = oname(deadcat, sc, ONAME_NO_FLAGS); + + if (!svc.context.mon_moving) { + /* give experience points for the death of the cat since + that has been finalized by the hero opening the box */ + more_experienced(20, 10); /* 20:current exp; 10:score bonus */ + newexplevel(); + } } - if (givemsg) - pline_The("%s inside the box is dead!", - Hallucination ? rndmonnam((char *) 0) : "housecat"); } nhUse(deadcat); return; From b70da87d3e51b98d1c45bb199364628f3884dd58 Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 12 Mar 2026 13:17:11 -0700 Subject: [PATCH 293/442] 'onefile' tidbit --- src/light.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/light.c b/src/light.c index 574665fa9..93f7ea50a 100644 --- a/src/light.c +++ b/src/light.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 light.c $NHDT-Date: 1726609514 2024/09/17 21:45:14 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.75 $ */ +/* NetHack 3.7 light.c $NHDT-Date: 1773375430 2026/03/12 20:17:10 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.82 $ */ /* Copyright (c) Dean Luick, 1994 */ /* NetHack may be freely redistributed. See license for details. */ @@ -974,9 +974,12 @@ wiz_light_sources(void) return ECMD_OK; } #endif /* !SFCTOOL */ + /* for 'onefile' processing where end of this file isn't necessarily the end of the source code seen by the compiler */ #undef LSF_SHOW #undef LSF_NEEDS_FIXUP +#undef LSF_IS_PROBLEMATIC #undef mon_is_local + /*light.c*/ From 619de1ec0e8f40d33a6a61b5ada14eb69a04a317 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Mon, 16 Mar 2026 11:20:34 +0200 Subject: [PATCH 294/442] Make monster destroy armor -spell erode armor first Instead of outright destroying the armor, the spell will instead first erode the armor. The spell hits 2-4 times, so if it hits the same armor 4 times, it will get destroyed. This does not hit erodeproof armor. Also change the scroll of destroy armor, so that blessed one will destroy a cursed armor, if hero is only wearing that. --- doc/fixes3-7-0.txt | 5 +++- include/extern.h | 3 ++- include/obj.h | 1 + src/do_wear.c | 65 ++++++++++++++++++++++++++++++++++++++++++++-- src/mcastu.c | 2 +- src/pray.c | 8 +++--- src/read.c | 58 +++++++++++++++++++++++++++++++++-------- src/zap.c | 10 +++---- 8 files changed, 127 insertions(+), 25 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 54dcaa9fb..be8ca2259 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -2890,12 +2890,15 @@ enlightenment/attribute disclosure for saving-grace: include a line for have tourists gain experience by "taking photos" of new creatures, and going to new dungeon levels healers gain experience by healing pets -blessed scroll of destroy armor asks which armor to destroy +blessed scroll of destroy armor asks which armor to destroy if wearing more + than one armor; otherwise it'll destroy a cursed armor, or erodes + random armor archeologists' fedora is lucky telepathic hero can discern which particular monster just read a scroll add "make fetch-docs" to download pre-formatted documentation monsters will use throw-and-return weapons such as an aklys archeologists can identify scrolls by deciphering their labels +monster destroy armor -spell first erodes armor Platform- and/or Interface-Specific New Features diff --git a/include/extern.h b/include/extern.h index 2593f9362..0af1dfda7 100644 --- a/include/extern.h +++ b/include/extern.h @@ -766,7 +766,8 @@ extern struct obj *unchanger(void); extern void reset_remarm(void); extern int doddoremarm(void); extern int remarm_swapwep(void); -extern int destroy_arm(struct obj *); +extern int disintegrate_arm(struct obj *); +extern int destroy_arm(void); extern void adj_abon(struct obj *, schar) NONNULLARG1; extern boolean inaccessible_equipment(struct obj *, const char *, boolean); extern int any_worn_armor_ok(struct obj *); diff --git a/include/obj.h b/include/obj.h index 431094918..d9ef83d21 100644 --- a/include/obj.h +++ b/include/obj.h @@ -451,6 +451,7 @@ struct obj { #define BURIED_TOO 0x2 /* object erosion types */ +#define ERODE_NONE -1 #define ERODE_BURN 0 #define ERODE_RUST 1 #define ERODE_ROT 2 diff --git a/src/do_wear.c b/src/do_wear.c index 7a65bbf07..025b097ef 100644 --- a/src/do_wear.c +++ b/src/do_wear.c @@ -52,6 +52,7 @@ staticfn int takeoff_ok(struct obj *); /* maybe_destroy_armor() may return NULL */ staticfn struct obj *maybe_destroy_armor(struct obj *, struct obj *, boolean *) NONNULLARG3; +staticfn int obj_erode_type(struct obj *) NONNULLARG1; staticfn boolean better_not_take_that_off(struct obj *) NONNULLARG1; /* plural "fingers" or optionally "gloves" */ @@ -3191,9 +3192,9 @@ maybe_destroy_armor(struct obj *armor, struct obj *atmp, boolean *resisted) return (struct obj *) 0; } -/* hit by destroy armor scroll/black dragon breath/monster spell */ +/* hit by destroy armor scroll/black dragon breath */ int -destroy_arm(struct obj *atmp) +disintegrate_arm(struct obj *atmp) { struct obj *otmp = (struct obj *) 0; boolean losing_gloves = FALSE, resisted = FALSE, @@ -3250,6 +3251,66 @@ destroy_arm(struct obj *atmp) return 1; } +/* return ERODE_foo erosion type which can apply to object */ +staticfn int +obj_erode_type(struct obj *otmp) +{ + if (is_flammable(otmp)) + return ERODE_BURN; + else if (is_rustprone(otmp)) + return ERODE_RUST; + else if (is_crackable(otmp)) + return ERODE_CRACK; + else if (is_rottable(otmp)) + return ERODE_ROT; + else if (is_corrodeable(otmp)) + return ERODE_CORRODE; + return ERODE_NONE; +} + +/* erode a number of worn armor(s). + if the armor is hit when max eroded, destroys it. */ +int +destroy_arm(void) +{ + struct obj *armors[7] = { NULL }; + struct obj *otmp; + int i, idx = 0, hits = rn2(4) + 1; + int ret = 0; + + /* gather worn armor; include non-erodeable ones */ + if (uarm) armors[idx++] = uarm; + if (uarmc) armors[idx++] = uarmc; + if (uarmh) armors[idx++] = uarmh; + if (uarms) armors[idx++] = uarms; + if (uarmg) armors[idx++] = uarmg; + if (uarmf) armors[idx++] = uarmf; + if (uarmu) armors[idx++] = uarmu; + if (!idx) + return 0; + + for (i = 0; i < hits; i++) { + otmp = armors[rn2(idx)]; + + if (erosion_matters(otmp) && is_damageable(otmp) && !otmp->oerodeproof) { + int erosion = obj_erode_type(otmp); + + if (erosion != ERODE_NONE) { + int r = erode_obj(otmp, xname(otmp), erosion, EF_PAY|EF_DESTROY); + + if (r != ER_NOTHING) + ret = 1; + if (r == ER_DESTROYED) + break; + } + } + } + + if (ret) + stop_occupation(); + return ret; +} + void adj_abon(struct obj *otmp, schar delta) { diff --git a/src/mcastu.c b/src/mcastu.c index 29792429f..bffe7d790 100644 --- a/src/mcastu.c +++ b/src/mcastu.c @@ -530,7 +530,7 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum) shieldeff(u.ux, u.uy); monstseesu(M_SEEN_MAGR); pline("A field of force surrounds you!"); - } else if (!destroy_arm(some_armor(&gy.youmonst))) { + } else if (!destroy_arm()) { Your("skin itches."); } else { /* monsters only realize you aren't magic-protected if armor is diff --git a/src/pray.c b/src/pray.c index 281ce64e7..55e95e096 100644 --- a/src/pray.c +++ b/src/pray.c @@ -660,15 +660,15 @@ god_zaps_you(aligntyp resp_god) */ if (uarms && !(EReflecting & W_ARMS) && !(EDisint_resistance & W_ARMS)) - (void) destroy_arm(uarms); + (void) disintegrate_arm(uarms); if (uarmc && !(EReflecting & W_ARMC) && !(EDisint_resistance & W_ARMC)) - (void) destroy_arm(uarmc); + (void) disintegrate_arm(uarmc); if (uarm && !(EReflecting & W_ARM) && !(EDisint_resistance & W_ARM) && !uarmc) - (void) destroy_arm(uarm); + (void) disintegrate_arm(uarm); if (uarmu && !uarm && !uarmc) - (void) destroy_arm(uarmu); + (void) disintegrate_arm(uarmu); if (!Disint_resistance) { fry_by_god(resp_god, TRUE); monstunseesu(M_SEEN_DISINT); diff --git a/src/read.c b/src/read.c index 8b8f6bc79..2770ee4ed 100644 --- a/src/read.c +++ b/src/read.c @@ -22,6 +22,7 @@ staticfn int maybe_tame(struct monst *, struct obj *); staticfn boolean can_center_cloud(coordxy, coordxy); staticfn void display_stinking_cloud_positions(boolean); staticfn void seffect_enchant_armor(struct obj **); +staticfn boolean disintegrate_cursed_armor(void); staticfn void seffect_destroy_armor(struct obj **); staticfn void seffect_confuse_monster(struct obj **); staticfn void seffect_scare_monster(struct obj **); @@ -1281,6 +1282,29 @@ seffect_enchant_armor(struct obj **sobjp) Blind ? "again" : "unexpectedly"); } +/* destroy a random cursed armor worn by hero */ +staticfn boolean +disintegrate_cursed_armor(void) +{ + struct obj *armors[7] = { NULL }; + int idx = 0; + + if (uarm && uarm->cursed) armors[idx++] = uarm; + if (uarmc && uarmc->cursed) armors[idx++] = uarmc; + if (uarmh && uarmh->cursed) armors[idx++] = uarmh; + if (uarms && uarms->cursed) armors[idx++] = uarms; + if (uarmg && uarmg->cursed) armors[idx++] = uarmg; + if (uarmf && uarmf->cursed) armors[idx++] = uarmf; + if (uarmu && uarmu->cursed) armors[idx++] = uarmu; + if (!idx) + return FALSE; + + if (disintegrate_arm(armors[rn2(idx)])) + return TRUE; + + return FALSE; +} + staticfn void seffect_destroy_armor(struct obj **sobjp) { @@ -1310,7 +1334,21 @@ seffect_destroy_armor(struct obj **sobjp) otmp->oerodeproof = new_erodeproof ? 1 : 0; return; } - if (!scursed || !otmp || !otmp->cursed) { + + if (scursed) { + if (otmp && otmp->cursed) { + /* armor and scroll both cursed */ + pline("%s.", Yobjnam2(otmp, "vibrate")); + if (otmp->spe >= -6) { + otmp->spe += -1; + adj_abon(otmp, -1); + } + make_stunned((HStun & TIMEOUT) + (long) rn1(10, 10), TRUE); + } else if (disintegrate_arm(otmp)) { + gk.known = TRUE; + return; + } + } else { boolean gets_choice = (otmp && sobj && sobj->blessed && count_worn_armor() > 1); @@ -1324,9 +1362,14 @@ seffect_destroy_armor(struct obj **sobjp) /* check the return value, if user picked non-valid obj */ if (any_worn_armor_ok(atmp) == GETOBJ_SUGGEST) otmp = atmp; - } - - if (!destroy_arm(otmp)) { + if (disintegrate_arm(otmp)) { + gk.known = TRUE; + return; + } + } else if (sobj->blessed && disintegrate_cursed_armor()) { + gk.known = TRUE; + return; + } else if (!destroy_arm()) { strange_feeling(sobj, "Your skin itches."); *sobjp = 0; /* useup() in strange_feeling() */ exercise(A_STR, FALSE); @@ -1334,13 +1377,6 @@ seffect_destroy_armor(struct obj **sobjp) return; } else gk.known = TRUE; - } else { /* armor and scroll both cursed */ - pline("%s.", Yobjnam2(otmp, "vibrate")); - if (otmp->spe >= -6) { - otmp->spe += -1; - adj_abon(otmp, -1); - } - make_stunned((HStun & TIMEOUT) + (long) rn1(10, 10), TRUE); } } diff --git a/src/zap.c b/src/zap.c index ec49363f5..96dc3d44b 100644 --- a/src/zap.c +++ b/src/zap.c @@ -4462,21 +4462,21 @@ zhitu( monstunseesu(M_SEEN_DISINT); if (uarms) { /* destroy shield; other possessions are safe */ - (void) destroy_arm(uarms); + (void) disintegrate_arm(uarms); break; } else if (uarm) { /* destroy suit; if present, cloak goes too */ if (uarmc) - (void) destroy_arm(uarmc); - (void) destroy_arm(uarm); + (void) disintegrate_arm(uarmc); + (void) disintegrate_arm(uarm); break; } /* no shield or suit, you're dead; wipe out cloak and/or shirt in case of life-saving or bones */ if (uarmc) - (void) destroy_arm(uarmc); + (void) disintegrate_arm(uarmc); if (uarmu) - (void) destroy_arm(uarmu); + (void) disintegrate_arm(uarmu); } else if (nonliving(gy.youmonst.data) || is_demon(gy.youmonst.data)) { shieldeff(sx, sy); You("seem unaffected."); From 13e7d21b937db542a23392b4122c5eefe125ab0f Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Mon, 16 Mar 2026 15:36:23 +0200 Subject: [PATCH 295/442] Replace magic number with define --- src/mhitm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mhitm.c b/src/mhitm.c index 322b65b22..5e8bb928e 100644 --- a/src/mhitm.c +++ b/src/mhitm.c @@ -1259,7 +1259,7 @@ slept_monst(struct monst *mon) void rustm(struct monst *mdef, struct obj *obj) { - int dmgtyp = -1, chance = 1; + int dmgtyp = ERODE_NONE, chance = 1; if (!mdef || !obj) return; /* just in case */ @@ -1275,7 +1275,7 @@ rustm(struct monst *mdef, struct obj *obj) chance = 6; } - if (dmgtyp >= 0 && !rn2(chance)) + if (dmgtyp != ERODE_NONE && !rn2(chance)) (void) erode_obj(obj, (char *) 0, dmgtyp, EF_GREASE | EF_VERBOSE); } From b6df17f34252a2e3170acd2e42edacab10a6a5eb Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Tue, 17 Mar 2026 00:05:16 +0000 Subject: [PATCH 296/442] Iron shoes protect / partially protect against certain trap types --- doc/fixes3-7-0.txt | 1 + src/trap.c | 91 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 82 insertions(+), 10 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index be8ca2259..525eb0ac6 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -2899,6 +2899,7 @@ add "make fetch-docs" to download pre-formatted documentation monsters will use throw-and-return weapons such as an aklys archeologists can identify scrolls by deciphering their labels monster destroy armor -spell first erodes armor +iron shoes protect the wearer against certain floor-based traps Platform- and/or Interface-Specific New Features diff --git a/src/trap.c b/src/trap.c index 109c002ae..8444e9b30 100644 --- a/src/trap.c +++ b/src/trap.c @@ -16,6 +16,7 @@ staticfn boolean mu_maybe_destroy_web(struct monst *, boolean, struct trap *); staticfn struct obj *t_missile(int, struct trap *); staticfn boolean floor_trigger(int); staticfn boolean check_in_air(struct monst *, unsigned); +staticfn boolean wearing_iron_shoes(struct monst *); staticfn int trapeffect_arrow_trap(struct monst *, struct trap *, unsigned); staticfn int trapeffect_dart_trap(struct monst *, struct trap *, unsigned); staticfn int trapeffect_rocktrap(struct monst *, struct trap *, unsigned); @@ -1093,6 +1094,14 @@ check_in_air(struct monst *mtmp, unsigned trflags) || ((is_you ? Flying : is_flyer(mtmp->data)) && !plunged)); } +/* return TRUE if mtmp is wearing iron shoes */ +staticfn boolean +wearing_iron_shoes(struct monst *mtmp) +{ + struct obj *armf = which_armor(mtmp, W_ARMF); + return armf && armf->otyp == IRON_SHOES; +} + /* is trap ttmp harmless to monster mtmp? */ boolean m_harmless_trap(struct monst *mtmp, struct trap *ttmp) @@ -1501,10 +1510,14 @@ trapeffect_bear_trap( } else { pline("%s bear trap closes on your %s!", A_Your[trap->madeby_u], body_part(FOOT)); - set_wounded_legs(rn2(2) ? RIGHT_SIDE : LEFT_SIDE, rn1(10, 10)); if (u.umonnum == PM_OWLBEAR || u.umonnum == PM_BUGBEAR) You("howl in anger!"); - losehp(Maybe_Half_Phys(dmg), "bear trap", KILLED_BY_AN); + if (wearing_iron_shoes(mtmp)) + pline("%s protects your leg.", Yname2(uarmf)); + else { + set_wounded_legs(rn2(2) ? RIGHT_SIDE : LEFT_SIDE, rn1(10, 10)); + losehp(Maybe_Half_Phys(dmg), "bear trap", KILLED_BY_AN); + } } exercise(A_DEX, FALSE); } else { @@ -1535,7 +1548,7 @@ trapeffect_bear_trap( seetrap(trap); } } - if (mtmp->mtrapped) + if (mtmp->mtrapped && !wearing_iron_shoes(mtmp)) trapkilled = thitm(0, mtmp, (struct obj *) 0, d(2, 4), FALSE); return trapkilled ? Trap_Killed_Mon : mtmp->mtrapped @@ -1813,6 +1826,9 @@ trapeffect_pit( unsigned int trflags) { int ttype = trap->ttyp; + /* relevant_spikes is initially always true for spiked pits, but + set to false if the spikes are found to not be relevant */ + boolean relevant_spikes = ttype == SPIKED_PIT; if (mtmp == &gy.youmonst) { boolean plunged = (trflags & TOOKPLUNGE) != 0; @@ -1880,7 +1896,10 @@ trapeffect_pit( } else if (u.umonnum == PM_PIT_VIPER || u.umonnum == PM_PIT_FIEND) { pline("How pitiful. Isn't that the pits?"); } - if (ttype == SPIKED_PIT) { + if (relevant_spikes && wearing_iron_shoes(mtmp)) { + pline("%s protects you from the sharp iron spikes.", Yname2(uarmf)); + relevant_spikes = FALSE; + } else if (relevant_spikes) { const char *predicament = "on a set of sharp iron spikes"; if (u.usteed) { @@ -1898,7 +1917,7 @@ trapeffect_pit( */ set_utrap((unsigned) rn1(6, 2), TT_PIT); if (!steedintrap(trap, (struct obj *) 0)) { - if (ttype == SPIKED_PIT) { + if (relevant_spikes) { int oldumort = u.umortality; losehp(Maybe_Half_Phys(rnd(conj_pit ? 4 : adj_pit ? 6 : 10)), @@ -1977,8 +1996,9 @@ trapeffect_pit( seetrap(trap); } mselftouch(mtmp, "Falling, ", FALSE); + if (wearing_iron_shoes(mtmp)) relevant_spikes = FALSE; if (DEADMONSTER(mtmp) || thitm(0, mtmp, (struct obj *) 0, - rnd((ttype == PIT) ? 6 : 10), FALSE)) + rnd(relevant_spikes ? 10 : 6), FALSE)) trapkilled = TRUE; return trapkilled ? Trap_Killed_Mon : mtmp->mtrapped @@ -2303,6 +2323,24 @@ trapeffect_anti_magic( struct trap *trap, /* trap->ttyp == ANTI_MAGIC */ unsigned int trflags UNUSED) { + if (wearing_iron_shoes(mtmp)) { + struct obj *shoes = which_armor(mtmp, W_ARMF); + /* iron shoes protect against antimagic traps only if + positively enchanted; the trap drains the enchantment + rather than the wearer */ + if (shoes->spe > 0) { + /* no message if a monster does this, it isn't visible enough */ + if (mtmp == &gy.youmonst) { + seetrap(trap); + pline("A lethargic aura surrounds %s.", yname(shoes)); + costly_alteration(shoes, COST_DECHNT); + } + shoes->spe -= 1; + update_inventory(); + return Trap_Effect_Finished; + } + } + if (mtmp == &gy.youmonst) { int drain, halfd; boolean exclaim_it = FALSE; @@ -2415,6 +2453,10 @@ trapeffect_poly_trap( struct trap *trap, unsigned int trflags) { + static int possible_boots[] = { + ELVEN_BOOTS, KICKING_BOOTS, FUMBLE_BOOTS, LEVITATION_BOOTS, + JUMPING_BOOTS, SPEED_BOOTS, WATER_WALKING_BOOTS }; + if (mtmp == &gy.youmonst) { boolean viasitting = (trflags & VIASITTING) != 0; int steed_article = ARTICLE_THE; @@ -2435,7 +2477,14 @@ trapeffect_poly_trap( else Sprintf(verbbuf, "%s onto", u_locomotion("step")); You("%s a polymorph trap!", verbbuf); - if (Antimagic || Unchanging) { + if (wearing_iron_shoes(mtmp)) { + deltrap(trap); + pline("%s warps strangely.", Yname2(uarmf)); + poly_obj(uarmf, ROLL_FROM(possible_boots)); + update_inventory(); + if (uarmf) + prinv(NULL, uarmf, 0); + } else if (Antimagic || Unchanging) { shieldeff(u.ux, u.uy); You_feel("momentarily different."); /* Trap did nothing; don't remove it --KAA */ @@ -2449,7 +2498,22 @@ trapeffect_poly_trap( } else { boolean in_sight = canseemon(mtmp) || (mtmp == u.usteed); - if (resists_magm(mtmp)) { + if (wearing_iron_shoes(mtmp)) { + /* remove and readd the shoes to forcibly unwear them */ + struct obj *shoes = which_armor(mtmp, W_ARMF); + extract_from_minvent(mtmp, shoes, TRUE, TRUE); + if (mpickobj(mtmp, shoes)) { + impossible("re-equipping iron shoes destroyed them?"); + return Trap_Effect_Finished; + } + shoes = poly_obj(shoes, ROLL_FROM(possible_boots)); + /* now equip them again */ + if (shoes) { + mtmp->misc_worn_check |= W_ARMF; + shoes->owornmask = W_ARMF; + update_mon_extrinsics(mtmp, shoes, TRUE, TRUE); + } + } else if (resists_magm(mtmp)) { shieldeff_mon(mtmp); } else if (!resist(mtmp, WAND_CLASS, 0, NOTELL)) { (void) newcham(mtmp, (struct permonst *) 0, NC_SHOW_MSG); @@ -2466,6 +2530,13 @@ trapeffect_landmine( struct trap *trap, unsigned int trflags) { + int damage = rnd(16); + /* iron shoes protect against much of the damage from the + explosion, but you still take some damage (and wound legs) + because they can't fully block the blast */ + if (wearing_iron_shoes(mtmp)) + damage = (damage + 3) / 4; + if (mtmp == &gy.youmonst) { boolean already_seen = trap->tseen; boolean forcetrap = ((trflags & FORCETRAP) != 0 @@ -2515,7 +2586,7 @@ trapeffect_landmine( blow_up_landmine() will remove pit afterwards if inappropriate */ trap->ttyp = PIT; trap->madeby_u = FALSE; - losehp(Maybe_Half_Phys(rnd(16)), "land mine", KILLED_BY_AN); + losehp(Maybe_Half_Phys(damage), "land mine", KILLED_BY_AN); blow_up_landmine(trap); if (steed_mid && saddle && !u.usteed) (void) keep_saddle_with_steedcorpse(steed_mid, fobj, saddle); @@ -2564,7 +2635,7 @@ trapeffect_landmine( /* explosion might have destroyed a drawbridge; don't dish out more damage if monster is already dead */ if (DEADMONSTER(mtmp) - || thitm(0, mtmp, (struct obj *) 0, rnd(16), FALSE)) { + || thitm(0, mtmp, (struct obj *) 0, damage, FALSE)) { trapkilled = TRUE; } else { /* monsters recursively fall into new pit */ From 66e7123678fe53bfaa8066146af464e249d6f419 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Tue, 17 Mar 2026 13:31:01 +0200 Subject: [PATCH 297/442] Categorize more commands as general commands "general commands" tend to be ones which take no game time, or are non-diegetic. --- src/cmd.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/cmd.c b/src/cmd.c index e069c42ff..0969f0222 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -1652,21 +1652,21 @@ struct ext_func_tab extcmdlist[] = { doextlist, IFBURIED | AUTOCOMPLETE | GENERALCMD | CMD_M_PREFIX, NULL }, { M('a'), "adjust", "adjust inventory letters", - doorganize, IFBURIED | AUTOCOMPLETE, NULL }, + doorganize, IFBURIED | AUTOCOMPLETE | GENERALCMD, NULL }, { M('A'), "annotate", "name current level", donamelevel, IFBURIED | AUTOCOMPLETE | GENERALCMD, NULL }, { 'a', "apply", "apply (use) a tool (pick-axe, key, lamp...)", doapply, CMD_M_PREFIX, NULL }, { C('x'), "attributes", "show your attributes", - doattributes, IFBURIED, NULL }, + doattributes, IFBURIED | GENERALCMD, NULL }, { '@', "autopickup", "toggle the 'autopickup' option on/off", - dotogglepickup, IFBURIED, NULL }, + dotogglepickup, IFBURIED | GENERALCMD, NULL }, #ifdef CRASHREPORT { '\0', "bugreport", "file a bug report", dobugreport, GENERALCMD | NOFUZZERCMD, NULL }, #endif { 'C', "call", "name a monster, specific object, or type of object", - docallcmd, IFBURIED, NULL }, + docallcmd, IFBURIED | GENERALCMD, NULL }, { 'Z', "cast", "zap (cast) a spell", docast, IFBURIED, NULL }, { M('c'), "chat", "talk to someone", @@ -1695,7 +1695,7 @@ struct ext_func_tab extcmdlist[] = { { 'E', "engrave", "engrave writing on the floor", doengrave, 0, NULL }, { M('e'), "enhance", "advance or check weapon and spell skills", - enhance_weapon_skill, IFBURIED | AUTOCOMPLETE, NULL }, + enhance_weapon_skill, IFBURIED | AUTOCOMPLETE | GENERALCMD, NULL }, /* #exploremode should be flagged AUTOCOMPETE but that would negatively impact frequently used #enhance by making #e become ambiguous */ { M('X'), "exploremode", "enter explore (discovery) mode", @@ -1719,9 +1719,9 @@ struct ext_func_tab extcmdlist[] = { { 'V', "history", "show long version and game history", dohistory, IFBURIED | GENERALCMD, NULL }, { 'i', "inventory", "show your inventory", - ddoinv, IFBURIED, NULL }, + ddoinv, IFBURIED | GENERALCMD, NULL }, { 'I', "inventtype", "show inventory of one specific item class", - dotypeinv, IFBURIED, NULL }, + dotypeinv, IFBURIED | GENERALCMD, NULL }, { M('i'), "invoke", "invoke an object's special powers", doinvoke, IFBURIED | AUTOCOMPLETE, NULL }, { M('j'), "jump", "jump to another location", @@ -1752,7 +1752,7 @@ struct ext_func_tab extcmdlist[] = { { M('m'), "monster", "use monster's special ability", domonability, IFBURIED | AUTOCOMPLETE, NULL }, { M('n'), "name", "same as call; name a monster or object or object type", - docallcmd, IFBURIED | AUTOCOMPLETE, NULL }, + docallcmd, IFBURIED | AUTOCOMPLETE | GENERALCMD, NULL }, { M('o'), "offer", "offer a sacrifice to the gods", dosacrifice, AUTOCOMPLETE | CMD_M_PREFIX, NULL }, { 'o', "open", "open a door", @@ -1827,17 +1827,17 @@ struct ext_func_tab extcmdlist[] = { { 's', "search", "search for traps and secret doors", dosearch, IFBURIED | CMD_M_PREFIX, "searching" }, { '*', "seeall", "show all equipment in use", - doprinuse, IFBURIED | CMD_M_PREFIX, NULL }, + doprinuse, IFBURIED | GENERALCMD | CMD_M_PREFIX, NULL }, { AMULET_SYM, "seeamulet", "show the amulet currently worn", - dopramulet, IFBURIED | CMD_M_PREFIX, NULL }, + dopramulet, IFBURIED | GENERALCMD | CMD_M_PREFIX, NULL }, { ARMOR_SYM, "seearmor", "show the armor currently worn", - doprarm, IFBURIED | CMD_M_PREFIX, NULL }, + doprarm, IFBURIED | GENERALCMD | CMD_M_PREFIX, NULL }, { RING_SYM, "seerings", "show the ring(s) currently worn", - doprring, IFBURIED | CMD_M_PREFIX, NULL }, + doprring, IFBURIED | GENERALCMD | CMD_M_PREFIX, NULL }, { TOOL_SYM, "seetools", "show the tools currently in use", - doprtool, IFBURIED | CMD_M_PREFIX, NULL }, + doprtool, IFBURIED | GENERALCMD | CMD_M_PREFIX, NULL }, { WEAPON_SYM, "seeweapon", "show the weapon currently wielded", - doprwep, IFBURIED | CMD_M_PREFIX, NULL }, + doprwep, IFBURIED | GENERALCMD | CMD_M_PREFIX, NULL }, { '!', "shell", "leave game to enter a sub-shell ('exit' to come back)", dosh_core, (IFBURIED | GENERALCMD | NOFUZZERCMD @@ -1847,11 +1847,11 @@ struct ext_func_tab extcmdlist[] = { ), NULL }, /* $ is like ),=,&c but is not included with *, so not called "seegold" */ { GOLD_SYM, "showgold", "show gold, possibly shop credit or debt", - doprgold, IFBURIED | CMD_M_PREFIX, NULL }, + doprgold, IFBURIED | GENERALCMD | CMD_M_PREFIX, NULL }, { SPBOOK_SYM, "showspells", "list and reorder known spells", - dovspell, IFBURIED, NULL }, + dovspell, IFBURIED | GENERALCMD, NULL }, { '^', "showtrap", "describe an adjacent, discovered trap", - doidtrap, IFBURIED, NULL }, + doidtrap, IFBURIED | GENERALCMD, NULL }, { M('s'), "sit", "sit down", dosit, AUTOCOMPLETE, NULL }, { '\0', "stats", "show memory statistics", @@ -1875,7 +1875,7 @@ struct ext_func_tab extcmdlist[] = { it may or may not actually invoke the #terrain command */ { '\177', "terrain", "view map without monsters or objects obstructing it", - doterrain, IFBURIED | AUTOCOMPLETE, NULL }, + doterrain, IFBURIED | GENERALCMD | AUTOCOMPLETE, NULL }, { '\0', "therecmdmenu", "menu of commands you can do from here to adjacent spot", dotherecmdmenu, AUTOCOMPLETE | GENERALCMD | MOUSECMD, NULL }, @@ -1911,7 +1911,7 @@ struct ext_func_tab extcmdlist[] = { { 'W', "wear", "wear a piece of armor", dowear, 0, NULL }, { '&', "whatdoes", "tell what a command does", - dowhatdoes, IFBURIED, NULL }, + dowhatdoes, IFBURIED | GENERALCMD, NULL }, { '/', "whatis", "show what type of thing a symbol corresponds to", dowhatis, IFBURIED | GENERALCMD, NULL }, { 'w', "wield", "wield (put in use) a weapon", From 1c5cf5382b4a1ae4bf23920d66172052042545d2 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Tue, 17 Mar 2026 17:29:43 +0200 Subject: [PATCH 298/442] Hellfill variance --- dat/hellfill.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/dat/hellfill.lua b/dat/hellfill.lua index 00175ffd3..3a9b4ed9f 100644 --- a/dat/hellfill.lua +++ b/dat/hellfill.lua @@ -413,14 +413,16 @@ hells = { -- 7: open cavern, "mines" with more space function () + -- walls are either stone or lava + local wter = percent(50) and " " or "L"; des.level_init({ style = "solidfill", fg = " ", lit = 0 }); des.level_flags("mazelevel", "noflip"); - des.level_init({ style="mines", fg=".", smoothed=true ,joined=true, lit=0 }); + des.level_init({ style="mines", fg=".", bg = wter, smoothed=true ,joined=true, lit=0 }); local sel = selection.match("."):grow(); - des.terrain({ selection = sel, typ = "." }); + des.terrain({ selection = sel, typ = ".", lit = 0 }); local border = selection.rect(0,0, 78, 20); - des.terrain({ selection = border, typ = " " }); + des.terrain({ selection = border, typ = wter, lit = 0 }); des.wallify(); end, From 8d33cafb3ef994018eeeab628f182f169a8db875 Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 17 Mar 2026 14:25:24 -0400 Subject: [PATCH 299/442] update tested versions of Visual Studio 2026-03-17 --- sys/windows/Makefile.nmake | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index 630575be5..044fb9e70 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.28 -# - Microsoft Visual Studio Community 2026 v 18.4.0 +# - Microsoft Visual Studio Community 2022 v 17.14.29 +# - Microsoft Visual Studio Community 2026 v 18.4.1 # #============================================================================== # This is used for building two distinct executables of NetHack: @@ -1190,6 +1190,7 @@ rc=Rc.exe # Recently tested versions: TESTEDVS2022 = 14.44.35222.0 TESTEDVS2026 = 14.50.35726.0 +TESTEDVS2026 = 14.50.35727.0 VS20261ST = 1450000000 VS2026CUR = $(TESTEDVS2026:.=) From 18cb293766fd19d9fabf5477c7d043d68ef5016b Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Wed, 18 Mar 2026 17:12:01 +0200 Subject: [PATCH 300/442] Replace magic number with define --- src/mon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mon.c b/src/mon.c index 0900ebe22..a8837d127 100644 --- a/src/mon.c +++ b/src/mon.c @@ -1119,7 +1119,7 @@ mcalcmove( if (mon->mspeed == MSLOW) { /* slow-monster effects work better against faster monsters: they lose 1/3 of their speed below 12 but 2/3 of their speed above */ - if (mmove < 12) + if (mmove < NORMAL_SPEED) mmove = (2 * mmove + 1) / 3; else mmove = 4 + (mmove / 3); From 9877e52025c8bca62b2f7bc50ad137d0940bba0b Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Wed, 18 Mar 2026 18:04:33 +0200 Subject: [PATCH 301/442] Ring of stealth prevents hero from leaving tracks --- doc/fixes3-7-0.txt | 1 + src/track.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 525eb0ac6..9a8bd5dc4 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -2900,6 +2900,7 @@ monsters will use throw-and-return weapons such as an aklys archeologists can identify scrolls by deciphering their labels monster destroy armor -spell first erodes armor iron shoes protect the wearer against certain floor-based traps +ring of stealth prevents hero from leaving tracks Platform- and/or Interface-Specific New Features diff --git a/src/track.c b/src/track.c index f5b915f22..178e5a6f9 100644 --- a/src/track.c +++ b/src/track.c @@ -23,6 +23,10 @@ initrack(void) void settrack(void) { + if ((uleft && uleft->otyp == RIN_STEALTH) + || (uright && uright->otyp == RIN_STEALTH)) + return; + if (utcnt < UTSZ) utcnt++; if (utpnt == UTSZ) From 4fae45220e5f2d372c236f11172fabcf13a904ea Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Wed, 18 Mar 2026 21:08:22 +0000 Subject: [PATCH 302/442] Remember price quotes that have been seen for types of item These are displayed in discoveries, and a new 'price_quotes' option allows them to be displayed for un-IDed objects in other contexts too (the idea is that you turn on the option while identifying objects and off for general play). Invalidates existing save files. --- dat/opthelp | 1 + doc/Guidebook.mn | 9 ++++++ doc/fixes3-7-0.txt | 3 ++ doc/options.txt | 13 ++++++--- include/extern.h | 2 ++ include/flag.h | 1 + include/objclass.h | 5 ++++ include/objects.h | 2 +- include/optlist.h | 3 ++ include/patchlevel.h | 2 +- src/o_init.c | 20 +++++++++---- src/objnam.c | 7 +++++ src/options.c | 1 + src/shk.c | 67 +++++++++++++++++++++++++++++++++++++++++++- 14 files changed, 124 insertions(+), 12 deletions(-) diff --git a/dat/opthelp b/dat/opthelp index 1fcb803d9..a5c7d9111 100644 --- a/dat/opthelp +++ b/dat/opthelp @@ -51,6 +51,7 @@ null allow nulls to be sent to your terminal [True] perm_invent keep inventory in a permanent window [False] pickup_stolen override pickup_types for stolen objects [True] pickup_thrown override pickup_types for thrown objects [True] +price_quotes show remembered price quotes for unIDed items [False] pushweapon when wielding a new weapon, put your previously [False] wielded weapon into the secondary weapon slot quick_farsight usually skip the chance to browse the map when [False] diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 20caca85a..9fd2c8d05 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -4592,6 +4592,15 @@ user name (on multi-user systems) or specifying a particular character name (on single-user systems) or it might be disabled entirely. Requesting it when not allowed or not possible results in explore mode instead. Default is normal play. +.lp price_quotes +Whenever the game mentions the name of an object you haven't identified yet, +it also mentions the range of buy and sell prices you have seen for that +item (to help narrow down what it could be). +The price shown is the unit price for one item (even when you are looking at +a stack of multiple items). +Many players may want to turn this on while identifying objects, and then +turn it back off again for general play. +Default is off. .lp pushweapon Using the \(oqw\(cq (wield) command when already wielding something pushes the old item into your alternate weapon slot (default off). diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 9a8bd5dc4..431a7e113 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -2901,6 +2901,9 @@ archeologists can identify scrolls by deciphering their labels monster destroy armor -spell first erodes armor iron shoes protect the wearer against certain floor-based traps ring of stealth prevents hero from leaving tracks +the game now automatically tracks which sell prices and buy prices you have + seen for each type of item; these are visible in the discoveries list, + and can also be shown elsewhere using the new 'price_quotes' option Platform- and/or Interface-Specific New Features diff --git a/doc/options.txt b/doc/options.txt index 8cc665b7e..71a4dd6bc 100644 --- a/doc/options.txt +++ b/doc/options.txt @@ -40,9 +40,14 @@ To add an entirely new option macro type: iii) an initialization of an element in the allopt[] array, at index opt_xxxx from step ii (xxxx is the option name). - 2. Create the optfn_xxxx() function in options.c. Failure to do that will - result in a link error of "undefined function optfn_xxxx." The functions are - in options.c in alphabetical sequence by function name. + 2. If you are adding a boolean option, link it to a global variable + (e.g. in flags or iflags) and update optfn_boolean in options.c if + necessary. + + 3. If you are not adding a boolean option, create the optfn_xxxx() + function in options.c. Failure to do that will result in a link error + of "undefined function optfn_xxxx." The functions are in options.c in + alphabetical sequence by function name. The skeletal template for an optn_xxxx() function is: @@ -83,7 +88,7 @@ To add an entirely new option macro type: return optn_ok; } - 3. NOTE: If you add (or delete) an option, please update the short + 4. NOTE: If you add (or delete) an option, please update the short options help (option_help()), the long options help (dat/opthelp) and also the Guidebooks. diff --git a/include/extern.h b/include/extern.h index 0af1dfda7..7c5dd9a49 100644 --- a/include/extern.h +++ b/include/extern.h @@ -2860,6 +2860,8 @@ extern void bclose(int); /* setpaid() has a conditional code block near the end of the function, where arg1 is tested for NULL, preventing NONNULLARG1 */ extern void setpaid(struct monst *) NO_NNARGS; +extern void record_price_quote(int, unsigned long, boolean); +extern void append_price_quote(char *, char **, int) NONNULLARG12; extern long money2mon(struct monst *, long) NONNULLARG1; extern void money2u(struct monst *, long) NONNULLARG1; extern void shkgone(struct monst *) NONNULLARG1; diff --git a/include/flag.h b/include/flag.h index dbcccd312..c251236f0 100644 --- a/include/flag.h +++ b/include/flag.h @@ -330,6 +330,7 @@ struct instance_flags { boolean num_pad; /* use numbers for movement commands */ boolean perm_invent; /* display persistent inventory window */ boolean perm_invent_pending; /* need to try again */ + boolean pricequotes; /* display price quotes on unIDd objects */ boolean renameallowed; /* can change hero name during role selection */ boolean renameinprogress; /* we are changing hero name */ boolean sounds; /* master on/off switch for using soundlib */ diff --git a/include/objclass.h b/include/objclass.h index b04fa496a..f7896487e 100644 --- a/include/objclass.h +++ b/include/objclass.h @@ -104,6 +104,11 @@ struct objclass { #define oc_level oc_oc2 /* books: spell level */ unsigned short oc_nutrition; /* food value */ + + unsigned long oc_sell_minseen; + unsigned long oc_sell_maxseen; + unsigned long oc_buy_minseen; + unsigned long oc_buy_maxseen; }; struct class_sym { diff --git a/include/objects.h b/include/objects.h index 1108f14b0..60ca4396c 100644 --- a/include/objects.h +++ b/include/objects.h @@ -44,7 +44,7 @@ #define OBJECT(obj,bits,prp,sym,prob,dly,wt, \ cost,sdam,ldam,oc1,oc2,nut,color,sn) \ { 0, 0, (char *) 0, bits, prp, sym, dly, color, prob, wt, \ - cost, sdam, ldam, oc1, oc2, nut } + cost, sdam, ldam, oc1, oc2, nut, -1UL, 0, -1UL, 0 } #define MARKER(tag,sn) /*empty*/ #elif defined(OBJECTS_ENUM) diff --git a/include/optlist.h b/include/optlist.h index 0625b1e51..b920048ac 100644 --- a/include/optlist.h +++ b/include/optlist.h @@ -589,6 +589,9 @@ static int optfn_##a(int, int, boolean, char *, char *); NHOPTB(preload_tiles, Advanced, 0, opt_out, set_in_config, /* MSDOS only */ On, Yes, No, No, NoAlias, &iflags.wc_preload_tiles, Term_False, (char *)0) + NHOPTB(price_quotes, General, 0, opt_in, set_in_game, + Off, Yes, No, No, NoAlias, &iflags.pricequotes, Term_False, + "display prices you have seen for unidentified objects") NHOPTB(pushweapon, Behavior, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias, &flags.pushweapon, Term_False, "previous weapon goes to secondary slot") diff --git a/include/patchlevel.h b/include/patchlevel.h index 9dde5579e..64ff46ee3 100644 --- a/include/patchlevel.h +++ b/include/patchlevel.h @@ -17,7 +17,7 @@ * Incrementing EDITLEVEL can be used to force invalidation of old bones * and save files. */ -#define EDITLEVEL 132 +#define EDITLEVEL 133 /* * Development status possibilities. diff --git a/src/o_init.c b/src/o_init.c index 26732a624..67fd4d21d 100644 --- a/src/o_init.c +++ b/src/o_init.c @@ -688,16 +688,20 @@ disco_typename(int otyp) return result; } -/* append typename(dis) to buf[], possibly truncating in the process */ +/* append typename(dis) to buf[], possibly truncating in the process; + also append price quote information if it fits */ staticfn void disco_append_typename(char *buf, int dis) { - unsigned len = (unsigned) strlen(buf); + size_t len = strlen(buf); char *p, *typnm = disco_typename(dis); + size_t typnm_len = strlen(typnm); + char *eos; - if (len + (unsigned) strlen(typnm) < BUFSZ) { + if (len + typnm_len < BUFSZ) { /* ordinary */ Strcat(buf, typnm); + eos = buf + len + typnm_len; } else if ((p = strrchr(typnm, '(')) != 0 && p > typnm && p[-1] == ' ' && strchr(p, ')') != 0) { /* typename() returned "really long user-applied name (actual type)" @@ -706,10 +710,14 @@ disco_append_typename(char *buf, int dis) --p; /* back up to space in front of open paren */ (void) strncat(buf, typnm, BUFSZ - 1 - (len + (unsigned) strlen(p))); Strcat(buf, p); + eos = buf + strlen(buf); } else { /* unexpected; just truncate from end of typename */ (void) strncat(buf, typnm, BUFSZ - 1 - len); + eos = buf + strlen(buf); } + + append_price_quote(buf, &eos, dis); } /* minor fixup for Book of the Dead needed in more than one place */ @@ -1125,6 +1133,7 @@ rename_disco(void) anything any; menu_item *selected = 0; int clr = NO_COLOR; + char buf[BUFSZ]; any = cg.zeroany; tmpwin = create_nhwindow(NHW_MENU); @@ -1158,9 +1167,10 @@ rename_disco(void) prev_class = oclass; } any.a_int = dis; + *buf = '\0'; + disco_append_typename(buf, dis); add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, - disco_typename(dis), MENU_ITEMFLAGS_NONE); + ATR_NONE, clr, buf, MENU_ITEMFLAGS_NONE); } } if (ct == 0) { diff --git a/src/objnam.c b/src/objnam.c index 762b9c25d..7c5a2d637 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -1659,6 +1659,8 @@ doname_base( Sprintf(pricebuf, "%ld %s", quotedprice, currency(quotedprice)); ConcatF2(bp, 0, " (%s, %s)", obj->unpaid ? "unpaid" : "contents", pricebuf); + + record_price_quote(obj->otyp, quotedprice / obj->quan, TRUE); } else if (with_price) { /* on floor or in container on floor */ int nochrg = 0; long price = get_cost_of_shop_item(obj, &nochrg); @@ -1672,6 +1674,11 @@ doname_base( } else if (nochrg > 0) { Concat(bp, 0, " (no charge)"); } + + if (price) + record_price_quote(obj->otyp, price / obj->quan, TRUE); + } else if (iflags.pricequotes && !objects[obj->otyp].oc_name_known) { + append_price_quote(bp, &bp_eos, obj->otyp); } if (!strncmp(prefix, "a ", 2)) { diff --git a/src/options.c b/src/options.c index 090300fe1..d63db5ba7 100644 --- a/src/options.c +++ b/src/options.c @@ -5334,6 +5334,7 @@ optfn_boolean( disp.botl = TRUE; break; case opt_fixinv: + case opt_price_quotes: case opt_sortpack: case opt_implicit_uncursed: case opt_wizweight: diff --git a/src/shk.c b/src/shk.c index 4b1aedc1e..927352971 100644 --- a/src/shk.c +++ b/src/shk.c @@ -433,6 +433,65 @@ setpaid(struct monst *shkp) } } +/* Remembers that a shopkeeper has quoted a particular price for a + particular type of object. */ +void +record_price_quote(int otyp, unsigned long price, boolean buyprice) { + struct objclass *oc = &objects[otyp]; + if (buyprice) { + if (price > oc->oc_buy_maxseen) oc->oc_buy_maxseen = price; + if (price < oc->oc_buy_minseen) oc->oc_buy_minseen = price; + } else { + if (price > oc->oc_sell_maxseen) oc->oc_sell_maxseen = price; + if (price < oc->oc_sell_minseen) oc->oc_sell_minseen = price; + } +} + +/* Appends price-quote information to the given buffer, updating the + given end of string position. *eos mut be buf + strlen(buf). If the + update would make bug longer than BUFSZ, instead does nothing. */ +void +append_price_quote(char *buf, char **eos, int otyp) { + char buf2[BUFSZ]; + char *eos2 = buf2; + const char *sep = ""; + size_t len = *eos - buf; + size_t len2; + + if (objects[otyp].oc_sell_minseen > objects[otyp].oc_sell_maxseen && + objects[otyp].oc_buy_minseen > objects[otyp].oc_buy_maxseen) + return; + + eos2 += sprintf(eos2, " {"); + + if (objects[otyp].oc_buy_minseen < objects[otyp].oc_buy_maxseen) { + eos2 += sprintf(eos2, "buy %lu-%lu", + objects[otyp].oc_buy_minseen, + objects[otyp].oc_buy_maxseen); + sep = " "; + } else if (objects[otyp].oc_buy_minseen == objects[otyp].oc_buy_maxseen) { + eos2 += sprintf(eos2, "buy %lu", + objects[otyp].oc_buy_minseen); + sep = " "; + } + + if (objects[otyp].oc_sell_minseen < objects[otyp].oc_sell_maxseen) { + eos2 += sprintf(eos2, "%ssell %lu-%lu", sep, + objects[otyp].oc_sell_minseen, + objects[otyp].oc_sell_maxseen); + } else if (objects[otyp].oc_sell_minseen == objects[otyp].oc_sell_maxseen) { + eos2 += sprintf(eos2, "%ssell %lu", sep, + objects[otyp].oc_sell_minseen); + } + + eos2 += sprintf(eos2, "}"); + len2 = eos2 - buf2; + if (len2 < BUFSZ - len - 1) { + Strcpy(*eos, buf2); + *eos += len2; + } +} + staticfn long addupbill(struct monst *shkp) { @@ -3285,7 +3344,7 @@ add_one_tobill( bp->bo_id = obj->o_id; bp->bquan = obj->quan; if (dummy) { /* a dummy object must be inserted into */ - bp->useup = TRUE; /* the gb.billobjs chain here. crucial for */ + bp->useup = TRUE; /* the gb.billobjs chain here. crucial for */ add_to_billobjs(obj); /* eating floorfood in shop. see eat.c */ } else bp->useup = FALSE; @@ -3300,6 +3359,7 @@ add_one_tobill( } eshkp->billct++; obj->unpaid = 1; + record_price_quote(obj->otyp, bp->price, TRUE); } staticfn void @@ -3492,6 +3552,9 @@ addtobill( if (!Deaf && !muteshk(shkp) && !silent) { char buf[BUFSZ]; + /* no need to update price quotes here; it was done by + add_one_tobill above */ + if (!ltmp) { pline("%s has no interest in %s.", Shknam(shkp), the(xname(obj))); return; @@ -3991,6 +4054,7 @@ sellobj( pline("%s cannot pay you at present.", Shknam(shkp)); Sprintf(qbuf, "Will you accept %ld %s in credit for ", tmpcr, currency(tmpcr)); + record_price_quote(obj->otyp, tmpcr / obj->quan, FALSE); c = ynaq(safe_qbuf(qbuf, qbuf, "?", obj, doname, thesimpleoname, (obj->quan == 1L) ? "that" : "those")); if (c == 'a') { @@ -4086,6 +4150,7 @@ sellobj( : and_its_contents) : "", one ? "it" : "them"); + record_price_quote(obj->otyp, offer / obj->quan, FALSE); (void) safe_qbuf(qbuf, qbuf, qsfx, obj, xname, simpleonames, one ? "that" : "those"); } else From ea023677c7edb50923eb262f947fe0d0a1df8da7 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Wed, 18 Mar 2026 23:23:34 +0000 Subject: [PATCH 303/442] Work around MSVC false positive warning in price-tracking code The MSVC warning assumes that any attempt to write - followed by an unsigned number is an attempt to create a negative number (which produces the wrong data type if you type, e.g., -2147483648). However, it's also warning when an explicit U is given, e.g. in -1UL, which is clearly not an attempt to express a negative value. Suppress the warning by rewriting the number in question as (0UL-1UL) with a binary rather than unary minus. --- include/objects.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/objects.h b/include/objects.h index 60ca4396c..0de88dbba 100644 --- a/include/objects.h +++ b/include/objects.h @@ -41,10 +41,14 @@ the second zero is oc_spare1 for padding between oc_tough and oc_dir */ #define BITS(nmkn,mrg,uskn,ctnr,mgc,chrg,uniq,nwsh,big,tuf,dir,sub,mtrl) \ nmkn,mrg,uskn,0,mgc,chrg,uniq,nwsh,big,tuf,0,dir,mtrl,sub /*cpp fodder*/ +/* note: 0UL-1UL is a method of expressing the largest possible + unsigned long value whilst working around a false-positive warning + in Microsoft Visual C (which assumes that a negative number was + intended despite the explicit U suffix) */ #define OBJECT(obj,bits,prp,sym,prob,dly,wt, \ cost,sdam,ldam,oc1,oc2,nut,color,sn) \ { 0, 0, (char *) 0, bits, prp, sym, dly, color, prob, wt, \ - cost, sdam, ldam, oc1, oc2, nut, -1UL, 0, -1UL, 0 } + cost, sdam, ldam, oc1, oc2, nut, (0UL-1UL), 0, (0UL-1UL), 0 } #define MARKER(tag,sn) /*empty*/ #elif defined(OBJECTS_ENUM) From 5d3a1e684bed7a7d622a2a1ad72d466e94c169d3 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Wed, 18 Mar 2026 22:16:22 +0000 Subject: [PATCH 304/442] Add shields of shock resistance and of drain resistance These both appear as "wooden shield" when unidentified; so does the small shield. --- doc/fixes3-7-0.txt | 1 + include/objects.h | 14 ++++++++++---- include/patchlevel.h | 2 +- src/do_wear.c | 6 +++++- src/u_init.c | 6 ++++-- win/share/objects.txt | 40 +++++++++++++++++++++++++++++++++++++++- 6 files changed, 60 insertions(+), 9 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 431a7e113..82df2aec0 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -2904,6 +2904,7 @@ ring of stealth prevents hero from leaving tracks the game now automatically tracks which sell prices and buy prices you have seen for each type of item; these are visible in the discoveries list, and can also be shown elsewhere using the new 'price_quotes' option +new shields: shield of shock resistance, shield of drain resistance Platform- and/or Interface-Specific New Features diff --git a/include/objects.h b/include/objects.h index 0de88dbba..932d063f6 100644 --- a/include/objects.h +++ b/include/objects.h @@ -650,9 +650,15 @@ CLOAK("cloak of displacement", "piece of cloth", CLOAK_OF_DISPLACEMENT), /* shields */ -SHIELD("small shield", NoDes, - 1, 0, 0, 0, 6, 0, 30, 3, 9, 0, WOOD, HI_WOOD, +SHIELD("small shield", "wooden shield", + 0, 0, 0, 0, 4, 0, 30, 3, 9, 0, WOOD, HI_WOOD, SMALL_SHIELD), +SHIELD("shield of drain resistance", "wooden shield", + 0, 1, 0, DRAIN_RES, 3, 0, 30, 50, 9, 0, WOOD, HI_WOOD, + SHIELD_OF_DRAIN_RESISTANCE), +SHIELD("shield of shock resistance", "wooden shield", + 0, 1, 0, SHOCK_RES, 3, 0, 30, 50, 9, 0, WOOD, HI_WOOD, + SHIELD_OF_SHOCK_RESISTANCE), SHIELD("elven shield", "blue and green shield", 0, 0, 0, 0, 2, 0, 40, 7, 8, 0, WOOD, CLR_GREEN, ELVEN_SHIELD), @@ -663,10 +669,10 @@ SHIELD("orcish shield", "red-eyed shield", 0, 0, 0, 0, 2, 0, 50, 7, 9, 0, IRON, CLR_RED, ORCISH_SHIELD), SHIELD("large shield", NoDes, - 1, 0, 1, 0, 7, 0, 100, 10, 8, 0, IRON, HI_METAL, + 1, 0, 1, 0, 4, 0, 100, 10, 8, 0, IRON, HI_METAL, LARGE_SHIELD), SHIELD("dwarvish roundshield", "large round shield", - 0, 0, 0, 0, 4, 0, 100, 10, 8, 0, IRON, HI_METAL, + 0, 0, 0, 0, 3, 0, 100, 10, 8, 0, IRON, HI_METAL, DWARVISH_ROUNDSHIELD), SHIELD("shield of reflection", "polished silver shield", 0, 1, 0, REFLECTING, 3, 0, 50, 50, 8, 0, SILVER, HI_SILVER, diff --git a/include/patchlevel.h b/include/patchlevel.h index 64ff46ee3..0d814189e 100644 --- a/include/patchlevel.h +++ b/include/patchlevel.h @@ -17,7 +17,7 @@ * Incrementing EDITLEVEL can be used to force invalidation of old bones * and save files. */ -#define EDITLEVEL 133 +#define EDITLEVEL 134 /* * Development status possibilities. diff --git a/src/do_wear.c b/src/do_wear.c index 025b097ef..382b17741 100644 --- a/src/do_wear.c +++ b/src/do_wear.c @@ -706,10 +706,12 @@ Shield_on(void) { /* no shield currently requires special handling when put on, but we keep this uncommented in case somebody adds a new one which does - [reflection is handled by setting u.uprops[REFLECTION].extrinsic + [the magical shields are handled by setting u.uprops[*].extrinsic in setworn() called by armor_or_accessory_on() before Shield_on()] */ switch (uarms->otyp) { case SMALL_SHIELD: + case SHIELD_OF_DRAIN_RESISTANCE: + case SHIELD_OF_SHOCK_RESISTANCE: case ELVEN_SHIELD: case URUK_HAI_SHIELD: case ORCISH_SHIELD: @@ -736,6 +738,8 @@ Shield_off(void) keep this uncommented in case somebody adds a new one which does */ switch (uarms->otyp) { case SMALL_SHIELD: + case SHIELD_OF_DRAIN_RESISTANCE: + case SHIELD_OF_SHOCK_RESISTANCE: case ELVEN_SHIELD: case URUK_HAI_SHIELD: case ORCISH_SHIELD: diff --git a/src/u_init.c b/src/u_init.c index 9c37508c0..dcf3e4077 100644 --- a/src/u_init.c +++ b/src/u_init.c @@ -603,8 +603,10 @@ knows_class(char sym) */ for (ct = svb.bases[(uchar) sym]; ct < svb.bases[(uchar) sym + 1]; ct++) { - /* not flagged as magic but shouldn't be pre-discovered */ - if (ct == CORNUTHAUM || ct == DUNCE_CAP) + /* not flagged as magic but shouldn't be pre-discovered + (small shields look the same as two types of magical shield; + cornuthaum / dunce cap look the same as each other) */ + if (ct == CORNUTHAUM || ct == DUNCE_CAP || ct == SMALL_SHIELD) continue; if (sym == WEAPON_CLASS) { odummy.otyp = ct; /* update 'o' */ diff --git a/win/share/objects.txt b/win/share/objects.txt index 9c1a3e736..64a53c16a 100644 --- a/win/share/objects.txt +++ b/win/share/objects.txt @@ -2938,7 +2938,45 @@ Z = (195, 195, 195) .....PPPPPAA.... .......PPAA..... } -# tile 152 (small shield) +# tile 152 (wooden shield / small shield) +{ + ................ + ................ + ................ + ................ + ................ + ...C.CJJ.J...... + ...CCKKJJJA..... + ...CKKKJJJA..... + ...CKKJJJJA..... + ...CKKJJJJA..... + ....CKKJJAA..... + .....CKJAA...... + ......CAA....... + .......A........ + ................ + ................ +} +# tile 152 (wooden shield / shield of drain resistance) +{ + ................ + ................ + ................ + ................ + ................ + ...C.CJJ.J...... + ...CCKKJJJA..... + ...CKKKJJJA..... + ...CKKJJJJA..... + ...CKKJJJJA..... + ....CKKJJAA..... + .....CKJAA...... + ......CAA....... + .......A........ + ................ + ................ +} +# tile 152 (wooden shield / shield of shock resistance) { ................ ................ From 85e3d721fba02affbbfdb48c8b04af6dd2cd30d3 Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 18 Mar 2026 23:32:26 +0000 Subject: [PATCH 305/442] Increase the value range of corpse weights This breaks save compatibility, but is being pushed together with other save-breaking changes to avoid the need for multiple bumps to EDITLEVEL. --- include/permonst.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/permonst.h b/include/permonst.h index 6df66b841..fbc135c41 100644 --- a/include/permonst.h +++ b/include/permonst.h @@ -64,8 +64,8 @@ struct permonst { aligntyp maligntyp; /* basic monster alignment */ unsigned short geno; /* creation/geno mask value */ struct attack mattk[NATTK]; /* attacks matrix */ - unsigned short cwt, /* weight of corpse */ - cnutrit; /* its nutritional value */ + unsigned cwt; /* weight of corpse */ + unsigned short cnutrit; /* its nutritional value */ uchar msound; /* noise it makes (6 bits) */ uchar msize; /* physical size (3 bits) */ uchar mresists; /* resistances */ From 6886b199be7e1a18e6a3ba37dae2ffb9de62fd33 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Thu, 19 Mar 2026 00:58:21 +0000 Subject: [PATCH 306/442] Teleport traps don't teleport you on no-teleport levels In previous versions of NetHack, this wasn't a problem because teleport traps didn't *generate* on such levels, but more recent changes have made this situation possible (e.g. a demon lord migrates to a Gehennom filler level that already had a teleport trap on it). This change causes a "wrenching sensation", like stepping onto a teleport trap with magic resistance does. --- doc/fixes3-7-0.txt | 2 ++ src/teleport.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 82df2aec0..464e6e701 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -2210,6 +2210,8 @@ when reading an engraving, suppress the addition of a trailing period if the text already has one cursed magic whistle could teleport hero on no-teleport levels give a brief explanation when receiving a wish for acquiring the Amulet +teleportation traps no longer teleport you on levels made temporarily + no-teleport by the presence of a monster Fixes to 3.7.0-x Platform and/or Interface Problems Exposed Via git Repository diff --git a/src/teleport.c b/src/teleport.c index 48d3fd7dc..9858dfe49 100644 --- a/src/teleport.c +++ b/src/teleport.c @@ -1494,7 +1494,7 @@ tele_trap(struct trap *trap) return; in_tele_trap = TRUE; - if (In_endgame(&u.uz) || Antimagic) { + if (In_endgame(&u.uz) || Antimagic || noteleport_level(&gy.youmonst)) { if (Antimagic) shieldeff(u.ux, u.uy); You_feel("a wrenching sensation."); From 07fc4904c67d472c5e0930697d57e9bdea318554 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Thu, 19 Mar 2026 01:16:30 +0000 Subject: [PATCH 307/442] Add a new wand, the wand of stasis A wand of stasis prevents teleportation (even in some cases where it would normally not be prevented, e.g. the hero teleporting a monster, or covetous monsters teleporting). This is intended to provide an alternative tactic against covetous monsters (and their AI has been adjusted to handle being under a stasis effect), but might also be useful in other situations. It does not prevent teleportation of objects, only the hero / monsters, and does not at present prevent level teleportation (although I'm not sure about this and it might well change in the future). This breaks save compatibility, but is being pushed together with other save-breaking changes to avoid the need for multiple bumps to EDITLEVEL. --- doc/fixes3-7-0.txt | 1 + include/objects.h | 4 +++- include/rm.h | 1 + src/apply.c | 5 +++++ src/do.c | 3 ++- src/engrave.c | 1 + src/mklev.c | 1 + src/mkobj.c | 4 ++++ src/monmove.c | 7 +++---- src/teleport.c | 21 +++++++++++++++++---- src/wizard.c | 20 +++++++++++++------- src/zap.c | 5 +++++ win/share/objects.txt | 19 +++++++++++++++++++ 13 files changed, 75 insertions(+), 17 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 464e6e701..0b9220403 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -2907,6 +2907,7 @@ the game now automatically tracks which sell prices and buy prices you have seen for each type of item; these are visible in the discoveries list, and can also be shown elsewhere using the new 'price_quotes' option new shields: shield of shock resistance, shield of drain resistance +new wand: wand of stasis Platform- and/or Interface-Specific New Features diff --git a/include/objects.h b/include/objects.h index 932d063f6..faa630a50 100644 --- a/include/objects.h +++ b/include/objects.h @@ -1457,9 +1457,11 @@ WAND("create monster", "maple", 45, 200, 1, NODIR, WOOD, HI_WOOD, WAN_CREATE_MONSTER), WAND("wishing", "pine", 5, 500, 1, NODIR, WOOD, HI_WOOD, WAN_WISHING), +WAND("stasis", "redwood", 45, 150, 1, NODIR, WOOD, CLR_RED, + WAN_STASIS), WAND("nothing", "oak", 25, 100, 0, IMMEDIATE, WOOD, HI_WOOD, WAN_NOTHING), -WAND("striking", "ebony", 75, 150, 1, IMMEDIATE, WOOD, HI_WOOD, +WAND("striking", "ebony", 30, 150, 1, IMMEDIATE, WOOD, HI_WOOD, WAN_STRIKING), WAND("make invisible", "marble", 45, 150, 1, IMMEDIATE, MINERAL, HI_MINERAL, WAN_MAKE_INVISIBLE), diff --git a/include/rm.h b/include/rm.h index 1c9f49f14..8811deae1 100644 --- a/include/rm.h +++ b/include/rm.h @@ -454,6 +454,7 @@ struct levelflags { Bitfield(stormy, 1); /* clouds create lightning bolts at random */ schar temperature; /* +1 == hot, -1 == cold */ + long stasis_until; /* wand of stasis effect lasts until when? */ }; typedef struct { diff --git a/src/apply.c b/src/apply.c index 6cbcb30a8..3abfc5b6c 100644 --- a/src/apply.c +++ b/src/apply.c @@ -526,6 +526,10 @@ magic_whistled(struct obj *obj) already_discovered = objects[obj->otyp].oc_name_known != 0; int omx, omy, shift = 0, appear = 0, disappear = 0, trapped = 0; + /* stasis prevents magic-whistling */ + if (svl.level.flags.stasis_until >= svm.moves) + return; + /* need to copy (up to 3) names as they're collected rather than just save pointers to them, otherwise churning through every mbuf[] might clobber the ones we care about */ @@ -3984,6 +3988,7 @@ do_break_wand(struct obj *obj) case WAN_PROBING: case WAN_ENLIGHTENMENT: case WAN_SECRET_DOOR_DETECTION: + case WAN_STASIS: pline(nothing_else_happens); discard_broken_wand(); return ECMD_TIME; diff --git a/src/do.c b/src/do.c index 875d0c62f..26485069c 100644 --- a/src/do.c +++ b/src/do.c @@ -2248,7 +2248,8 @@ revive_mon(anything *arg, long timeout UNUSED) /* corpse will revive somewhere else if there is a monster in the way; Riders get a chance to try to bump the obstacle out of their way */ if (is_displacer(mptr) && body->where == OBJ_FLOOR - && get_obj_location(body, &x, &y, 0) && (mtmp = m_at(x, y)) != 0) { + && get_obj_location(body, &x, &y, 0) && (mtmp = m_at(x, y)) != 0 && + svl.level.flags.stasis_until < svm.moves) { boolean notice_it = canseemon(mtmp); /* before rloc() */ char *monname = Monnam(mtmp); diff --git a/src/engrave.c b/src/engrave.c index c84908cf1..22ca30aaf 100644 --- a/src/engrave.c +++ b/src/engrave.c @@ -589,6 +589,7 @@ doengrave_sfx_item_WAN(struct _doengrave_ctx *de) /* NODIR wands */ case WAN_LIGHT: case WAN_SECRET_DOOR_DETECTION: + case WAN_STASIS: case WAN_CREATE_MONSTER: case WAN_WISHING: case WAN_ENLIGHTENMENT: diff --git a/src/mklev.c b/src/mklev.c index 0350562f3..703133ca4 100644 --- a/src/mklev.c +++ b/src/mklev.c @@ -891,6 +891,7 @@ clear_level_structures(void) svl.level.flags.noautosearch = 0; svl.level.flags.fumaroles = 0; svl.level.flags.stormy = 0; + svl.level.flags.stasis_until = 0L; svn.nroom = 0; svr.rooms[0].hx = -1; diff --git a/src/mkobj.c b/src/mkobj.c index f2adf2185..ab0f98fb3 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -1115,6 +1115,10 @@ mksobj_init(struct obj **obj, boolean artif) case WAND_CLASS: if (otmp->otyp == WAN_WISHING) otmp->spe = 1; + else if (otmp->otyp == WAN_STASIS) + /* just as easy to recharge as other NODIR wands, but starts with + fewer charges */ + otmp->spe = rn1(4, 3); else otmp->spe = rn1(5, (objects[otmp->otyp].oc_dir == NODIR) ? 11 : 4); diff --git a/src/monmove.c b/src/monmove.c index 3830e3713..6489d19d4 100644 --- a/src/monmove.c +++ b/src/monmove.c @@ -1794,11 +1794,10 @@ m_move(struct monst *mtmp, int after) if (covetousattack & M_ATTK_AGR_DIED) return MMOVE_DIED; mmoved = MMOVE_MOVED; - } else { - mmoved = MMOVE_NOTHING; + return postmov(mtmp, ptr, omx, omy, mmoved, + seenflgs, can_tunnel, can_unlock, can_open); } - return postmov(mtmp, ptr, omx, omy, mmoved, - seenflgs, can_tunnel, can_unlock, can_open); + /* otherwise continue with normal AI routine */ } /* likewise for shopkeeper, guard, or priest */ diff --git a/src/teleport.c b/src/teleport.c index 9858dfe49..b235cb238 100644 --- a/src/teleport.c +++ b/src/teleport.c @@ -34,8 +34,13 @@ noteleport_level(struct monst *mon) if (get_iter_mons(m_blocks_teleporting)) return TRUE; - /* natural no-teleport level */ - if (svl.level.flags.noteleport) + /* natural no-teleport level; covetous monsters can bypass these */ + if (svl.level.flags.noteleport && !is_covetous(mon->data)) + return TRUE; + + /* wand of stasis prevents teleportation while the effect is active + (even for covetous monsters) */ + if (svl.level.flags.stasis_until >= svm.moves) return TRUE; return FALSE; @@ -1958,8 +1963,11 @@ mtele_trap(struct monst *mtmp, struct trap *trap, int in_sight) { char *monname; - if (tele_restrict(mtmp)) + /* don't print feedback here: a monster stepping on a trap and not + teleporting from it isn't visible */ + if (noteleport_level(mtmp)) return; + if (teleport_pet(mtmp, FALSE)) { /* save name with pre-movement visibility */ monname = Monnam(mtmp); @@ -2257,7 +2265,12 @@ u_teleport_mon( { coord cc; - if (mtmp->ispriest && *in_rooms(mtmp->mx, mtmp->my, TEMPLE)) { + if (svl.level.flags.stasis_until >= svm.moves) { + if (give_feedback) + pline("A mysterious force prevents you teleporting %s!", + mon_nam(mtmp)); + return FALSE; + } else if (mtmp->ispriest && *in_rooms(mtmp->mx, mtmp->my, TEMPLE)) { if (give_feedback) pline("%s resists your magic!", Monnam(mtmp)); return FALSE; diff --git a/src/wizard.c b/src/wizard.c index 674b4db6e..c89e3296f 100644 --- a/src/wizard.c +++ b/src/wizard.c @@ -386,10 +386,12 @@ tactics(struct monst *mtmp) mtmp->mavenge = 1; /* covetous monsters attack while fleeing */ if (In_W_tower(mx, my, &u.uz) || (mtmp->iswiz && !sx && !mon_has_amulet(mtmp))) { - if (!rn2(3 + mtmp->mhp / 10)) + if (!noteleport_level(mtmp) && + !rn2(3 + mtmp->mhp / 10)) (void) rloc(mtmp, RLOC_MSG); } else if (sx && (mx != sx || my != sy)) { - if (!mnearto(mtmp, sx, sy, TRUE, RLOC_MSG)) { + if (!noteleport_level(mtmp) && + !mnearto(mtmp, sx, sy, TRUE, RLOC_MSG)) { /* couldn't move to the target spot for some reason, so stay where we are (don't actually need rloc_to() because mtmp is still on the map at ... */ @@ -408,7 +410,7 @@ tactics(struct monst *mtmp) /*FALLTHRU*/ case STRAT_NONE: /* harass */ - if (!rn2(!mtmp->mflee ? 5 : 33)) + if (!noteleport_level(mtmp) && !rn2(!mtmp->mflee ? 5 : 33)) mnexto(mtmp, RLOC_MSG); return 0; @@ -419,13 +421,16 @@ tactics(struct monst *mtmp) int targ = (int) (strat & STRAT_GOAL); struct obj *otmp; - if (!targ) { /* simply wants you to close */ + if (!targ || !isok(tx, ty)) { /* simply wants you to close */ return 0; } + if (noteleport_level(mtmp) && !monnear(mtmp, tx, ty)) + return 0; if (u_at(tx, ty) || where == STRAT_PLAYER) { /* player is standing on it (or has it) */ mx = mtmp->mx, my = mtmp->my; - if (!mnearto(mtmp, tx, ty, FALSE, RLOC_MSG)) + if (noteleport_level(mtmp) || + !mnearto(mtmp, tx, ty, FALSE, RLOC_MSG)) rloc_to(mtmp, mx, my); /* no room? stay put */ return 0; } @@ -445,13 +450,14 @@ tactics(struct monst *mtmp) return 0; } else { /* a monster is standing on it - cause some trouble */ - if (!rn2(5)) + if (!rn2(5) && !noteleport_level(mtmp)) mnexto(mtmp, RLOC_MSG); return 0; } } else { /* a monster has it - 'port beside it. */ mx = mtmp->mx, my = mtmp->my; - if (!mnearto(mtmp, tx, ty, FALSE, RLOC_MSG)) + if (!noteleport_level(mtmp) && + !mnearto(mtmp, tx, ty, FALSE, RLOC_MSG)) rloc_to(mtmp, mx, my); /* no room? stay put */ return 0; } diff --git a/src/zap.c b/src/zap.c index 96dc3d44b..adecdbeca 100644 --- a/src/zap.c +++ b/src/zap.c @@ -2555,6 +2555,11 @@ zapnodir(struct obj *obj) known = !!obj->dknown; (void) findit(); break; + case WAN_STASIS: + /* no immediately obvious effect, and no message so that it isn't + distinguishable from other NODIR wands that produce no message */ + svl.level.flags.stasis_until = svm.moves + rn1(21, 10); + break; case WAN_CREATE_MONSTER: /* create_critters() returns True iff hero sees a new monster appear */ if (create_critters(rn2(23) ? 1 : rn1(7, 2), diff --git a/win/share/objects.txt b/win/share/objects.txt index 64a53c16a..986d88e6f 100644 --- a/win/share/objects.txt +++ b/win/share/objects.txt @@ -7973,6 +7973,25 @@ Z = (195, 195, 195) ................ ................ } +# tile 413 (redwood / stasis) +{ + ................ + ................ + ................ + ...........NO... + ..........DDAA.. + .........DDAA... + ........DDAA.... + .......DDAA..... + ......DDAA...... + .....DDAA....... + ....DDAA........ + ...NOAA......... + ....AA.......... + ................ + ................ + ................ +} # tile 414 (oak / nothing) { ................ From 4d043a6f9ce2d04999e5d0b52cf934cda790addb Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Thu, 19 Mar 2026 03:46:42 +0000 Subject: [PATCH 308/442] Monsters require experience using wands before they can hit with them It takes time for an early-game monster to acclimatize itself to the power of an attack wand: in much the same way as a nervous human may quite possibly miss with their first attempt to use a gun in combat, an early-game monster will always miss on its first use of an attack wand (but from then on will understand how they work and get over their nerves, and will hit as normal). This is a balance change based on observed results from tournaments: guarding against deaths to early-game attack wands requires an unusually cautious playstyle which isn't much fun (and might not always be possible even for the best players), so it is quite common for them to be the cause of random deaths that it wasn't worth trying to avoid. Although trying to dodge a monster who found an attack wand is fun, you only actually get that fun if something makes you aware of the danger: the monster missing with the wand is a clear way to demonstrate the danger and let the player know that now is the right time to take precautions. This change could theoretically have broken saves, but probably doesn't due to there having been a spare bit in struct monst. Just in case, it is being pushed together with other save-breaking changes to avoid the need for multiple bumps to EDITLEVEL. --- doc/fixes3-7-0.txt | 1 + include/extern.h | 3 ++- include/monst.h | 2 ++ src/makemon.c | 4 ++++ src/mthrowu.c | 3 ++- src/muse.c | 32 ++++++++++++++++++++++++++------ src/zap.c | 11 ++++++----- 7 files changed, 43 insertions(+), 13 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 0b9220403..d32fda191 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -2908,6 +2908,7 @@ the game now automatically tracks which sell prices and buy prices you have and can also be shown elsewhere using the new 'price_quotes' option new shields: shield of shock resistance, shield of drain resistance new wand: wand of stasis +early-game monsters always miss with their first use of an attack wand Platform- and/or Interface-Specific New Features diff --git a/include/extern.h b/include/extern.h index 7c5dd9a49..69fca8969 100644 --- a/include/extern.h +++ b/include/extern.h @@ -3969,7 +3969,8 @@ extern int zhitm(struct monst *, int, int, struct obj **) NONNULLPTRS; extern int burn_floor_objects(coordxy, coordxy, boolean, boolean); extern void ubuzz(int, int); extern void buzz(int, int, coordxy, coordxy, int, int); -extern void dobuzz(int, int, coordxy, coordxy, int, int, boolean, boolean); +extern void dobuzz(int, int, coordxy, coordxy, int, int, + boolean, boolean, boolean); extern void melt_ice(coordxy, coordxy, const char *) NO_NNARGS; extern void start_melt_ice_timeout(coordxy, coordxy, long); extern void melt_ice_away(union any *, long) NONNULLARG1; diff --git a/include/monst.h b/include/monst.h index 15114c600..f1fb3b9c2 100644 --- a/include/monst.h +++ b/include/monst.h @@ -162,6 +162,8 @@ struct monst { Bitfield(meverseen, 1); /* mon has been seen at some point */ Bitfield(mspotted, 1); /* mon is currently seen by hero */ + Bitfield(mwandexp, 1); /* mon has experience with wands */ + /* 6 spare bits */ unsigned long mstrategy; /* for monsters with mflag3: current strategy */ #ifdef NHSTDC diff --git a/src/makemon.c b/src/makemon.c index 130694401..4f12e12cb 100644 --- a/src/makemon.c +++ b/src/makemon.c @@ -1288,6 +1288,10 @@ makemon( /* quest leader and nemesis both know about all trap types */ if (ptr->msound == MS_LEADER || ptr->msound == MS_NEMESIS) mon_learns_traps(mtmp, ALL_TRAPS); + /* locations where monsters are already experienced with wands */ + if (Is_stronghold(&u.uz) || Is_knox(&u.uz) || In_endgame(&u.uz) || + In_hell(&u.uz) || In_V_tower(&u.uz) || In_quest(&u.uz)) + mtmp->mwandexp = TRUE; place_monster(mtmp, x, y); mtmp->mcansee = mtmp->mcanmove = TRUE; diff --git a/src/mthrowu.c b/src/mthrowu.c index 613150656..88066266d 100644 --- a/src/mthrowu.c +++ b/src/mthrowu.c @@ -1094,7 +1094,8 @@ breamm(struct monst *mtmp, struct attack *mattk, struct monst *mtarg) Monnam(mtmp), breathwep_name(typ)); gb.buzzer = mtmp; dobuzz(BZ_M_BREATH(BZ_OFS_AD(typ)), (int) mattk->damn, - mtmp->mx, mtmp->my, sgn(gt.tbx), sgn(gt.tby), utarget, utarget); + mtmp->mx, mtmp->my, sgn(gt.tbx), sgn(gt.tby), + utarget, utarget, FALSE); gb.buzzer = 0; nomul(0); /* breath runs out sometimes. Also, give monster some diff --git a/src/muse.c b/src/muse.c index aa0474e4e..2d5e82bd4 100644 --- a/src/muse.c +++ b/src/muse.c @@ -31,6 +31,7 @@ staticfn boolean hero_behind_chokepoint(struct monst *); staticfn boolean mon_has_friends(struct monst *); staticfn boolean mon_likes_objpile_at(struct monst *mtmp, coordxy x, coordxy y) NONNULLARG1; staticfn int mbhitm(struct monst *, struct obj *); +staticfn void buzz_force_miss(int, int, coordxy, coordxy, int, int); staticfn boolean fhito_loc(struct obj *obj, coordxy x, coordxy y, int (*fhito)(OBJ_P, OBJ_P)); staticfn void mbhit(struct monst *, int, int (*)(MONST_P, OBJ_P), @@ -1614,7 +1615,8 @@ mbhitm(struct monst *mtmp, struct obj *otmp) Soundeffect(se_boing, 40); pline("Boing!"); learnit = TRUE; - } else if (rnd(20) < 10 + u.uac) { + } else if (rnd(20) < 10 + u.uac && + !(gb.buzzer && !gb.buzzer->mwandexp)) { monstunseesu(M_SEEN_MAGR); /* mons see hero not resisting */ pline_The("wand hits you!"); tmp = d(2, 12); @@ -1809,6 +1811,12 @@ mbhit( } } +staticfn void +buzz_force_miss(int type, int nd, coordxy sx, coordxy sy, int dx, int dy) +{ + dobuzz(type, nd, sx, sy, dx, dy, TRUE, FALSE, TRUE); +} + /* Perform an offensive action for a monster. Must be called immediately * after find_offensive(). Return values are same as use_defensive(). */ @@ -1819,6 +1827,12 @@ use_offensive(struct monst *mtmp) struct obj *otmp = gm.m.offensive; boolean oseen; + /* if a monster has never used an attack wand before, it takes them some + time to get used to holding that much power, so the first shot always + misses */ + void (*buzzfn)(int, int, coordxy, coordxy, int, int) = + mtmp->mwandexp ? buzz : buzz_force_miss; + /* offensive potions are not drunk, they're thrown */ if (otmp->oclass != POTION_CLASS && (i = precheck(mtmp, otmp)) != 0) return i; @@ -1837,12 +1851,13 @@ use_offensive(struct monst *mtmp) gm.m_using = TRUE; gc.current_wand = otmp; gb.buzzer = mtmp; - buzz(BZ_M_WAND(BZ_OFS_WAN(otmp->otyp)), - (otmp->otyp == WAN_MAGIC_MISSILE) ? 2 : 6, mtmp->mx, mtmp->my, - sgn(mtmp->mux - mtmp->mx), sgn(mtmp->muy - mtmp->my)); + buzzfn(BZ_M_WAND(BZ_OFS_WAN(otmp->otyp)), + (otmp->otyp == WAN_MAGIC_MISSILE) ? 2 : 6, mtmp->mx, mtmp->my, + sgn(mtmp->mux - mtmp->mx), sgn(mtmp->muy - mtmp->my)); gb.buzzer = 0; gc.current_wand = 0; gm.m_using = FALSE; + mtmp->mwandexp = TRUE; return (DEADMONSTER(mtmp)) ? 1 : 2; case MUSE_FIRE_HORN: case MUSE_FROST_HORN: @@ -1850,13 +1865,14 @@ use_offensive(struct monst *mtmp) gm.m_using = TRUE; gb.buzzer = mtmp; gc.current_wand = otmp; /* needed by zhitu() */ - buzz(BZ_M_WAND(BZ_OFS_AD((otmp->otyp == FROST_HORN) ? AD_COLD - : AD_FIRE)), + buzzfn(BZ_M_WAND(BZ_OFS_AD( + (otmp->otyp == FROST_HORN) ? AD_COLD : AD_FIRE)), rn1(6, 6), mtmp->mx, mtmp->my, sgn(mtmp->mux - mtmp->mx), sgn(mtmp->muy - mtmp->my)); gb.buzzer = 0; gc.current_wand = 0; gm.m_using = FALSE; + mtmp->mwandexp = TRUE; return (DEADMONSTER(mtmp)) ? 1 : 2; case MUSE_WAN_TELEPORTATION: case MUSE_WAN_UNDEAD_TURNING: @@ -1864,9 +1880,13 @@ use_offensive(struct monst *mtmp) gz.zap_oseen = oseen; mzapwand(mtmp, otmp, FALSE); gm.m_using = TRUE; + gb.buzzer = mtmp; mbhit(mtmp, rn1(8, 6), mbhitm, bhito, otmp); + gb.buzzer = 0; /* note: 'otmp' might have been destroyed (drawbridge destruction) */ gm.m_using = FALSE; + if (gm.m.has_offense == MUSE_WAN_STRIKING) + mtmp->mwandexp = TRUE; return 2; case MUSE_SCR_EARTH: { /* TODO: handle steeds */ diff --git a/src/zap.c b/src/zap.c index adecdbeca..12c482227 100644 --- a/src/zap.c +++ b/src/zap.c @@ -4750,13 +4750,13 @@ disintegrate_mon( void ubuzz(int type, int nd) { - dobuzz(type, nd, u.ux, u.uy, u.dx, u.dy, TRUE, FALSE); + dobuzz(type, nd, u.ux, u.uy, u.dx, u.dy, TRUE, FALSE, FALSE); } void buzz(int type, int nd, coordxy sx, coordxy sy, int dx, int dy) { - dobuzz(type, nd, sx, sy, dx, dy, TRUE, FALSE); + dobuzz(type, nd, sx, sy, dx, dy, TRUE, FALSE, FALSE); } /* @@ -4774,7 +4774,8 @@ dobuzz( int nd, /* damage strength ('number of dice') */ coordxy sx, coordxy sy, /* starting point */ int dx, int dy, /* direction delta */ - boolean sayhit, boolean saymiss) /* report out of sight hit/miss events */ + boolean sayhit, boolean saymiss, /* report out of sight hit/miss events */ + boolean forcemiss) { int range, fltyp = zaptype(type), damgtype = fltyp % 10; coordxy lsx, lsy; @@ -4860,7 +4861,7 @@ dobuzz( buzzmonst: gn.notonhead = (mon->mx != gb.bhitpos.x || mon->my != gb.bhitpos.y); - if (zap_hit(find_mac(mon), spell_type)) { + if (!forcemiss && zap_hit(find_mac(mon), spell_type)) { if (mon_reflects(mon, (char *) 0)) { if (cansee(mon->mx, mon->my)) { hit(flash_str(fltyp, FALSE), mon, exclam(0)); @@ -4950,7 +4951,7 @@ dobuzz( if (u.usteed && !rn2(3) && !mon_reflects(u.usteed, (char *) 0)) { mon = u.usteed; goto buzzmonst; - } else if (zap_hit((int) u.uac, 0)) { + } else if (!forcemiss && zap_hit((int) u.uac, 0)) { range -= 2; pline_dir(xytodir(-dx, -dy), "%s hits you!", The(flash_str(fltyp, FALSE))); From e6d44c68e85e36074d4517fa77957fee18ace3f6 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Thu, 19 Mar 2026 05:10:09 +0000 Subject: [PATCH 309/442] Overhaul of priest donations The old code had two main problems: a) it was very difficult for unspoiled players to figure out how it worked (because donating too much got you a bad result, and the exact amount you needed depended on magic numbers that weren't stated in game, and because you had to hide your visible gold to get a good result); b) for players who knew the mechanics, it was somewhat exploitable and also somewhat tedious to make use of (due to needing to hide visible gold before donating). This change preserves the spirit of the previous code whilst making things more transparent for new players and less tedious for existing players: the donation amounts for the various effects are still roughly the same (but randomized), but the amounts you need to donate for clairvoyance and for protection are explicitly stated (and as before, the alignment reset is done by donating an unnecessarily large amount and isn't explicitly stated as an option). If you have a lot of visible gold, you still need to donate a sizeable proportion of it to get a useful effect, but now you get a larger reward to compensate for the larger donation (to the extent that doing this gives comparable results to doing it as a series of small donations, removing the incentive to hide your gold before donating). There's also something here for those players who like to squeeze every last point of optimality out of a game: the "obvious" donation strategy gives decent results, but players who are really willing to dig into the mechanics may be able to find a way to get slightly better results on average (which if I've balanced this correctly, will lead to a very long and complicated spoiler). One other change is that this is now based on your peak rather than current level, to fix an exploit in which the character was drained down to level 1 to donate a very large amount of gold (improving by 20 AC points) and then immediately restored back to the previous experience level using a blessed potion of restore ablity. This breaks save compatibility, but is being pushed together with other save-breaking changes to avoid the need for multiple bumps to EDITLEVEL. --- doc/fixes3-7-0.txt | 3 ++ include/extern.h | 2 +- include/mextra.h | 1 + src/minion.c | 7 +++-- src/priest.c | 74 ++++++++++++++++++++++++++++++++++------------ 5 files changed, 64 insertions(+), 23 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index d32fda191..d5b973c6f 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1582,6 +1582,9 @@ healers may get tiny damage increase when attacking with knives allow rogues to also backstab sleeping or paralyzed monsters rogues cannot backstab monsters that have no backside give experience if opening Schroedinger's Box causes death of the cat inside +priest donation amounts are explicitly stated, randomized slightly, based on + peak rather than current level, and allow for bulk donations (buying + larger amounts of clairvoyance/protection) if you have a lot of gold Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/include/extern.h b/include/extern.h index 69fca8969..28ec3fa4f 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1545,7 +1545,7 @@ extern int monster_census(boolean); extern int msummon(struct monst *); extern void summon_minion(aligntyp, boolean); extern int demon_talk(struct monst *) NONNULLARG1; -extern long bribe(struct monst *) NONNULLARG1; +extern long bribe(struct monst *, const char *) NONNULLARG12; extern int dprince(aligntyp); extern int dlord(aligntyp); extern int llord(void); diff --git a/include/mextra.h b/include/mextra.h index 6cafb4890..8472a5ad2 100644 --- a/include/mextra.h +++ b/include/mextra.h @@ -98,6 +98,7 @@ struct epri { schar shroom; /* index in rooms */ coord shrpos; /* position of shrine */ d_level shrlevel; /* level (& dungeon) of shrine */ + unsigned cheapskate_count; /* number of cheapskate donations */ long intone_time, /* used to limit verbosity +*/ enter_time, /*+ of temple entry messages */ hostile_time, /* forbidding feeling */ diff --git a/src/minion.c b/src/minion.c index 085a51643..96f9ffe1c 100644 --- a/src/minion.c +++ b/src/minion.c @@ -332,7 +332,8 @@ demon_talk(struct monst *mtmp) else if (canseemon(mtmp)) pline("%s seems to be demanding something.", Amonnam(mtmp)); offer = 0L; - if (!Deaf && ((offer = bribe(mtmp)) >= demand)) { + if (!Deaf && + ((offer = bribe(mtmp, "How much will you offer?")) >= demand)) { pline("%s vanishes, laughing about cowardly mortals.", Amonnam(mtmp)); } else if (offer > 0L @@ -357,13 +358,13 @@ demon_talk(struct monst *mtmp) } long -bribe(struct monst *mtmp) +bribe(struct monst *mtmp, const char *prompt) { char buf[BUFSZ] = DUMMY; long offer; long umoney = money_cnt(gi.invent); - getlin("How much will you offer?", buf); + getlin(prompt, buf); if (sscanf(buf, "%ld", &offer) != 1) offer = 0L; diff --git a/src/priest.c b/src/priest.c index 41e77179a..cb480a757 100644 --- a/src/priest.c +++ b/src/priest.c @@ -559,6 +559,8 @@ priest_talk(struct monst *priest) { boolean coaligned = p_coaligned(priest); boolean strayed = (u.ualign.record < 0); + unsigned *cheapskate = NULL; + if (EPRI(priest)) cheapskate = &EPRI(priest)->cheapskate_count; /* * Note: we won't be called if hero is Deaf [since dochat() will @@ -625,53 +627,87 @@ priest_talk(struct monst *priest) pline("%s is not interested.", Monnam(priest)); return; } else { + /* there's now some randomization in how much you need to donate, but + you are given suggested donation values that will guarantee + clairvoyance and protection respectively; with more gold visible + you need to donate more but get a greater effect; and if you + cheapskate out to rerandomize the donation amounts they will be + higher next time */ long offer; + long suggested = (u.ulevelpeak ? u.ulevelpeak : 1 ) * + rn1(101, 150 + (cheapskate ? *cheapskate : 0) * 40); + long quan = money_cnt(gi.invent) / (suggested * 3); + char buf[BUFSZ]; - pline("%s asks you for a contribution for the temple.", - Monnam(priest)); - if ((offer = bribe(priest)) == 0) { + if (quan < 1) + quan = 1; + + Sprintf(buf, "How much will you offer (suggested: %ld or %ld)?", + suggested * quan, suggested * quan * 2); + + if (flags.debug) + pline("%s asks you for a contribution for the temple (base %ld).", + Monnam(priest), suggested); + else + pline("%s asks you for a contribution for the temple.", + Monnam(priest)); + if ((offer = bribe(priest, buf)) == 0) { SetVoice(priest, 0, 80, 0); verbalize("Thou shalt regret thine action!"); if (coaligned) adjalign(-1); - } else if (offer < (u.ulevel * 200)) { + if (cheapskate) ++*cheapskate; + } else if (offer < suggested * quan) { if (money_cnt(gi.invent) > (offer * 2L)) { SetVoice(priest, 0, 80, 0); verbalize("Cheapskate."); + if (cheapskate) ++*cheapskate; } else { SetVoice(priest, 0, 80, 0); verbalize("I thank thee for thy contribution."); /* give player some token */ exercise(A_WIS, TRUE); } - } else if (offer < (u.ulevel * 400)) { + } else if (offer < suggested * quan * 2) { SetVoice(priest, 0, 80, 0); verbalize("Thou art indeed a pious individual."); if (money_cnt(gi.invent) < (offer * 2L)) { if (coaligned && u.ualign.record <= ALGN_SINNED) adjalign(1); - verbalize("I bestow upon thee a blessing."); - incr_itimeout(&HClairvoyant, rn1(500, 500)); } - } else if (offer < (u.ulevel * 600) - /* u.ublessed is only active when Protection is - enabled via something other than worn gear - (theft by gremlin clears the intrinsic but not - its former magnitude, making it recoverable) */ - && (!(HProtection & INTRINSIC) - || (u.ublessed < 20 - && (u.ublessed < 9 || !rn2(u.ublessed))))) { - SetVoice(priest, 0, 80, 0); - verbalize("Thou hast been rewarded for thy devotion."); + verbalize("I bestow upon thee a blessing."); + incr_itimeout(&HClairvoyant, rn1(500 * offer / suggested, + 500 * offer / suggested)); + } else if (offer < suggested * quan * 3) { + int orig_ublessed = u.ublessed; + + /* u.ublessed is only active when Protection is enabled via + something other than worn gear (theft by gremlin clears the + intrinsic but not its former magnitude, making it + recoverable) */ if (!(HProtection & INTRINSIC)) { HProtection |= FROMOUTSIDE; + orig_ublessed = -1; /* force "rewarded" message */ + } + + for (; offer >= (2 * suggested); offer -= (2 * suggested)) { if (!u.ublessed) u.ublessed = rn1(3, 2); - } else - u.ublessed++; + else if (u.ublessed < 20 && + (u.ublessed < 9 || !rn2(u.ublessed))) + u.ublessed++; + } + SetVoice(priest, 0, 80, 0); + if (u.ublessed > orig_ublessed) { + verbalize("Thou hast been rewarded for thy devotion."); + } else { + verbalize("Thy selfless generosity is deeply appreciated."); + } } else { SetVoice(priest, 0, 80, 0); verbalize("Thy selfless generosity is deeply appreciated."); + /* money_cnt check is preserved for futureproofing but probably + can't fail in the current code */ if (money_cnt(gi.invent) < (offer * 2L) && coaligned) { if (strayed && (svm.moves - u.ucleansed) > 5000L) { u.ualign.record = 0; /* cleanse thee */ From e8363ae23d090256b52d3317c41e1431148c1d24 Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 19 Mar 2026 09:48:29 -0400 Subject: [PATCH 310/442] include limits.h in cstd.h Ensure the standard C99 values are available to the NetHack code base, so that code/macros to accomplish essentially the same thing are not necessary. CHAR_BIT Defines the number of bits in a byte. SCHAR_MIN Defines the minimum value for a signed char. SCHAR_MAX Defines the maximum value for a signed char. UCHAR_MAX Defines the maximum value for an unsigned char. CHAR_MIN Defines the minimum value for type char and its value will be equal to SCHAR_MIN if char represents negative values, otherwise zero. CHAR_MAX Defines the value for type char and its value will be equal to SCHAR_MAX if char represents negative values, otherwise UCHAR_MAX. MB_LEN_MAX Defines the maximum number of bytes in a multi-byte character. SHRT_MIN Defines the minimum value for a short int. SHRT_MAX Defines the maximum value for a short int. USHRT_MAX Defines the maximum value for an unsigned short int. INT_MIN Defines the minimum value for an int. INT_MAX Defines the maximum value for an int. UINT_MAX Defines the maximum value for an unsigned int. LONG_MIN Defines the minimum value for a long int. LONG_MAX Defines the maximum value for a long int. ULONG_MAX Defines the maximum value for an unsigned long int. --- include/cstd.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/cstd.h b/include/cstd.h index 3e4764b52..54c950a40 100644 --- a/include/cstd.h +++ b/include/cstd.h @@ -59,6 +59,7 @@ #include #include #include +#include #else /* !__cplusplus */ /* for FILE */ From 87ab214229e195c3e39eaffe403977b599140cc5 Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 19 Mar 2026 09:54:19 -0400 Subject: [PATCH 311/442] allow some additional values for compiler testing Allow compiler.370 to recognize a couple of additional values on the Make command line. . --- sys/unix/hints/include/compiler.370 | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sys/unix/hints/include/compiler.370 b/sys/unix/hints/include/compiler.370 index b686636a2..32623fe32 100755 --- a/sys/unix/hints/include/compiler.370 +++ b/sys/unix/hints/include/compiler.370 @@ -55,6 +55,7 @@ CFLAGS+=-Wall -Wextra \ CFLAGS+=-pedantic CFLAGS+=-Wmissing-declarations #CFLAGS+=-Wformat=2 +CFLAGS+=-Wdiscarded-qualifiers # these are left out of the C++ flags CFLAGS+=-Wformat-nonliteral @@ -234,6 +235,20 @@ ifeq "$(C23)" "1" HAVECSTD=c2x endif +ifeq "$(c2x)" "1" +HAVECSTD=c2x +endif +ifeq "$(C2X)" "1" +HAVECSTD=c2x +endif + +ifeq "$(c2y)" "1" +HAVECSTD=c2y +endif +ifeq "$(C2Y)" "1" +HAVECSTD=c2y +endif + ifneq "$(HAVECSTD)" "" CSTD = -std=$(HAVECSTD) endif From 9aa2f61058b4b4c3bdc5dabc373bd33188d941bf Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 19 Mar 2026 10:02:10 -0400 Subject: [PATCH 312/442] limits.h follow-up --- src/botl.c | 3 --- src/end.c | 3 --- src/files.c | 5 ----- 3 files changed, 11 deletions(-) diff --git a/src/botl.c b/src/botl.c index eddfcccf5..2e9ed01db 100644 --- a/src/botl.c +++ b/src/botl.c @@ -4,9 +4,6 @@ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" -#ifndef LONG_MAX -#include -#endif extern const char *const hu_stat[]; /* defined in eat.c */ diff --git a/src/end.c b/src/end.c index a4ed7fc0d..1da474b39 100644 --- a/src/end.c +++ b/src/end.c @@ -9,9 +9,6 @@ #ifndef NO_SIGNAL #include #endif -#ifndef LONG_MAX -#include -#endif #include "dlb.h" #ifndef SFCTOOL diff --git a/src/files.c b/src/files.c index abdb728f5..b8efef8d1 100644 --- a/src/files.c +++ b/src/files.c @@ -3139,11 +3139,6 @@ debugcore(const char *filename, boolean wildcards) #endif /*DEBUG*/ #ifndef SFCTOOL -#ifdef UNIX -#ifndef PATH_MAX -#include -#endif -#endif #define SYSCONFFILE "system configuration file" From e21995a36d290b73ddf93409fd541c7274af59b1 Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 19 Mar 2026 10:10:59 -0400 Subject: [PATCH 313/442] follow-up bit (from testing) --- sys/unix/hints/include/compiler.370 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/unix/hints/include/compiler.370 b/sys/unix/hints/include/compiler.370 index 32623fe32..100d86299 100755 --- a/sys/unix/hints/include/compiler.370 +++ b/sys/unix/hints/include/compiler.370 @@ -55,7 +55,7 @@ CFLAGS+=-Wall -Wextra \ CFLAGS+=-pedantic CFLAGS+=-Wmissing-declarations #CFLAGS+=-Wformat=2 -CFLAGS+=-Wdiscarded-qualifiers +#CFLAGS+=-Wdiscarded-qualifiers # these are left out of the C++ flags CFLAGS+=-Wformat-nonliteral From 6cd742632c4e2c62b85a6af36ac7eff6beb5146e Mon Sep 17 00:00:00 2001 From: nhw_cron Date: Wed, 18 Mar 2026 17:16:09 -0400 Subject: [PATCH 314/442] This is cron-daily v1-Jan-12-2026. 005guidebook updated: doc/Guidebook.txt --- doc/Guidebook.txt | 670 +++++++++++++++++++++++----------------------- 1 file changed, 335 insertions(+), 335 deletions(-) diff --git a/doc/Guidebook.txt b/doc/Guidebook.txt index 1a362eb63..13e160360 100644 --- a/doc/Guidebook.txt +++ b/doc/Guidebook.txt @@ -5038,6 +5038,15 @@ it when not allowed or not possible results in explore mode instead. Default is normal play. + price_quotes + Whenever the game mentions the name of an object you haven't identi- + fied yet, it also mentions the range of buy and sell prices you have + seen for that item (to help narrow down what it could be). The + price shown is the unit price for one item (even when you are look- + ing at a stack of multiple items). Many players may want to turn + this on while identifying objects, and then turn it back off again + for general play. Default is off. + pushweapon Using the `w' (wield) command when already wielding something pushes the old item into your alternate weapon slot (default off). Like- @@ -5065,15 +5074,6 @@ prompted unless role forces a choice for race. Cannot be set with the `O' command. Persistent. - reroll - Allows rerolling your character's starting inventory and attributes - (default false). Persistent. - - Note that rerolling your character is not a recommended way to play - if aiming merely to win (a lucky start has a much smaller influence - on whether or not you win the game than your actions later in the - game). This option exists partly as an acknowledgement that some - players will insist on doing so anyway, and partly because rerolling NetHack 3.7.0 February 11, 2026 @@ -5086,6 +5086,15 @@ + reroll + Allows rerolling your character's starting inventory and attributes + (default false). Persistent. + + Note that rerolling your character is not a recommended way to play + if aiming merely to win (a lucky start has a much smaller influence + on whether or not you win the game than your actions later in the + game). This option exists partly as an acknowledgement that some + players will insist on doing so anyway, and partly because rerolling may be necessary for certain types of challenge games. rest_on_space @@ -5131,15 +5140,6 @@ teleport - update the map after movement has finished; run - update the map after every seven or so steps; - walk - update the map after each step; - crawl - like walk, but pause briefly after each step. - - This option only affects the game's screen display, not the actual - results of moving. The default is "run"; versions prior to 3.4.1 - used "teleport" only. Whether or not the effect is noticeable will - depend upon the window port used or on the type of terminal. Per- - sistent. - NetHack 3.7.0 February 11, 2026 @@ -5152,6 +5152,15 @@ + walk - update the map after each step; + crawl - like walk, but pause briefly after each step. + + This option only affects the game's screen display, not the actual + results of moving. The default is "run"; versions prior to 3.4.1 + used "teleport" only. Whether or not the effect is noticeable will + depend upon the window port used or on the type of terminal. Per- + sistent. + safe_pet Prevent you from (knowingly) attacking your pets (default on). Per- sistent. @@ -5198,15 +5207,6 @@ interface or your terminal emulator window displays fewer than 25 lines. Persistent. - silent - Suppress terminal beeps (default on). Persistent. - - sortdiscoveries - Controls the sorting behavior for the output of the `\' and ``' com- - mands. Persistent. - - - NetHack 3.7.0 February 11, 2026 @@ -5218,6 +5218,13 @@ + silent + Suppress terminal beeps (default on). Persistent. + + sortdiscoveries + Controls the sorting behavior for the output of the `\' and ``' com- + mands. Persistent. + The possible values are: o - list object types by class, in discovery order within each @@ -5265,13 +5272,6 @@ index; z - order by count, low to high; ties broken by internal index. - Can be interactively set via the `m O' command or via using the `m' - prefix before either the #vanquished command or the #genocided com- - mand. - - sounds - Allow sounds to be emitted from an integrated sound library (default - on). NetHack 3.7.0 February 11, 2026 @@ -5284,6 +5284,14 @@ + Can be interactively set via the `m O' command or via using the `m' + prefix before either the #vanquished command or the #genocided com- + mand. + + sounds + Allow sounds to be emitted from an integrated sound library (default + on). + sparkle Display a sparkly effect when a monster (including yourself) is hit by an attack to which it is resistant (default on). Persistent. @@ -5330,14 +5338,6 @@ tombstone Draw a tombstone graphic upon your death (default on). Persistent. - toptenwin - Put the ending display in a NetHack window instead of on stdout - (default off). Setting this option makes the score list visible - when a windowing version of NetHack is started without a parent win- - dow, but it no longer leaves the score list around after game end on - a terminal or emulating window. - - NetHack 3.7.0 February 11, 2026 @@ -5350,6 +5350,13 @@ + toptenwin + Put the ending display in a NetHack window instead of on stdout + (default off). Setting this option makes the score list visible + when a windowing version of NetHack is started without a parent win- + dow, but it no longer leaves the score list around after game end on + a terminal or emulating window. + travel Allow the travel command via mouse click (default on). Turning this option off will prevent the game from attempting unintended moves if @@ -5396,14 +5403,7 @@ Filtering can also be changed when getting a location with the "get- pos.filter" key. - whatis_menu - When getting a location on the map, and using a key to cycle through - next and previous targets, use a menu instead to pick a target. - (default off) - whatis_moveskip - When getting a location on the map, and using shifted movement keys - or meta-digit keys to fast-move, instead of moving 8 units at a NetHack 3.7.0 February 11, 2026 @@ -5416,6 +5416,14 @@ + whatis_menu + When getting a location on the map, and using a key to cycle through + next and previous targets, use a menu instead to pick a target. + (default off) + + whatis_moveskip + When getting a location on the map, and using shifted movement keys + or meta-digit keys to fast-move, instead of moving 8 units at a time, move by skipping the same glyphs. (default off) windowtype @@ -5462,14 +5470,6 @@ Where to align or place the status window (top, bottom, left, or right). - ascii_map - If NetHack can, it should display the map using simple characters - (letters and punctuation) rather than tiles graphics. In some - cases, characters can be augmented with line-drawing symbols; use - the symset option to select a symbol set such as DECgraphics or - IBMgraphics if your display supports them. Setting ascii_map to - True forces tiled_map to be False. - NetHack 3.7.0 February 11, 2026 @@ -5482,6 +5482,14 @@ + ascii_map + If NetHack can, it should display the map using simple characters + (letters and punctuation) rather than tiles graphics. In some + cases, characters can be augmented with line-drawing symbols; use + the symset option to select a symbol set such as DECgraphics or + IBMgraphics if your display supports them. Setting ascii_map to + True forces tiled_map to be False. + color If NetHack can, it should display color if it can for different mon- sters, objects, and dungeon features (default on). @@ -5528,14 +5536,6 @@ fullscreen If NetHack can, it should try to display on the entire screen rather - than in a window. - - guicolor - Use color text and/or highlighting attributes when displaying some - non-map data (such as menu selector letters). Curses interface - only; default is on. - - NetHack 3.7.0 February 11, 2026 @@ -5548,6 +5548,13 @@ + than in a window. + + guicolor + Use color text and/or highlighting attributes when displaying some + non-map data (such as menu selector letters). Curses interface + only; default is on. + large_font If NetHack can, it should use a large font. @@ -5595,13 +5602,6 @@ When set to 3, the tty interface moves some fields around and mainly shows status conditions on their own line. A display capable of - showing at least 25 lines is recommended. The value can be toggled - back and forth during the game with the `O' command. - - The curses interface does likewise if the align_status option is set - to top or bottom but ignores statuslines when set to left or right. - - NetHack 3.7.0 February 11, 2026 @@ -5614,6 +5614,12 @@ + showing at least 25 lines is recommended. The value can be toggled + back and forth during the game with the `O' command. + + The curses interface does likewise if the align_status option is set + to top or bottom but ignores statuslines when set to left or right. + The Qt interface already displays more than 3 lines for status so uses the statuslines value differently. A value of 3 renders status in the Qt interface's original format, with the status window spread @@ -5663,12 +5669,6 @@ If NetHack can, it should display this number of messages at a time in the message window. - windowborders - Whether to draw boxes around the map, status area, message area, and - persistent inventory window if enabled. Curses interface only. - Acceptable values are - - NetHack 3.7.0 February 11, 2026 @@ -5680,6 +5680,11 @@ + windowborders + Whether to draw boxes around the map, status area, message area, and + persistent inventory window if enabled. Curses interface only. + Acceptable values are + 0 - off, never show borders 1 - on, always show borders 2 - auto, on if display is at least (24+2)x(80+2) [default] @@ -5728,12 +5733,7 @@ OPTION=crash_email:email_address - OPTION=crash_name:your_name - These options are used only to save you some typing on the - crash report and #bugreport forms. - OPTION=crash_urlmax:bytes - This option is used to limit the length of the URLs generated NetHack 3.7.0 February 11, 2026 @@ -5746,6 +5746,12 @@ + OPTION=crash_name:your_name + These options are used only to save you some typing on the + crash report and #bugreport forms. + + OPTION=crash_urlmax:bytes + This option is used to limit the length of the URLs generated and is only needed if your browser cannot handle arbitrarily long URLs. @@ -5794,12 +5800,6 @@ video Set the video mode used (PC NetHack only). Values are "autodetect", - "default", "vga", or "vesa". Setting "vesa" will cause the game to - display tiles, using the full capability of the VGA hardware. Set- - ting "vga" will cause the game to display tiles, fixed at 640x480 in - 16 colors, a mode that is compatible with all VGA hardware. Third - party tilesets will probably not work. Setting "autodetect" - attempts "vesa", then "vga", and finally sets "default" if neither NetHack 3.7.0 February 11, 2026 @@ -5812,6 +5812,12 @@ + "default", "vga", or "vesa". Setting "vesa" will cause the game to + display tiles, using the full capability of the VGA hardware. Set- + ting "vga" will cause the game to display tiles, fixed at 640x480 in + 16 colors, a mode that is compatible with all VGA hardware. Third + party tilesets will probably not work. Setting "autodetect" + attempts "vesa", then "vga", and finally sets "default" if neither of those modes works. Cannot be set with the `O' command. video_height @@ -5861,12 +5867,6 @@ In addition, some characters are treated specially if they occur as the first character in the pattern, specifically: - < - always pickup an object that matches rest of pattern; - > - never pickup an object that matches rest of pattern. - - The autopickup_exception rules are processed in the order in which - they appear in your configuration file, thus allowing a later rule - NetHack 3.7.0 February 11, 2026 @@ -5878,6 +5878,11 @@ + < - always pickup an object that matches rest of pattern; + > - never pickup an object that matches rest of pattern. + + The autopickup_exception rules are processed in the order in which + they appear in your configuration file, thus allowing a later rule to override an earlier rule. Exceptions can be set with the `O' command, but because they are not @@ -5926,11 +5931,6 @@ You can bind "mouse1" or "mouse2" to "nothing", "therecmdmenu", "clicklook", or "mouseaction". - Special command keys - Below are the special commands you can rebind. Some of them can be - bound to same keys with no problems, others are in the same "con- - text", and if bound to same keys, only one of those commands will be - available. Special command can only be bound to a single key. @@ -5944,6 +5944,12 @@ + Special command keys + Below are the special commands you can rebind. Some of them can be + bound to same keys with no problems, others are in the same "con- + text", and if bound to same keys, only one of those commands will be + available. Special command can only be bound to a single key. + count Prefix key to start a count, to repeat a command this many times. With number_pad only. Default is `n'. @@ -5991,13 +5997,7 @@ getpos.help When asked for a location, the key to show help. Default is `?'. - getpos.mon.next - When asked for a location, the key to go to next closest monster. - Default is `m'. - getpos.mon.prev - When asked for a location, the key to go to previous closest mon- - ster. Default is `M'. NetHack 3.7.0 February 11, 2026 @@ -6010,6 +6010,14 @@ + getpos.mon.next + When asked for a location, the key to go to next closest monster. + Default is `m'. + + getpos.mon.prev + When asked for a location, the key to go to previous closest mon- + ster. Default is `M'. + getpos.obj.next When asked for a location, the key to go to next closest object. Default is `o'. @@ -6055,14 +6063,6 @@ When asked for a location, the key to choose the location, and show more info without asking. Default is `:'. - getpos.self - When asked for a location, the key to go to your location. Default - is `@'. - - getpos.unexplored.next - When asked for a location, the key to go to next closest unexplored - location. Default is `x'. - @@ -6076,6 +6076,14 @@ + getpos.self + When asked for a location, the key to go to your location. Default + is `@'. + + getpos.unexplored.next + When asked for a location, the key to go to next closest unexplored + location. Default is `x'. + getpos.unexplored.prev When asked for a location, the key to go to previous closest unex- plored location. Default is `X'. @@ -6123,14 +6131,6 @@ user is prompted with more-prompt, and a message matching "You dis- placed ." is not shown at all. - The order of the defined MSGTYPE lines is important; the last match- - ing rule is used. Put the general case first, exceptions below them. - - 9.12. Configuring Menu Colors - - Some platforms allow you to define colors used in menu lines when - the line matches a user-defined pattern. At this time the tty, - NetHack 3.7.0 February 11, 2026 @@ -6142,6 +6142,13 @@ + The order of the defined MSGTYPE lines is important; the last match- + ing rule is used. Put the general case first, exceptions below them. + + 9.12. Configuring Menu Colors + + Some platforms allow you to define colors used in menu lines when + the line matches a user-defined pattern. At this time the tty, curses, win32tty and win32gui interfaces support this. In general, the configuration file entries to describe the menu @@ -6186,15 +6193,8 @@ implicit_uncursed option off so that all items known to be uncursed are actually displayed with the "uncursed" description. - 9.13. Configuring User Sounds - Some platforms allow you to define sound files to be played when - a message that matches a user-defined pattern is delivered to the mes- - sage window. At this time the Qt port and the win32tty and win32gui - ports support the use of user sounds. - The following configuration file entries are relevant to mapping - user sounds to messages: @@ -6208,6 +6208,16 @@ + 9.13. Configuring User Sounds + + Some platforms allow you to define sound files to be played when + a message that matches a user-defined pattern is delivered to the mes- + sage window. At this time the Qt port and the win32tty and win32gui + ports support the use of user sounds. + + The following configuration file entries are relevant to mapping + user sounds to messages: + SOUNDDIR The directory that houses the sound files to be played. @@ -6252,16 +6262,6 @@ OPTION=hilite_status:hitpoints/<=30%/red/normal (That example is actually specifying red&normal for <=30% and no- - color&normal for >30%.) - - For another example, the following line in your configuration - file will cause wisdom to be displayed red if it drops and green if it - rises: - - OPTION=hilite_status:wisdom/down/red/up/green - - Allowed colors are black, red, green, brown, blue, magenta, cyan, - gray, orange, light-green, yellow, light-blue, light-magenta, light- NetHack 3.7.0 February 11, 2026 @@ -6274,6 +6274,16 @@ + color&normal for >30%.) + + For another example, the following line in your configuration + file will cause wisdom to be displayed red if it drops and green if it + rises: + + OPTION=hilite_status:wisdom/down/red/up/green + + Allowed colors are black, red, green, brown, blue, magenta, cyan, + gray, orange, light-green, yellow, light-blue, light-magenta, light- cyan, and white. And "no-color", the default foreground color on the display, which is not necessarily the same as black or white or any of the other colors. @@ -6319,16 +6329,6 @@ hallu, "movement" for lev, fly, and ride, and "all" for every condi- tion. - Allowed behaviors are "always", "up", "down", "changed", a percent- - age or absolute number threshold, or text to match against. For the - hitpoints field, the additional behavior "criticalhp" is available. - It overrides other behavior rules if hit points are at or below the - major problem threshold (which varies depending upon maximum hit - points and experience level). - - * "always" will set the default attributes for that field. - - NetHack 3.7.0 February 11, 2026 @@ -6340,6 +6340,15 @@ + Allowed behaviors are "always", "up", "down", "changed", a percent- + age or absolute number threshold, or text to match against. For the + hitpoints field, the additional behavior "criticalhp" is available. + It overrides other behavior rules if hit points are at or below the + major problem threshold (which varies depending upon maximum hit + points and experience level). + + * "always" will set the default attributes for that field. + * "up", "down" set the field attributes for when the field value changes upwards or downwards. This attribute times out after statushilites turns. @@ -6384,15 +6393,6 @@ met, a criticalhp rule takes precedence over all other hit- points rules. - * text match sets the attribute when the field value matches the - text. Text matches can only be used for "alignment", "carry- - ing-capacity", "hunger", "dungeon-level", and "title". For - title, only the role's rank title is tested; the character's - name is ignored. - - The in-game options menu can help you determine the correct syn- - tax for a configuration file. - @@ -6406,6 +6406,15 @@ + * text match sets the attribute when the field value matches the + text. Text matches can only be used for "alignment", "carry- + ing-capacity", "hunger", "dungeon-level", and "title". For + title, only the role's rank title is tested; the character's + name is ignored. + + The in-game options menu can help you determine the correct syn- + tax for a configuration file. + The whole feature can be disabled by setting option statushilites to 0. @@ -6450,15 +6459,6 @@ NetHack Symbols Symbol Name Description ----------------------------------------------------------------- - S_air (air) - _ S_altar (altar) - " S_amulet (amulet) - A S_angel (angelic being) - a S_ant (ant or other insect) - ^ S_anti_magic_trap (anti-magic field) - [ S_armor (suit or piece of armor) - [ S_armour (suit or piece of armor) - ^ S_arrow_trap (arrow trap) @@ -6472,6 +6472,15 @@ + S_air (air) + _ S_altar (altar) + " S_amulet (amulet) + A S_angel (angelic being) + a S_ant (ant or other insect) + ^ S_anti_magic_trap (anti-magic field) + [ S_armor (suit or piece of armor) + [ S_armour (suit or piece of armor) + ^ S_arrow_trap (arrow trap) 0 S_ball (iron ball) # S_bars (iron bars) B S_bat (bat or bird) @@ -6516,15 +6525,6 @@ - S_expl_bc (explosion bottom center) / S_expl_br (explosion bottom right) e S_eye (eye or sphere) - ^ S_falling_rock_trap (falling rock trap) - f S_feline (cat or other feline) - ^ S_fire_trap (fire trap) - ! S_flashbeam (flash beam) - % S_food (piece of food) - { S_fountain (fountain) - F S_fungus (fungus or mold) - * S_gem (gem or rock) - S_ghost (ghost) @@ -6538,6 +6538,15 @@ + ^ S_falling_rock_trap (falling rock trap) + f S_feline (cat or other feline) + ^ S_fire_trap (fire trap) + ! S_flashbeam (flash beam) + % S_food (piece of food) + { S_fountain (fountain) + F S_fungus (fungus or mold) + * S_gem (gem or rock) + S_ghost (ghost) H S_giant (giant humanoid) G S_gnome (gnome) ' S_golem (golem) @@ -6582,15 +6591,6 @@ p S_piercer (piercer) ^ S_pit (pit) # S_poisoncloud (poison cloud) - ^ S_polymorph_trap (polymorph trap) - } S_pool (water) - ! S_potion (potion) - P S_pudding (pudding or ooze) - q S_quadruped (quadruped) - Q S_quantmech (quantum mechanic) - = S_ring (ring) - ` S_rock (boulder or statue) - r S_rodent (rodent) @@ -6604,6 +6604,15 @@ + ^ S_polymorph_trap (polymorph trap) + } S_pool (water) + ! S_potion (potion) + P S_pudding (pudding or ooze) + q S_quadruped (quadruped) + Q S_quantmech (quantum mechanic) + = S_ring (ring) + ` S_rock (boulder or statue) + r S_rodent (rodent) ^ S_rolling_boulder_trap (rolling boulder trap) . S_room (floor of a room) / S_rslant (diagonal beam [zap animation]) @@ -6648,15 +6657,6 @@ S_unexplored (unexplored terrain) u S_unicorn (unicorn or horse) < S_upladder (ladder up) - < S_upstair (staircase up) - V S_vampire (vampire) - | S_vbeam (vertical beam [zap animation]) - # S_vcdbridge (vertical raised drawbridge) - + S_vcdoor (closed door in vertical wall) - . S_venom (splash of venom) - ^ S_vibrating_square (vibrating square) - . S_vodbridge (vertical lowered drawbridge) - - S_vodoor (open door in vertical wall) @@ -6670,6 +6670,15 @@ + < S_upstair (staircase up) + V S_vampire (vampire) + | S_vbeam (vertical beam [zap animation]) + # S_vcdbridge (vertical raised drawbridge) + + S_vcdoor (closed door in vertical wall) + . S_venom (splash of venom) + ^ S_vibrating_square (vibrating square) + . S_vodbridge (vertical lowered drawbridge) + - S_vodoor (open door in vertical wall) v S_vortex (vortex) | S_vwall (vertical wall) / S_wand (wand) @@ -6715,15 +6724,6 @@ The window port that is active needs to provide support for dis- playing UTF-8 character sequences and explicit red-green-blue colors - in order for the glyph representation to be visible. For example, the - following line in your configuration file will cause the glyph repre- - sentation for glyphid G_pool to use Unicode codepoint U+224B and the - color represented by R-G-B value 0-0-160: - - OPTIONS=glyph:G_pool/U+224B/0-0-160 - - The list of acceptable glyphid's can be produced by nethack --dumpg- - lyphids. Individual NetHack glyphs can be specified using the G_ pre- NetHack 3.7.0 February 11, 2026 @@ -6736,6 +6736,15 @@ + in order for the glyph representation to be visible. For example, the + following line in your configuration file will cause the glyph repre- + sentation for glyphid G_pool to use Unicode codepoint U+224B and the + color represented by R-G-B value 0-0-160: + + OPTIONS=glyph:G_pool/U+224B/0-0-160 + + The list of acceptable glyphid's can be produced by nethack --dumpg- + lyphids. Individual NetHack glyphs can be specified using the G_ pre- fix, or you can use an S_ symbol for a glyphid and store the custom representation for all NetHack glyphs that would map to that particu- lar symbol. @@ -6780,16 +6789,7 @@ menustyle:traditional This will assist in the interface to speech synthesizers. - nomenu_overlay - Show menus on a cleared screen and aligned to the left edge. - number_pad - A lot of speech access programs use the number-pad to review the - screen. If this is the case, disable the number_pad option and use - the traditional Rogue-like commands. - - paranoid_confirmation:swim - Prevent walking into water or lava. NetHack 3.7.0 February 11, 2026 @@ -6802,6 +6802,17 @@ + nomenu_overlay + Show menus on a cleared screen and aligned to the left edge. + + number_pad + A lot of speech access programs use the number-pad to review the + screen. If this is the case, disable the number_pad option and use + the traditional Rogue-like commands. + + paranoid_confirmation:swim + Prevent walking into water or lava. + accessiblemsg Adds direction or location information to messages. @@ -6843,6 +6854,20 @@ showdamage Give a message of damage taken and how many hit points are left. + + + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 105 + + + 9.18. Global Configuration for System Administrators If NetHack is compiled with the SYSCF option, a system adminis- @@ -6856,18 +6881,6 @@ WIZARDS = A space-separated list of user names who are allowed to play in debug mode (commonly referred to as wizard mode). A value of a single asterisk (*) allows anyone to start a game in debug - - - NetHack 3.7.0 February 11, 2026 - - - - - - NetHack Guidebook 105 - - - mode. SHELLERS = A list of users who are allowed to use the shell escape @@ -6909,6 +6922,18 @@ POINTSMIN = Minimum number of points to get an entry in the score file. + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 106 + + + PERS_IS_UID = 0 or 1 to use user names or numeric userids, respec- tively, to identify unique people for the score file. @@ -6922,18 +6947,6 @@ ity for players to set S_pet_override and S_hero_override symbols in their configuration file. - - - NetHack 3.7.0 February 11, 2026 - - - - - - NetHack Guidebook 106 - - - PORTABLE_DEVICE_PATHS = 0 or 1 Windows OS only, the game will look for all of its external files, and write to all of its output files in one place rather than at the standard locations. @@ -6975,6 +6988,18 @@ NetHack maintains a list of the top scores or scorers on your machine, depending on how it is set up. In the latter case, each + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 107 + + + account on the machine can post only one non-winning score on this list. If you score higher than someone else on this list, or better your previous score, you will be inserted in the proper place under @@ -6988,18 +7013,6 @@ only hear about 90% of your gold when your corpse is discovered (adventurers have been known to collect finder's fees). So, consider whether you want to take one last hit at that monster and possibly - - - NetHack 3.7.0 February 11, 2026 - - - - - - NetHack Guidebook 107 - - - live, or quit and stop with whatever you have. If you quit, you keep all your gold, but if you swing and live, you might find more. @@ -7040,6 +7053,19 @@ start a game in debug mode when not allowed or not available will result in falling back to explore mode instead. + + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 108 + + + 12. Credits The original hack game was modeled on the Berkeley UNIX rogue @@ -7054,18 +7080,6 @@ Jay Fenlason wrote the original Hack, with help from Kenny Wood- land, Mike Thome, and Jon Payne. - - - NetHack 3.7.0 February 11, 2026 - - - - - - NetHack Guidebook 108 - - - Andries Brouwer did a major re-write while at Stichting Mathema- tisch Centrum (now Centrum Wiskunde & Informatica), transforming Hack into a very different game. He published the Hack source code for use @@ -7106,6 +7120,18 @@ Olaf Seibert ported NetHack 2.3 and 3.0 to the Amiga. Norm Meluch, Stephen Spackman and Pierre Martineau designed overlay code for PC NetHack 3.0. Johnny Lee ported NetHack 3.0 to the Macintosh. + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 109 + + + Along with various other Dungeoneers, they continued to enhance the PC, Macintosh, and Amiga ports through the later revisions of 3.0. @@ -7120,18 +7146,6 @@ Janet Walz, the NetHack Development Team which now included Ken Arromdee, David Cohrs, Jean-Christophe Collet, Kevin Darcy, Matt Day, Timo Hakulinen, Steve Linhart, Dean Luick, Pat Rankin, Eric Raymond, - - - NetHack 3.7.0 February 11, 2026 - - - - - - NetHack Guidebook 109 - - - and Eric Smith undertook a radical revision of 3.0. They re-struc- tured the game's design, and re-wrote major parts of the code. They added multiple dungeons, a new display, special individual character @@ -7172,6 +7186,18 @@ just their classes. He contributed them to the NetHack Development Team which rechristened them "tiles", original usage which has subse- quently been picked up by various other games. NetHack's tiles sup- + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 110 + + + port was then implemented on other platforms (initially MS-DOS but eventually Windows, Qt, and X11 too). @@ -7186,18 +7212,6 @@ thirteen members of the original NetHack Development Team remained on the team at the start of work on that release. During the interval between the release of 3.1.3 and 3.2.0, one of the founding members of - - - NetHack 3.7.0 February 11, 2026 - - - - - - NetHack Guidebook 110 - - - the NetHack Development Team, Dr. Izchak Miller, was diagnosed with cancer and passed away. That release of the game was dedicated to him by the development and porting teams. @@ -7239,6 +7253,17 @@ retrieval of old character names to use for random ghost and statue names in the current game.) + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 111 + + + The 3.3 NetHack Development Team, consisting of Michael Allison, Ken Arromdee, David Cohrs, Jessie Collet, Steve Creps, Kevin Darcy, Timo Hakulinen, Kevin Hugo, Steve Linhart, Ken Lorber, Dean Luick, Pat @@ -7252,18 +7277,6 @@ and Ranger roles joined Archeologists, Barbarians, Cavemen, Healers, Knights, Priests, Rogues, Samurai, Tourists, Valkyries and of course, Wizards. It was also the first version to allow you to ride a steed, - - - NetHack 3.7.0 February 11, 2026 - - - - - - NetHack Guidebook 111 - - - and was the first version to have a publicly available web-site list- ing all the bugs that had been discovered. Despite that constantly growing bug list, 3.3 proved stable enough to last for more than a @@ -7304,6 +7317,19 @@ Christian "Marvin" Bressler maintained 3.4 for the Atari after he resurrected it for 3.3.1. + + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 112 + + + The release of NetHack 3.4.3 in December 2003 marked the begin- ning of a long release hiatus. 3.4.3 proved to be a remarkably stable version that provided continued enjoyment by the community for more @@ -7318,18 +7344,6 @@ In September 2014, an interim snapshot of the code under develop- ment was released publicly by other parties. Since that code was a - - - NetHack 3.7.0 February 11, 2026 - - - - - - NetHack Guidebook 112 - - - work-in-progress and had not gone through the process of debugging it as a suitable release, it was decided that the version numbers present on that code snapshot would be retired and never used in an official @@ -7369,6 +7383,19 @@ Kompel, Dion Nicolaas, Derek S. Ray and Yitzhak Sapir maintained the port of NetHack 3.6 for Microsoft Windows. + + + + NetHack 3.7.0 February 11, 2026 + + + + + + NetHack Guidebook 113 + + + Pat Rankin attempted to keep the VMS port running for NetHack 3.6, hindered by limited access. Kevin Smolkowski has updated and tested it for the most recent version of OpenVMS (V8.4 as of this @@ -7385,17 +7412,6 @@ Rankin, Derek S. Ray, Alex Smith, Mike Stephenson, Janet Walz, and Paul Winner. - - NetHack 3.7.0 February 11, 2026 - - - - - - NetHack Guidebook 113 - - - In early May 2019, another 320 bug fixes along with some enhance- ments and the adopted curses window port, were released as 3.6.2. @@ -7431,22 +7447,6 @@ - - - - - - - - - - - - - - - - From aafc414a9f548cc2fc5aae6ec3229301cd27934b Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 19 Mar 2026 15:55:08 -0700 Subject: [PATCH 315/442] remove inaccurate comment Floating eyes aren't the only monsters that pass the is_floater() test. Other 'e's aned 'y's do too. --- src/mon.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/mon.c b/src/mon.c index a8837d127..627e673bd 100644 --- a/src/mon.c +++ b/src/mon.c @@ -2146,8 +2146,6 @@ mfndpos( wantpool = (mdat->mlet == S_EEL); poolok = ((!Is_waterlevel(&u.uz) && m_in_air(mon)) || (is_swimmer(mdat) && !wantpool)); - /* note: floating eye is the only is_floater() so this could be - simplified, but then adding another floater would be error prone */ lavaok = (m_in_air(mon) || likes_lava(mdat)); if (mdat == &mons[PM_FLOATING_EYE]) /* prefers to avoid heat */ lavaok = FALSE; @@ -3477,10 +3475,10 @@ xkilled( iflags.sad_feeling = FALSE; mtmp->mhp = 0; /* caller will usually have already done this */ - if (!noconduct) /* KMH, conduct */ + if (!noconduct) { /* KMH, conduct */ if (!u.uconduct.killer++) livelog_printf(LL_CONDUCT, "killed for the first time"); - + } if (!nomsg) { boolean namedpet = has_mgivenname(mtmp) && !Hallucination; From e38377e89977d33552f29c187e49af8f3fb1cae8 Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 19 Mar 2026 16:29:11 -0700 Subject: [PATCH 316/442] some reformatting for read.c --- src/read.c | 57 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/src/read.c b/src/read.c index 2770ee4ed..b422ee082 100644 --- a/src/read.c +++ b/src/read.c @@ -1188,7 +1188,8 @@ seffect_enchant_armor(struct obj **sobjp) useup(otmp); return; } - if (s < -100) s = -100; /* avoid integer overflow with very negative armor */ + if (s < -100) + s = -100; /* avoid integer overflow with very negative armor */ /* Base power of the enchantment: @@ -1201,19 +1202,25 @@ seffect_enchant_armor(struct obj **sobjp) /* Elven/artifact and nonmagical armor is easier to enchant; blessed scrolls are more effective. */ - if (special_armor) ++s; - if (!objects[otmp->otyp].oc_magic) ++s; - if (sblessed) ++s; + if (special_armor) + ++s; + if (!objects[otmp->otyp].oc_magic) + ++s; + if (sblessed) + ++s; if (s <= 0) { s = 0; - if (otmp->spe > 0 && !rn2(otmp->spe)) s = 1; + if (otmp->spe > 0 && !rn2(otmp->spe)) + s = 1; } else { s = rnd(s); } - if (s > 11) s = 11; /* unlikely but possible: avoids an overflow later */ + if (s > 11) + s = 11; /* unlikely but possible: avoids an overflow later */ - if (scursed) s = -s; + if (scursed) + s = -s; if (s >= 0 && Is_dragon_scales(otmp)) { unsigned was_lit = otmp->lamplit; @@ -1286,16 +1293,24 @@ seffect_enchant_armor(struct obj **sobjp) staticfn boolean disintegrate_cursed_armor(void) { - struct obj *armors[7] = { NULL }; + struct obj *armors[10]; int idx = 0; - if (uarm && uarm->cursed) armors[idx++] = uarm; - if (uarmc && uarmc->cursed) armors[idx++] = uarmc; - if (uarmh && uarmh->cursed) armors[idx++] = uarmh; - if (uarms && uarms->cursed) armors[idx++] = uarms; - if (uarmg && uarmg->cursed) armors[idx++] = uarmg; - if (uarmf && uarmf->cursed) armors[idx++] = uarmf; - if (uarmu && uarmu->cursed) armors[idx++] = uarmu; + armors[0] = NULL; + if (uarm && uarm->cursed) + armors[idx++] = uarm; + if (uarmc && uarmc->cursed) + armors[idx++] = uarmc; + if (uarmh && uarmh->cursed) + armors[idx++] = uarmh; + if (uarms && uarms->cursed) + armors[idx++] = uarms; + if (uarmg && uarmg->cursed) + armors[idx++] = uarmg; + if (uarmf && uarmf->cursed) + armors[idx++] = uarmf; + if (uarmu && uarmu->cursed) + armors[idx++] = uarmu; if (!idx) return FALSE; @@ -1616,6 +1631,7 @@ seffect_enchant_weapon(struct obj **sobjp) boolean scursed = sobj->cursed; boolean confused = (Confusion != 0); boolean old_erodeproof, new_erodeproof; + int s; /* [What about twoweapon mode? Proofing/repairing/enchanting both would be too powerful, but shouldn't we choose randomly between @@ -1648,11 +1664,12 @@ seffect_enchant_weapon(struct obj **sobjp) uwep->oerodeproof = new_erodeproof ? 1 : 0; return; } - if (!chwepon(sobj, scursed ? -1 - : !uwep ? 1 - : (uwep->spe >= 9) ? !rn2(uwep->spe) - : sblessed ? rnd(3 - uwep->spe / 3) - : 1)) + s = scursed ? -1 + : !uwep ? 1 /* guard further tests against null pointer */ + : (uwep->spe >= 9) ? (rn2(uwep->spe) == 0) /* usually 0, maybe 1 */ + : sblessed ? rnd(3 - uwep->spe / 3) /* >= 9 case prevents rnd(0) */ + : 1; /* uncursed */ + if (!chwepon(sobj, s)) *sobjp = 0; /* nothing enchanted: strange_feeling -> useup */ if (uwep) cap_spe(uwep); From b33b09bcac22cd8a0ed055746c92657c5469d43c Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 19 Mar 2026 20:22:24 -0400 Subject: [PATCH 317/442] whitespace cleanup: tabs to spaces --- src/end.c | 2 +- src/files.c | 4 ++-- src/restore.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/end.c b/src/end.c index 1da474b39..6d8da53a7 100644 --- a/src/end.c +++ b/src/end.c @@ -1762,7 +1762,7 @@ save_killers(NHFILE *nhfp) if (update_file(nhfp)) { for (kptr = &svk.killer; kptr; kptr = kptr->next) { - Sfo_kinfo(nhfp, kptr, "kinfo"); + Sfo_kinfo(nhfp, kptr, "kinfo"); } } if (release_data(nhfp)) { diff --git a/src/files.c b/src/files.c index b8efef8d1..d2c81a4a9 100644 --- a/src/files.c +++ b/src/files.c @@ -1174,7 +1174,7 @@ create_savefile(void) if (nhfp->fd >= 0) (void) setmode(nhfp->fd, O_BINARY); #endif - } + } } #if defined(VMS) && !defined(SECURE) /* @@ -1218,7 +1218,7 @@ open_savefile(void) #ifdef SAVEFILE_DEBUGGING nhfp->fplog = fopen("open-savefile.log", "w"); #endif - } + } #ifdef MAC nhfp->fd = macopen(fq_save, O_RDONLY | O_BINARY, SAVE_TYPE); #else diff --git a/src/restore.c b/src/restore.c index 91ade76f2..191db7ddd 100644 --- a/src/restore.c +++ b/src/restore.c @@ -1097,7 +1097,7 @@ getlev(NHFILE *nhfp, int pid, xint8 lev) for (c = 0; c < COLNO; ++c) { for (r = 0; r < ROWNO; ++r) { Sfi_schar(nhfp, &svl.lastseentyp[c][r], "lastseentyp"); - } + } } Sfi_long(nhfp, &svo.omoves, "lev-timestmp"); elapsed = (svm.moves - svo.omoves); From 044a229467176a64cb2a69ba5c10ac167c46ed18 Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 19 Mar 2026 20:33:03 -0400 Subject: [PATCH 318/442] whitespace cleanup: tabs to spaces in sys --- sys/msdos/video.c | 20 ++++++++++---------- sys/windows/consoletty.c | 30 +++++++++++++++--------------- sys/windows/windsys.c | 4 ++-- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/sys/msdos/video.c b/sys/msdos/video.c index 916a3518d..57911edab 100644 --- a/sys/msdos/video.c +++ b/sys/msdos/video.c @@ -174,28 +174,28 @@ term_curs_set(int visibility) return; if (!visibility) { - if (!iflags.grmode) { + if (!iflags.grmode) { txt_hide_cursor(); #ifdef SCREEN_VGA - } else if (iflags.usevga) { - vga_hide_cursor(); + } else if (iflags.usevga) { + vga_hide_cursor(); #endif #ifdef SCREEN_VESA - } else if (iflags.usevesa) { - vesa_hide_cursor(); - } + } else if (iflags.usevesa) { + vesa_hide_cursor(); + } #endif } else if (visibility) { - if (!iflags.grmode) { + if (!iflags.grmode) { txt_show_cursor(); #ifdef SCREEN_VGA - } else if (iflags.usevga) { + } else if (iflags.usevga) { vga_show_cursor(); #endif #ifdef SCREEN_VESA - } else if (iflags.usevesa) { + } else if (iflags.usevesa) { vesa_show_cursor(); - } + } #endif } vis = visibility; diff --git a/sys/windows/consoletty.c b/sys/windows/consoletty.c index 10bc06c37..ed44f1271 100644 --- a/sys/windows/consoletty.c +++ b/sys/windows/consoletty.c @@ -98,22 +98,22 @@ cell_t undefined_cell = { CONSOLE_UNDEFINED_CHARACTER, CONSOLE_UNDEFINED_ATTRIBUTE }; #else /* VIRTUAL_TERMINAL_SEQUENCES */ cell_t clear_cell = { - { CONSOLE_CLEAR_CHARACTER, 0, 0, 0, 0, 0, 0 }, + { CONSOLE_CLEAR_CHARACTER, 0, 0, 0, 0, 0, 0 }, CONSOLE_CLEAR_CHARACTER, /* wcharacter */ - 0, /* attr */ - 0L, /* color24 */ - 0, /* color256idx */ - "\x1b[0m", /* bkcolorseq */ - 0 /* colorseq */ + 0, /* attr */ + 0L, /* color24 */ + 0, /* color256idx */ + "\x1b[0m", /* bkcolorseq */ + 0 /* colorseq */ }; cell_t undefined_cell = { - { CONSOLE_UNDEFINED_CHARACTER, 0, 0, 0, 0, 0, 0 }, + { CONSOLE_UNDEFINED_CHARACTER, 0, 0, 0, 0, 0, 0 }, CONSOLE_UNDEFINED_CHARACTER, /* wcharacter */ - 0, /* attr */ - 0L, /* color24 */ - 0, /* color256idx */ - (const char *) 0, /* bkcolorseq */ - (const char *) 0 /* colorseq */ + 0, /* attr */ + 0L, /* color24 */ + 0, /* color256idx */ + (const char *) 0, /* bkcolorseq */ + (const char *) 0 /* colorseq */ }; #if 0 static const uint8 empty_utf8str[MAX_UTF8_SEQUENCE] = { 0 }; @@ -1146,7 +1146,7 @@ CtrlHandler(DWORD ctrltype) case CTRL_BREAK_EVENT: term_clear_screen(); FALLTHROUGH; - /* FALLTHRU */ + /* FALLTHRU */ case CTRL_CLOSE_EVENT: case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: @@ -1308,8 +1308,8 @@ really_move_cursor(void) oldtitle[39] = '\0'; } Snprintf(newtitle, sizeof newtitle, - "%-55s tty=(%02d,%02d) consoletty=(%02d,%02d)", - oldtitle, + "%-55s tty=(%02d,%02d) consoletty=(%02d,%02d)", + oldtitle, ttyDisplay->curx, ttyDisplay->cury, console.cursor.X, console.cursor.Y); (void) SetConsoleTitle(newtitle); diff --git a/sys/windows/windsys.c b/sys/windows/windsys.c index d1327869f..88f1a69bf 100644 --- a/sys/windows/windsys.c +++ b/sys/windows/windsys.c @@ -1338,8 +1338,8 @@ printf("E2: M=%s e=%d\n",msg,errnum); int win32_cr_gettrace(int maxframes USED_IF_BACKTRACE, - char *out USED_IF_BACKTRACE, - int outsize USED_IF_BACKTRACE) + char *out USED_IF_BACKTRACE, + int outsize USED_IF_BACKTRACE) { #ifdef USE_BACKTRACE userstate.error_count = 0; From c595f241e667c2ce2f678b676cb2d470543a02a3 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Fri, 20 Mar 2026 17:28:27 +0200 Subject: [PATCH 319/442] Change key binds from array to linked list Key bindings were stored as a fixed-size array, indexed by the input character, pointing to the extended commands. This changes that into a linked list of an intermediary struct Cmd_bind, storing the input key and the pointer to the command. This is just code cleanup for future enhancements, and should have no effect on gameplay. --- include/decl.h | 1 + include/extern.h | 3 +- include/func_tab.h | 8 ++ include/hack.h | 2 +- src/cmd.c | 291 +++++++++++++++++++++++++++++++++------------ src/decl.c | 1 + src/options.c | 2 +- src/save.c | 1 + 8 files changed, 228 insertions(+), 81 deletions(-) diff --git a/include/decl.h b/include/decl.h index 1a048b007..a34550ef9 100644 --- a/include/decl.h +++ b/include/decl.h @@ -240,6 +240,7 @@ struct instance_globals_c { /* decl.c */ char chosen_windowtype[WINTYPELEN]; int cmd_key; /* parse() / rhack() */ + struct Cmd_bind *cmd_bind; cmdcount_nht command_count; /* some objects need special handling during destruction or placement */ struct obj *current_wand; /* wand currently zapped/applied */ diff --git a/include/extern.h b/include/extern.h index 28ec3fa4f..52358357e 100644 --- a/include/extern.h +++ b/include/extern.h @@ -370,6 +370,7 @@ extern void change_palette(void); /* ### cmd.c ### */ +extern void cmdbind_freeall(void); extern void set_move_cmd(int, int); extern int do_move_west(void); extern int do_move_northwest(void); @@ -447,7 +448,7 @@ extern int doextlist(void); extern int extcmd_via_menu(void); extern int enter_explore_mode(void); extern boolean bind_mousebtn(int, const char *); -extern boolean bind_key(uchar, const char *); +extern boolean bind_key(uchar, const char *, boolean); extern void dokeylist(void); extern int xytodir(int, int); extern void dirtocoord(coord *, int); diff --git a/include/func_tab.h b/include/func_tab.h index 2924b3bf3..b5d1f9704 100644 --- a/include/func_tab.h +++ b/include/func_tab.h @@ -29,6 +29,14 @@ #define ECM_EXACTMATCH 0x02 /* needs exact match of findstr */ #define ECM_NO1CHARCMD 0x04 /* ignore commands like '?' and '#' */ +/* a key bound to ext_func_tab */ +struct Cmd_bind { + uchar key; + boolean userbind; /* added by user */ + const struct ext_func_tab *cmd; + struct Cmd_bind *next; +}; + struct ext_func_tab { uchar key; const char *ef_txt, *ef_desc; diff --git a/include/hack.h b/include/hack.h index a52258a4e..0a025a902 100644 --- a/include/hack.h +++ b/include/hack.h @@ -252,7 +252,7 @@ struct cmd { boolean swap_yz; /* QWERTZ keyboards; use z to move NW, y to zap */ const char *dirchars; /* current movement/direction characters */ const char *alphadirchars; /* same as dirchars if !numpad */ - const struct ext_func_tab *commands[256]; /* indexed by input character */ + struct Cmd_bind *cmdbinds; const struct ext_func_tab *mousebtn[NUM_MOUSE_BUTTONS]; char spkeys[NUM_NHKF]; char extcmd_char; /* key that starts an extended command ('#') */ diff --git a/src/cmd.c b/src/cmd.c index 0969f0222..20866f8db 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -94,6 +94,10 @@ extern int dozap(void); /**/ extern int doorganize(void); /**/ #endif /* DUMB */ +staticfn struct Cmd_bind *cmdbind_get(uchar); +staticfn void cmdbind_add(uchar, const struct ext_func_tab *, boolean); +staticfn void cmdbind_remove(uchar); +staticfn void cmdbind_swapkeys(uchar, uchar); staticfn int dosuspend_core(void); staticfn int dosh_core(void); staticfn int doherecmdmenu(void); @@ -2078,17 +2082,119 @@ extcmds_getentry(int i) return &extcmdlist[i]; } +/* get the command bound to a key */ +staticfn struct Cmd_bind * +cmdbind_get(uchar key) +{ + struct Cmd_bind *bind = gc.Cmd.cmdbinds; + + if (!key) + return NULL; + + while (bind) { + if (bind->key == key) + return bind; + bind = bind->next; + } + return bind; +} + +staticfn void +cmdbind_add(uchar key, const struct ext_func_tab *extcmd, boolean user) +{ + struct Cmd_bind *bind = cmdbind_get(key); + + if (!key) + return; + if (!extcmd && bind) { + cmdbind_remove(key); + return; + } + + /* binding exists, set it to this command */ + if (bind) { + bind->cmd = extcmd; + bind->userbind = user; + return; + } else { + bind = (struct Cmd_bind *) alloc(sizeof(struct Cmd_bind)); + bind->key = key; + bind->userbind = user; + bind->cmd = extcmd; + bind->next = gc.Cmd.cmdbinds; + gc.Cmd.cmdbinds = bind; + } +} + +staticfn void +cmdbind_remove(uchar key) +{ + struct Cmd_bind *bind = gc.Cmd.cmdbinds; + struct Cmd_bind *prev = (struct Cmd_bind *) 0; + + while (bind) { + if (bind->key == key) { + if (prev) + prev->next = bind->next; + else + gc.Cmd.cmdbinds = bind->next; + free(bind); + return; + } + prev = bind; + bind = bind->next; + } +} + +void +cmdbind_freeall(void) +{ + struct Cmd_bind *next; + + while (gc.Cmd.cmdbinds) { + next = gc.Cmd.cmdbinds->next; + free(gc.Cmd.cmdbinds); + gc.Cmd.cmdbinds = next; + } +} + +/* swap key bindings for key1 and key2. both bindings must exist. */ +staticfn void +cmdbind_swapkeys(uchar key1, uchar key2) +{ + struct Cmd_bind *bind1 = cmdbind_get(key1); + struct Cmd_bind *bind2 = cmdbind_get(key2); + + if (bind1 && bind2) { + bind1->key = key2; + bind2->key = key1; + } +} + /* return number of extended commands bound to a non-default key */ int count_bind_keys(void) { - int nbinds = 0; - int i; + struct Cmd_bind *bind = gc.Cmd.cmdbinds; + int i, nbinds = 0; + uchar keys[256]; - for (i = 0; i < extcmdlist_length; i++) - if (extcmdlist[i].key - && gc.Cmd.commands[extcmdlist[i].key] != &extcmdlist[i]) + (void) memset(keys, 0, sizeof(uchar) * 256); + + /* commands bound to different key */ + while (bind) { + keys[bind->key] = 1; + if (bind->userbind && bind->cmd && bind->cmd->key != bind->key) { nbinds++; + } + bind = bind->next; + } + + /* commands which should be bound to a key, but aren't */ + for (i = 0; i < extcmdlist_length; i++) + if (extcmdlist[i].key && !keys[extcmdlist[i].key]) + nbinds++; + return nbinds; } @@ -2100,16 +2206,35 @@ get_changed_key_binds(strbuf_t *sbuf) int i; char buf[BUFSZ]; char buf2[QBUFSZ]; + struct Cmd_bind *bind = gc.Cmd.cmdbinds; + uchar keys[256]; + + (void) memset(keys, 0, sizeof(uchar) * 256); if (!sbuf) win = create_nhwindow(NHW_TEXT); + + /* commands bound to different key */ + while (bind) { + keys[bind->key] = 1; + if (bind->userbind && bind->cmd && bind->cmd->key != bind->key) { + Sprintf(buf, "BIND=%s:%s%s", key2txt(bind->key, buf2), + bind->cmd->ef_txt, + sbuf ? "\n" : ""); + if (sbuf) + strbuf_append(sbuf, buf); + else + putstr(win, 0, buf); + } + bind = bind->next; + } + + /* commands which should be bound to a key, but aren't */ for (i = 0; i < extcmdlist_length; i++) { struct ext_func_tab *ec = &extcmdlist[i]; - if (ec->key && gc.Cmd.commands[ec->key] - && gc.Cmd.commands[ec->key] != ec) { - Sprintf(buf, "BIND=%s:%s%s", key2txt(ec->key, buf2), - gc.Cmd.commands[ec->key]->ef_txt, + if (ec->key && !keys[ec->key]) { + Sprintf(buf, "BIND=%s:nothing%s", key2txt(ec->key, buf2), sbuf ? "\n" : ""); if (sbuf) strbuf_append(sbuf, buf); @@ -2136,6 +2261,7 @@ handler_rebind_keys_add(boolean keyfirst) char buf2[QBUFSZ]; uchar key = '\0'; int clr = NO_COLOR; + struct Cmd_bind *bind; if (keyfirst) { pline("Bind which key? "); @@ -2150,9 +2276,10 @@ handler_rebind_keys_add(boolean keyfirst) any = cg.zeroany; if (key) { - if (gc.Cmd.commands[key]) { + bind = cmdbind_get(key); + if (bind) { Sprintf(buf, "Key '%s' is currently bound to \"%s\".", - key2txt(key, buf2), gc.Cmd.commands[key]->ef_txt); + key2txt(key, buf2), bind->cmd->ef_txt); } else { Sprintf(buf, "Key '%s' is not bound to anything.", key2txt(key, buf2)); @@ -2187,7 +2314,7 @@ handler_rebind_keys_add(boolean keyfirst) npick = select_menu(win, PICK_ONE, &picks); destroy_nhwindow(win); if (npick > 0) { - const struct ext_func_tab *prevec; + struct Cmd_bind *prevcmd; const char *cmdstr; i = picks->item.a_int; @@ -2210,13 +2337,13 @@ handler_rebind_keys_add(boolean keyfirst) return; } - prevec = gc.Cmd.commands[key]; + prevcmd = cmdbind_get(key); - if (bind_key(key, cmdstr)) { - if (prevec && prevec != ec) { + if (bind_key(key, cmdstr, TRUE)) { + if (prevcmd && prevcmd->cmd != ec) { pline("Changed key '%s' from \"%s\" to \"%s\".", - key2txt(key, buf2), prevec->ef_txt, cmdstr); - } else if (!prevec) { + key2txt(key, buf2), prevcmd->cmd->ef_txt, cmdstr); + } else if (!prevcmd) { pline("Bound key '%s' to \"%s\".", key2txt(key, buf2), cmdstr); } @@ -2386,6 +2513,7 @@ key2extcmddesc(uchar key) const char *txt; int k, i, j; uchar M_5 = (uchar) M('5'), M_0 = (uchar) M('0'); + struct Cmd_bind *cmdbind; /* need to check for movement commands before checking the extended commands table because it contains entries for number_pad commands @@ -2418,8 +2546,9 @@ key2extcmddesc(uchar key) return misc_keys[i].desc; } /* finally, check whether 'key' is a command */ - if (gc.Cmd.commands[key] && (txt = gc.Cmd.commands[key]->ef_txt) != 0) { - Sprintf(key2cmdbuf, "%s (#%s)", gc.Cmd.commands[key]->ef_desc, txt); + if ((cmdbind = cmdbind_get(key)) != 0 + && (txt = cmdbind->cmd->ef_txt) != 0) { + Sprintf(key2cmdbuf, "%s (#%s)", cmdbind->cmd->ef_desc, txt); /* special case: for reqmenu prefix (normally 'm'), replace "prefix: request menu or modify command (#reqmenu)" @@ -2478,13 +2607,13 @@ bind_mousebtn(int btn, const char *command) } boolean -bind_key(uchar key, const char *command) +bind_key(uchar key, const char *command, boolean user) { struct ext_func_tab *extcmd; /* special case: "nothing" is reserved for unbinding */ if (!strcmpi(command, "nothing")) { - gc.Cmd.commands[key] = (struct ext_func_tab *) 0; + cmdbind_remove(key); return TRUE; } @@ -2493,7 +2622,7 @@ bind_key(uchar key, const char *command) continue; if ((extcmd->flags & INTERNALCMD) != 0) continue; - gc.Cmd.commands[key] = extcmd; + cmdbind_add(key, extcmd, user); #if 0 /* silently accept key binding for unavailable command (!SHELL,&c) */ if ((extcmd->flags & CMD_NOT_AVAILABLE) != 0) { char buf[BUFSZ]; @@ -2519,7 +2648,7 @@ bind_key_fn(uchar key, int (*fn)(void)) continue; if ((extcmd->flags & INTERNALCMD) != 0) continue; - gc.Cmd.commands[key] = extcmd; + cmdbind_add(key, extcmd, FALSE); return TRUE; } @@ -2534,31 +2663,31 @@ commands_init(void) for (extcmd = extcmdlist; extcmd->ef_txt; extcmd++) if (extcmd->key) - gc.Cmd.commands[extcmd->key] = extcmd; + cmdbind_add(extcmd->key, extcmd, FALSE); (void) bind_mousebtn(1, "therecmdmenu"); (void) bind_mousebtn(2, "clicklook"); /* number_pad */ - (void) bind_key(C('l'), "redraw"); - (void) bind_key('h', "help"); - (void) bind_key('j', "jump"); - (void) bind_key('k', "kick"); - (void) bind_key('l', "loot"); - (void) bind_key(C('n'), "annotate"); - (void) bind_key('N', "name"); - (void) bind_key('u', "untrap"); - (void) bind_key('5', "run"); - (void) bind_key(M('5'), "rush"); - (void) bind_key('-', "fight"); + (void) bind_key(C('l'), "redraw", FALSE); + (void) bind_key('h', "help", FALSE); + (void) bind_key('j', "jump", FALSE); + (void) bind_key('k', "kick", FALSE); + (void) bind_key('l', "loot", FALSE); + (void) bind_key(C('n'), "annotate", FALSE); + (void) bind_key('N', "name", FALSE); + (void) bind_key('u', "untrap", FALSE); + (void) bind_key('5', "run", FALSE); + (void) bind_key(M('5'), "rush", FALSE); + (void) bind_key('-', "fight", FALSE); /* alt keys: */ - (void) bind_key(M('O'), "overview"); - (void) bind_key(M('2'), "twoweapon"); - (void) bind_key(M('N'), "name"); + (void) bind_key(M('O'), "overview", FALSE); + (void) bind_key(M('2'), "twoweapon", FALSE); + (void) bind_key(M('N'), "name", FALSE); #if 0 /* don't do this until the rest_on_space option is set or cleared */ - (void) bind_key(' ', "wait"); + (void) bind_key(' ', "wait", FALSE); #endif } @@ -2567,12 +2696,13 @@ keylist_func_has_key(const struct ext_func_tab *extcmd, boolean *skip_keys_used) /* boolean keys_used[256] */ { int i; + struct Cmd_bind *bind; for (i = 0; i < 256; ++i) { if (skip_keys_used[i]) continue; - if (gc.Cmd.commands[i] == extcmd) + if (((bind = cmdbind_get(i)) != 0) && (bind->cmd == extcmd)) return TRUE; } return FALSE; @@ -2588,6 +2718,7 @@ keylist_putcmds(winid datawin, boolean docount, char buf[BUFSZ], buf2[QBUFSZ]; boolean keys_already_used[256]; /* copy of keys_used[] before updates */ int count = 0; + struct Cmd_bind *bind; for (i = 0; i < 256; i++) { uchar key = (uchar) i; @@ -2597,16 +2728,17 @@ keylist_putcmds(winid datawin, boolean docount, continue; if (key == ' ' && !flags.rest_on_space) continue; - if ((extcmd = gc.Cmd.commands[i]) != (struct ext_func_tab *) 0) { - if ((incl_flags && !(extcmd->flags & incl_flags)) - || (excl_flags && (extcmd->flags & excl_flags))) + bind = cmdbind_get(key); + if (bind && bind->cmd != (struct ext_func_tab *) 0) { + if ((incl_flags && !(bind->cmd->flags & incl_flags)) + || (excl_flags && (bind->cmd->flags & excl_flags))) continue; if (docount) { count++; continue; } Sprintf(buf, "%-7s %-13s %s", key2txt(key, buf2), - extcmd->ef_txt, extcmd->ef_desc); + bind->cmd->ef_txt, bind->cmd->ef_desc); putstr(datawin, 0, buf); keys_used[i] = TRUE; } @@ -2810,9 +2942,10 @@ cmd_from_func(int (*fn)(void)) { int i; char ret = '\0'; + struct Cmd_bind *bind; - /* skip NUL; allowing it would wreak havoc */ - for (i = 1; i < 256; ++i) { + for (bind = gc.Cmd.cmdbinds; bind; bind = bind->next) { + i = bind->key; /* skip space; we'll use it below as last resort if no other keystroke invokes space's command */ if (i == ' ') @@ -2823,7 +2956,7 @@ cmd_from_func(int (*fn)(void)) && !gc.Cmd.num_pad) continue; - if (gc.Cmd.commands[i] && gc.Cmd.commands[i]->ef_funct == fn) { + if (bind->cmd && bind->cmd->ef_funct == fn) { if (i >= ' ' && i <= '~') return (char) i; else { @@ -2831,7 +2964,7 @@ cmd_from_func(int (*fn)(void)) } } } - if (gc.Cmd.commands[' '] && gc.Cmd.commands[' ']->ef_funct == fn) + if ((bind = cmdbind_get(' ')) != 0 && bind->cmd->ef_funct == fn) return ' '; return ret; } @@ -3124,7 +3257,6 @@ reset_commands(boolean initial) static struct ext_func_tab *back_dir_cmd[N_DIRS][N_MOVEMODES]; static uchar back_dir_key[N_DIRS][N_MOVEMODES]; static boolean backed_dir_cmd = FALSE; - const struct ext_func_tab *cmdtmp; boolean flagtemp; int c, i, updated = 0; int dir, mode; @@ -3140,8 +3272,7 @@ reset_commands(boolean initial) if (backed_dir_cmd) { for (dir = 0; dir < N_DIRS; dir++) { for (mode = 0; mode < N_MOVEMODES; mode++) { - gc.Cmd.commands[back_dir_key[dir][mode]] - = back_dir_cmd[dir][mode]; + cmdbind_add(back_dir_key[dir][mode], back_dir_cmd[dir][mode], FALSE); } } } @@ -3163,9 +3294,7 @@ reset_commands(boolean initial) perform the swap (or reverse previous one) */ for (i = 0; i < SIZE(ylist); i++) { c = ylist[i] & 0xff; - cmdtmp = gc.Cmd.commands[c]; /* tmp = [y] */ - gc.Cmd.commands[c] = gc.Cmd.commands[c + 1]; /* [y] = [z] */ - gc.Cmd.commands[c + 1] = cmdtmp; /* [z] = tmp */ + cmdbind_swapkeys(c, c + 1); } } /* MSDOS compatibility mode (only applicable for num_pad) */ @@ -3182,8 +3311,10 @@ reset_commands(boolean initial) #endif /* FIXME: NHKF_DOINV2 ought to be implemented instead of this */ c = M('0') & 0xff; - gc.Cmd.commands[c] = gc.Cmd.pcHack_compat ? gc.Cmd.commands['I'] - : 0; + if (gc.Cmd.pcHack_compat) + cmdbind_add(c, ext_func_tab_from_func(dotypeinv), FALSE); + else + cmdbind_remove(c); } /* phone keypad layout (only applicable for num_pad) */ flagtemp = (iflags.num_pad_mode & 2) ? gc.Cmd.num_pad : FALSE; @@ -3193,13 +3324,9 @@ reset_commands(boolean initial) /* phone_layout has been toggled */ for (i = 0; i < 3; i++) { c = '1' + i; /* 1,2,3 <-> 7,8,9 */ - cmdtmp = gc.Cmd.commands[c]; /* tmp = [1] */ - gc.Cmd.commands[c] = gc.Cmd.commands[c + 6]; /* [1] = [7] */ - gc.Cmd.commands[c + 6] = cmdtmp; /* [7] = tmp */ + cmdbind_swapkeys(c, c + 6); c = (M('1') & 0xff) + i; /* M-1,M-2,M-3 <-> M-7,M-8,M-9 */ - cmdtmp = gc.Cmd.commands[c]; /* tmp = [M-1] */ - gc.Cmd.commands[c] = gc.Cmd.commands[c + 6]; /* [M-1]=[M-7] */ - gc.Cmd.commands[c + 6] = cmdtmp; /* [M-7] = tmp */ + cmdbind_swapkeys(c, c + 6); } } } /*?initial*/ @@ -3216,6 +3343,7 @@ reset_commands(boolean initial) for (dir = 0; dir < N_DIRS; dir++) { for (mode = MV_WALK; mode < N_MOVEMODES; mode++) { uchar di = (uchar) gc.Cmd.dirchars[dir]; + struct Cmd_bind *bind; if (!gc.Cmd.num_pad) { if (mode == MV_RUN) di = highc(di); @@ -3225,9 +3353,11 @@ reset_commands(boolean initial) else if (mode == MV_RUSH) di = M(di); } back_dir_key[dir][mode] = di; - back_dir_cmd[dir][mode] - = (struct ext_func_tab *) gc.Cmd.commands[di]; - gc.Cmd.commands[di] = (struct ext_func_tab *) 0; + if ((bind = cmdbind_get(di)) != 0) + back_dir_cmd[dir][mode] = (struct ext_func_tab *) bind->cmd; + else + back_dir_cmd[dir][mode] = (struct ext_func_tab *) 0; + cmdbind_remove(di); } } backed_dir_cmd = TRUE; @@ -3265,15 +3395,15 @@ update_rest_on_space(void) donull, (IFBURIED | CMD_M_PREFIX), "waiting" }; static const struct ext_func_tab *unrestonspace = 0; - const struct ext_func_tab *bound_f = gc.Cmd.commands[' ']; + struct Cmd_bind *bind = cmdbind_get(' '); /* when 'rest_on_space' is On, will run the #wait command; when it is Off, will use 'unrestonspace' which will either be Null and elicit "Unknown command ' '." or have some non-Null command bound in player's RC file */ - if (bound_f != 0 && bound_f != &restonspace) - unrestonspace = bound_f; - gc.Cmd.commands[' '] = flags.rest_on_space ? &restonspace : unrestonspace; + if (bind && bind->cmd != &restonspace) + unrestonspace = bind->cmd; + cmdbind_add(' ', flags.rest_on_space ? &restonspace : unrestonspace, FALSE); } /* commands which accept 'm' prefix to request menu operation or other @@ -3450,11 +3580,13 @@ rhack(int key) const struct ext_func_tab *tlist; int res; + gc.cmd_bind = cmdbind_get(key & 0xFF); + do_cmdq_extcmd: if (cmdq_ec) tlist = cmdq_ec; else - tlist = gc.Cmd.commands[key & 0xff]; + tlist = gc.cmd_bind ? gc.cmd_bind->cmd : NULL; /* current - use key to directly index cmdlist array */ if (tlist != 0) { @@ -3640,9 +3772,10 @@ int movecmd(char sym, int mode) { int d = DIR_ERR; + struct Cmd_bind *bind = cmdbind_get(sym); - if (gc.Cmd.commands[(uchar) sym]) { - int (*fnc)(void) = gc.Cmd.commands[(uchar) sym]->ef_funct; + if (bind) { + int (*fnc)(void) = bind->cmd->ef_funct; if (mode == MV_ANY) { for (d = N_DIRS_Z - 1; d > DIR_ERR; d--) @@ -3681,9 +3814,9 @@ boolean redraw_cmd(char c) { uchar uc = (uchar) c; - const struct ext_func_tab *cmd = gc.Cmd.commands[uc]; + struct Cmd_bind *bind = cmdbind_get(uc); - return (boolean) (cmd && cmd->ef_funct == doredraw); + return (boolean) (bind && bind->cmd->ef_funct == doredraw); } /* @@ -4865,6 +4998,7 @@ staticfn int parse(void) { int foo; + struct Cmd_bind *bind; iflags.in_parse = TRUE; gc.command_count = 0; @@ -4894,13 +5028,14 @@ parse(void) gl.last_command_count = 0; } else if (gi.in_doagain) { gc.command_count = gl.last_command_count; - } else if (foo && gc.Cmd.commands[foo & 0xff] + } else if (foo && (bind = cmdbind_get(foo & 0xFF)) != 0 /* these shouldn't go into the do-again buffer */ - && (gc.Cmd.commands[foo & 0xff]->ef_funct == do_repeat - || gc.Cmd.commands[foo & 0xff]->ef_funct == doprev_message + && bind && bind->cmd + && (bind->cmd->ef_funct == do_repeat + || bind->cmd->ef_funct == doprev_message /* this one might get put into the do-again buffer but only if the interface code tells the core to do it */ - || gc.Cmd.commands[foo & 0xff]->ef_funct == doextcmd)) { + || bind->cmd->ef_funct == doextcmd)) { /* gc.command_count will be set again when we re-enter with gi.in_doagain set true */ gc.command_count = gl.last_command_count; diff --git a/src/decl.c b/src/decl.c index e6fd152f0..cc1e8a3b6 100644 --- a/src/decl.c +++ b/src/decl.c @@ -247,6 +247,7 @@ static const struct instance_globals_c g_init_c = { /* decl.c */ UNDEFINED_VALUES, /* chosen_windowtype */ 0, /* cmd_key */ + NULL, /* cmd_bind */ 0L, /* command_count */ UNDEFINED_PTR, /* current_wand */ #ifdef DEF_PAGER diff --git a/src/options.c b/src/options.c index d63db5ba7..51faccca8 100644 --- a/src/options.c +++ b/src/options.c @@ -7682,7 +7682,7 @@ parsebindings(char *bindings) } /* extended command? */ - if (!bind_key(key, bind)) { + if (!bind_key(key, bind, TRUE)) { config_error_add("Unknown key binding command '%s'", bind); return FALSE; } diff --git a/src/save.c b/src/save.c index 57c8bfb41..461ed10a1 100644 --- a/src/save.c +++ b/src/save.c @@ -1109,6 +1109,7 @@ freedynamicdata(void) freeroleoptvals(); /* saveoptvals(&tnhfp) */ cmdq_clear(CQ_CANNED); cmdq_clear(CQ_REPEAT); + cmdbind_freeall(); free_tutorial(); /* (only needed if quitting while in tutorial) */ /* per-turn data, but might get added to when freeing other stuff */ From f85b5b1c9d6625b7aa1c43a050c399527148d8dd Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Fri, 20 Mar 2026 22:49:26 +0200 Subject: [PATCH 320/442] Fix segfaults from the key binds change Should've checked rest_on_space behaviour ... --- src/cmd.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/cmd.c b/src/cmd.c index 20866f8db..b10a017ba 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -2547,6 +2547,7 @@ key2extcmddesc(uchar key) } /* finally, check whether 'key' is a command */ if ((cmdbind = cmdbind_get(key)) != 0 + && cmdbind->cmd && (txt = cmdbind->cmd->ef_txt) != 0) { Sprintf(key2cmdbuf, "%s (#%s)", cmdbind->cmd->ef_desc, txt); @@ -2964,7 +2965,8 @@ cmd_from_func(int (*fn)(void)) } } } - if ((bind = cmdbind_get(' ')) != 0 && bind->cmd->ef_funct == fn) + if ((bind = cmdbind_get(' ')) != 0 && bind->cmd + && bind->cmd->ef_funct == fn) return ' '; return ret; } @@ -3774,7 +3776,7 @@ movecmd(char sym, int mode) int d = DIR_ERR; struct Cmd_bind *bind = cmdbind_get(sym); - if (bind) { + if (bind && bind->cmd) { int (*fnc)(void) = bind->cmd->ef_funct; if (mode == MV_ANY) { @@ -3816,7 +3818,8 @@ redraw_cmd(char c) uchar uc = (uchar) c; struct Cmd_bind *bind = cmdbind_get(uc); - return (boolean) (bind && bind->cmd->ef_funct == doredraw); + return (boolean) (bind && bind->cmd + && bind->cmd->ef_funct == doredraw); } /* From 26fd5e7d73eba0d56493ef2c0351772d5d76229a Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Sat, 21 Mar 2026 04:52:51 +0000 Subject: [PATCH 321/442] Armor probability tweaks Armor is now slightly more likely to generate outside Gehennom (at the expense of gems that generate via random generation rather than mineralisation or level rules). Basic nonmagical armor (especially body armor) has had its generation probabilities reduced *relative to other armor*, but outside Gehennom, it is no less likely to generate (because armor in general is now more likely to generate outside Gehennom). It is slightly less likely to generate in Gehennom (but isn't typically needed in quantity there). Armor that has extrinsics is more likely to generate, both due to having increased probability relative to other armor, and due to the increased proportion of generated items being armor: this is the primary goal of this change. The intention behind this change is to increase the chance that players naturally find useful armor (especially armor that they might not have been planning to use, but that they can adapt their strategy to make use of), rather than needing to wish for it: the chance of finding useful armor is higher both in the Dungeons (due to the increased probability) and in Gehennom (because it is more biased towards armor that might be useful at that stage of the game). In practice, in 3.6.x (prior to this change and to wishing changes), it was quite common for players to wish up an entire set of armor at the Castle, ignoring almost everything they'd found so far that game; I'm hoping this change encourages more wish variety rather than spending the majority of wishes on armor. --- include/objects.h | 72 +++++++++++++++++++++++------------------------ src/mkobj.c | 4 +-- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/include/objects.h b/include/objects.h index faa630a50..41076db28 100644 --- a/include/objects.h +++ b/include/objects.h @@ -455,12 +455,12 @@ HELM("fedora", NoDes, 1, 0, 0, 0, 0, 3, 1, 10, 0, CLOTH, CLR_BROWN, FEDORA), HELM("cornuthaum", "conical hat", - 0, 1, CLAIRVOYANT, 3, 1, 4, 80, 10, 1, CLOTH, CLR_BLUE, + 0, 1, CLAIRVOYANT, 5, 1, 4, 80, 10, 1, CLOTH, CLR_BLUE, /* name coined by devteam; confers clairvoyance for wizards, blocks clairvoyance if worn by role other than wizard */ CORNUTHAUM), HELM("dunce cap", "conical hat", - 0, 1, 0, 3, 1, 4, 1, 10, 0, CLOTH, CLR_BLUE, + 0, 1, 0, 5, 1, 4, 1, 10, 0, CLOTH, CLR_BLUE, /* sets Int and Wis to fixed value of 6, so actually provides protection against death caused by Int being drained below 3 */ DUNCE_CAP), @@ -468,7 +468,7 @@ HELM("dented pot", NoDes, 1, 0, 0, 2, 0, 10, 8, 9, 0, IRON, CLR_BLACK, DENTED_POT), HELM("helm of brilliance", "crystal helmet", - 0, 1, 0, 3, 1, 40, 50, 9, 0, GLASS, CLR_WHITE, + 0, 1, 0, 6, 1, 40, 50, 9, 0, GLASS, CLR_WHITE, /* used to be iron and shuffled as "etched helmet" but required special case for the effect of iron armor on spell casting */ HELM_OF_BRILLIANCE), @@ -477,13 +477,13 @@ HELM("helmet", "plumed helmet", 0, 0, 0, 10, 1, 30, 10, 9, 0, IRON, HI_METAL, HELMET), HELM("helm of caution", "etched helmet", - 0, 1, WARNING, 3, 1, 50, 50, 9, 0, IRON, CLR_GREEN, + 0, 1, WARNING, 6, 1, 50, 50, 9, 0, IRON, CLR_GREEN, HELM_OF_CAUTION), HELM("helm of opposite alignment", "crested helmet", - 0, 1, 0, 6, 1, 50, 50, 9, 0, IRON, HI_METAL, + 0, 1, 0, 10, 1, 50, 50, 9, 0, IRON, HI_METAL, HELM_OF_OPPOSITE_ALIGNMENT), HELM("helm of telepathy", "visored helmet", - 0, 1, TELEPAT, 2, 1, 50, 50, 9, 0, IRON, HI_METAL, + 0, 1, TELEPAT, 4, 1, 50, 50, 9, 0, IRON, HI_METAL, HELM_OF_TELEPATHY), /* suits of armor */ @@ -554,19 +554,19 @@ DRGN_ARMR("yellow dragon scales", 0, ACID_RES, 500, 7, CLR_YELLOW, #undef DRGN_ARMR /* other suits */ ARMOR("plate mail", NoDes, - 1, 0, 1, 0, 44, 5, 450, 600, 3, 2, ARM_SUIT, IRON, HI_METAL, + 1, 0, 1, 0, 40, 5, 450, 600, 3, 2, ARM_SUIT, IRON, HI_METAL, PLATE_MAIL), ARMOR("crystal plate mail", NoDes, 1, 0, 1, 0, 10, 5, 415, 820, 3, 2, ARM_SUIT, GLASS, CLR_WHITE, CRYSTAL_PLATE_MAIL), ARMOR("bronze plate mail", NoDes, - 1, 0, 1, 0, 25, 5, 450, 400, 4, 1, ARM_SUIT, COPPER, HI_COPPER, + 1, 0, 1, 0, 23, 5, 450, 400, 4, 1, ARM_SUIT, COPPER, HI_COPPER, BRONZE_PLATE_MAIL), ARMOR("splint mail", NoDes, - 1, 0, 1, 0, 62, 5, 400, 80, 4, 1, ARM_SUIT, IRON, HI_METAL, + 1, 0, 1, 0, 57, 5, 400, 80, 4, 1, ARM_SUIT, IRON, HI_METAL, SPLINT_MAIL), ARMOR("banded mail", NoDes, - 1, 0, 1, 0, 72, 5, 350, 90, 4, 1, ARM_SUIT, IRON, HI_METAL, + 1, 0, 1, 0, 66, 5, 350, 90, 4, 1, ARM_SUIT, IRON, HI_METAL, BANDED_MAIL), ARMOR("dwarvish mithril-coat", NoDes, 1, 0, 0, 0, 10, 1, 150, 240, 4, 2, ARM_SUIT, MITHRIL, HI_SILVER, @@ -575,28 +575,28 @@ ARMOR("elven mithril-coat", NoDes, 1, 0, 0, 0, 15, 1, 150, 240, 5, 2, ARM_SUIT, MITHRIL, HI_SILVER, ELVEN_MITHRIL_COAT), ARMOR("chain mail", NoDes, - 1, 0, 0, 0, 72, 5, 300, 75, 5, 1, ARM_SUIT, IRON, HI_METAL, + 1, 0, 0, 0, 66, 5, 300, 75, 5, 1, ARM_SUIT, IRON, HI_METAL, CHAIN_MAIL), ARMOR("orcish chain mail", "crude chain mail", - 0, 0, 0, 0, 20, 5, 300, 75, 6, 1, ARM_SUIT, IRON, CLR_BLACK, + 0, 0, 0, 0, 19, 5, 300, 75, 6, 1, ARM_SUIT, IRON, CLR_BLACK, ORCISH_CHAIN_MAIL), ARMOR("scale mail", NoDes, - 1, 0, 0, 0, 72, 5, 250, 45, 6, 1, ARM_SUIT, IRON, HI_METAL, + 1, 0, 0, 0, 66, 5, 250, 45, 6, 1, ARM_SUIT, IRON, HI_METAL, SCALE_MAIL), ARMOR("studded leather armor", NoDes, - 1, 0, 0, 0, 72, 3, 200, 15, 7, 1, ARM_SUIT, LEATHER, HI_LEATHER, + 1, 0, 0, 0, 66, 3, 200, 15, 7, 1, ARM_SUIT, LEATHER, HI_LEATHER, STUDDED_LEATHER_ARMOR), ARMOR("ring mail", NoDes, - 1, 0, 0, 0, 72, 5, 250, 100, 7, 1, ARM_SUIT, IRON, HI_METAL, + 1, 0, 0, 0, 66, 5, 250, 100, 7, 1, ARM_SUIT, IRON, HI_METAL, RING_MAIL), ARMOR("orcish ring mail", "crude ring mail", - 0, 0, 0, 0, 20, 5, 250, 80, 8, 1, ARM_SUIT, IRON, CLR_BLACK, + 0, 0, 0, 0, 19, 5, 250, 80, 8, 1, ARM_SUIT, IRON, CLR_BLACK, ORCISH_RING_MAIL), ARMOR("leather armor", NoDes, - 1, 0, 0, 0, 82, 3, 150, 5, 8, 1, ARM_SUIT, LEATHER, HI_LEATHER, + 1, 0, 0, 0, 75, 3, 150, 5, 8, 1, ARM_SUIT, LEATHER, HI_LEATHER, LEATHER_ARMOR), ARMOR("leather jacket", NoDes, - 1, 0, 0, 0, 12, 0, 30, 10, 9, 0, ARM_SUIT, LEATHER, CLR_BLACK, + 1, 0, 0, 0, 11, 0, 30, 10, 9, 0, ARM_SUIT, LEATHER, CLR_BLACK, LEATHER_JACKET), /* shirts */ @@ -624,58 +624,58 @@ CLOAK("oilskin cloak", "slippery cloak", 0, 0, 0, 8, 0, 10, 50, 9, 2, CLOTH, HI_CLOTH, OILSKIN_CLOAK), CLOAK("robe", NoDes, - 1, 1, 0, 3, 0, 15, 50, 8, 2, CLOTH, CLR_RED, ROBE), + 1, 1, 0, 6, 0, 15, 50, 8, 2, CLOTH, CLR_RED, ROBE), /* robe was adopted from slash'em, where it's worn as a suit rather than as a cloak and there are several variations */ CLOAK("alchemy smock", "apron", - 0, 1, POISON_RES, 9, 0, 10, 50, 9, 1, CLOTH, CLR_WHITE, + 0, 1, POISON_RES, 11, 0, 10, 50, 9, 1, CLOTH, CLR_WHITE, ALCHEMY_SMOCK), CLOAK("leather cloak", NoDes, 1, 0, 0, 8, 0, 15, 40, 9, 1, LEATHER, CLR_BROWN, LEATHER_CLOAK), /* with shuffled appearances... */ CLOAK("cloak of protection", "tattered cape", - 0, 1, PROTECTION, 9, 0, 10, 50, 7, 3, CLOTH, HI_CLOTH, + 0, 1, PROTECTION, 11, 0, 10, 50, 7, 3, CLOTH, HI_CLOTH, CLOAK_OF_PROTECTION), /* cloak of protection is now the only item conferring MC 3 */ CLOAK("cloak of invisibility", "opera cloak", - 0, 1, INVIS, 10, 0, 10, 60, 9, 1, CLOTH, CLR_BRIGHT_MAGENTA, + 0, 1, INVIS, 12, 0, 10, 60, 9, 1, CLOTH, CLR_BRIGHT_MAGENTA, CLOAK_OF_INVISIBILITY), CLOAK("cloak of magic resistance", "ornamental cope", - 0, 1, ANTIMAGIC, 2, 0, 10, 60, 9, 1, CLOTH, CLR_WHITE, + 0, 1, ANTIMAGIC, 6, 0, 10, 60, 9, 1, CLOTH, CLR_WHITE, CLOAK_OF_MAGIC_RESISTANCE), /* 'cope' is not a spelling mistake... leave it be */ CLOAK("cloak of displacement", "piece of cloth", - 0, 1, DISPLACED, 10, 0, 10, 50, 9, 1, CLOTH, HI_CLOTH, + 0, 1, DISPLACED, 12, 0, 10, 50, 9, 1, CLOTH, HI_CLOTH, CLOAK_OF_DISPLACEMENT), /* shields */ SHIELD("small shield", "wooden shield", - 0, 0, 0, 0, 4, 0, 30, 3, 9, 0, WOOD, HI_WOOD, + 0, 0, 0, 0, 6, 0, 30, 3, 9, 0, WOOD, HI_WOOD, SMALL_SHIELD), SHIELD("shield of drain resistance", "wooden shield", - 0, 1, 0, DRAIN_RES, 3, 0, 30, 50, 9, 0, WOOD, HI_WOOD, + 0, 1, 0, DRAIN_RES, 12, 0, 30, 50, 9, 0, WOOD, HI_WOOD, SHIELD_OF_DRAIN_RESISTANCE), SHIELD("shield of shock resistance", "wooden shield", - 0, 1, 0, SHOCK_RES, 3, 0, 30, 50, 9, 0, WOOD, HI_WOOD, + 0, 1, 0, SHOCK_RES, 12, 0, 30, 50, 9, 0, WOOD, HI_WOOD, SHIELD_OF_SHOCK_RESISTANCE), SHIELD("elven shield", "blue and green shield", - 0, 0, 0, 0, 2, 0, 40, 7, 8, 0, WOOD, CLR_GREEN, + 0, 0, 0, 0, 2, 0, 40, 7, 8, 0, WOOD, CLR_GREEN, ELVEN_SHIELD), SHIELD("Uruk-hai shield", "white-handed shield", - 0, 0, 0, 0, 2, 0, 50, 7, 9, 0, IRON, HI_METAL, + 0, 0, 0, 0, 2, 0, 50, 7, 9, 0, IRON, HI_METAL, URUK_HAI_SHIELD), SHIELD("orcish shield", "red-eyed shield", - 0, 0, 0, 0, 2, 0, 50, 7, 9, 0, IRON, CLR_RED, + 0, 0, 0, 0, 2, 0, 50, 7, 9, 0, IRON, CLR_RED, ORCISH_SHIELD), SHIELD("large shield", NoDes, - 1, 0, 1, 0, 4, 0, 100, 10, 8, 0, IRON, HI_METAL, + 1, 0, 1, 0, 4, 0, 100, 10, 8, 0, IRON, HI_METAL, LARGE_SHIELD), SHIELD("dwarvish roundshield", "large round shield", - 0, 0, 0, 0, 3, 0, 100, 10, 8, 0, IRON, HI_METAL, + 0, 0, 0, 0, 3, 0, 100, 10, 8, 0, IRON, HI_METAL, DWARVISH_ROUNDSHIELD), SHIELD("shield of reflection", "polished silver shield", - 0, 1, 0, REFLECTING, 3, 0, 50, 50, 8, 0, SILVER, HI_SILVER, + 0, 1, 0, REFLECTING, 7, 0, 50, 50, 8, 0, SILVER, HI_SILVER, SHIELD_OF_REFLECTION), /* gloves */ @@ -684,7 +684,7 @@ SHIELD("shield of reflection", "polished silver shield", * HI_METAL or CLR_BLACK. All have shuffled descriptions. */ GLOVES("leather gloves", "old gloves", - 0, 0, 0, 16, 1, 10, 8, 9, 0, LEATHER, HI_LEATHER, + 0, 0, 0, 15, 1, 10, 8, 9, 0, LEATHER, HI_LEATHER, LEATHER_GLOVES), GLOVES("gauntlets of fumbling", "padded gloves", 0, 1, FUMBLING, 8, 1, 10, 50, 9, 0, LEATHER, HI_LEATHER, @@ -698,11 +698,11 @@ GLOVES("gauntlets of dexterity", "fencing gloves", /* boots */ BOOTS("low boots", "walking shoes", - 0, 0, 0, 25, 2, 10, 8, 9, 0, LEATHER, HI_LEATHER, LOW_BOOTS), + 0, 0, 0, 23, 2, 10, 8, 9, 0, LEATHER, HI_LEATHER, LOW_BOOTS), BOOTS("iron shoes", "hard shoes", 0, 0, 0, 7, 2, 50, 16, 8, 0, IRON, HI_METAL, IRON_SHOES), BOOTS("high boots", "jackboots", - 0, 0, 0, 15, 2, 20, 12, 8, 0, LEATHER, HI_LEATHER, HIGH_BOOTS), + 0, 0, 0, 14, 2, 20, 12, 8, 0, LEATHER, HI_LEATHER, HIGH_BOOTS), /* with shuffled appearances... */ BOOTS("speed boots", "combat boots", 0, 1, FAST, 12, 2, 20, 50, 9, 0, LEATHER, HI_LEATHER, SPEED_BOOTS), diff --git a/src/mkobj.c b/src/mkobj.c index ab0f98fb3..eb55a0802 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -34,10 +34,10 @@ struct icp { }; static const struct icp mkobjprobs[] = { { 10, WEAPON_CLASS }, - { 10, ARMOR_CLASS }, + { 11, ARMOR_CLASS }, { 20, FOOD_CLASS }, { 8, TOOL_CLASS }, - { 8, GEM_CLASS }, + { 7, GEM_CLASS }, { 16, POTION_CLASS }, { 16, SCROLL_CLASS }, { 4, SPBOOK_CLASS }, From 62a50413d35d42cd446d7228baee25fac97a3ab7 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Sat, 21 Mar 2026 05:05:48 +0000 Subject: [PATCH 322/442] New debug command #wizobjprobs for listing item probabilities As of the change to allow for item probabilities that don't add up to 1000, it's become a little difficult to figure out the exact probabilities from the source code, which makes it hard to balance item generation. Adding a tool to list the probabilities helps. Part of the problem is that changing an item's probability without balancing it elsewhere is usually wrong: doing that would in effect take the probability equally from (or add the probability equally to) all other items in the class, which might break the balancing of those items due to the probability change. As such, it is usually better to make an intentional decision about which items should be less and which items should be more likely to generate, then change them in a balancing way (meaning that the probabilities of objects that weren't intentionally changed remain unchanged). Doing a complex such change makes arithmetic errors fairly likely, though, so it's useful to have a command that verifies that it's been done correctly. This command is primarily intended as a development aid, so it's included only in debug builds and pre-release builds (the same as other similar commands like #wizmondiff). --- dat/wizhelp | 1 + include/extern.h | 1 + src/cmd.c | 4 ++++ src/wizcmds.c | 42 ++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/dat/wizhelp b/dat/wizhelp index 88e1b3417..1945214b0 100644 --- a/dat/wizhelp +++ b/dat/wizhelp @@ -30,6 +30,7 @@ Debug-Mode Quick Reference: #wizloadlua == load and execute a lua script #wizmakemap == recreate the current dungeon level #wizmondiff == [Opt] check for discrepancies in monster difficulty ratings +#wizobjprobs == [Opt] list actual probabilities of item generation #wizrumorcheck == validate rumor indexing; also show first, second, and last random engravings, epitaphs, and hallucinatory monsters #wizseenv == show map locations' seen vectors diff --git a/include/extern.h b/include/extern.h index 52358357e..998e7698c 100644 --- a/include/extern.h +++ b/include/extern.h @@ -3854,6 +3854,7 @@ extern void wizcustom_callback(winid win, int glyphnum, char *id); #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) extern int wiz_display_macros(void); extern int wiz_mon_diff(void); +extern int wiz_objprobs(void); #endif extern void sanity_check(void); diff --git a/src/cmd.c b/src/cmd.c index b10a017ba..b9cd4c799 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -1956,6 +1956,10 @@ struct ext_func_tab extcmdlist[] = { wiz_load_splua, IFBURIED | WIZMODECMD | NOFUZZERCMD, NULL }, { '\0', "wizloadlua", "load and execute a lua script", wiz_load_lua, IFBURIED | WIZMODECMD | NOFUZZERCMD, NULL }, +#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) + { '\0', "wizobjprobs", "list object generation probabilities", + wiz_objprobs, IFBURIED | WIZMODECMD, NULL }, +#endif { '\0', "wizmakemap", "recreate the current level", wiz_makemap, IFBURIED | WIZMODECMD, NULL }, { C('f'), "wizmap", "map the level", diff --git a/src/wizcmds.c b/src/wizcmds.c index 31cb338cc..b045578b8 100644 --- a/src/wizcmds.c +++ b/src/wizcmds.c @@ -1776,9 +1776,7 @@ wiz_display_macros(void) destroy_nhwindow(win); return ECMD_OK; } -#endif /* (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) */ -#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) /* the #wizmondiff command */ int wiz_mon_diff(void) @@ -1820,6 +1818,46 @@ wiz_mon_diff(void) destroy_nhwindow(win); return ECMD_OK; } + +/* the #wizobjprobs command */ +int +wiz_objprobs(void) +{ + int win; + char buf[BUFSZ]; + int probsum[MAXOCLASSES]; + int otyp; + int oclass = objects[FIRST_OBJECT].oc_class; + memset(probsum, 0, sizeof probsum); + + for (otyp = FIRST_OBJECT; otyp < NUM_OBJECTS; otyp++) { + probsum[(int) objects[otyp].oc_class] += objects[otyp].oc_prob; + } + + win = create_nhwindow(NHW_TEXT); + for (otyp = FIRST_OBJECT; otyp < NUM_OBJECTS; otyp++) { + /* placeholders for extra descriptions aren't generatable objects */ + if (!OBJ_NAME(objects[otyp])) + continue; + + if ((int) objects[otyp].oc_class != oclass) { + putstr(win, 0, ""); + } + oclass = objects[otyp].oc_class; + + Snprintf(buf, sizeof buf, "%4d / %4d (%6.2f%%): %s", + objects[otyp].oc_prob, + probsum[oclass], + (float) objects[otyp].oc_prob * 100.f / + (float) probsum[oclass], + OBJ_NAME(objects[otyp])); + putstr(win, 0, buf); + } + display_nhwindow(win, FALSE); + destroy_nhwindow(win); + + return ECMD_OK; +} #endif /* (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) */ /* #migratemons command */ From 63a78edcfa50062980dd8cd4c3fba1c7ba2efe84 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sat, 21 Mar 2026 11:52:36 +0200 Subject: [PATCH 323/442] Add toggle extended command Allows the user to configure a key binding to toggle any boolean option, for example: BIND=':toggle(price_quotes) BIND=v:toggle(autodig) The option must be settable in-game. --- doc/Guidebook.mn | 12 +++++ doc/Guidebook.tex | 15 +++++++ doc/fixes3-7-0.txt | 1 + include/extern.h | 2 + include/func_tab.h | 2 + src/cmd.c | 106 +++++++++++++++++++++++++++++++++++++++++---- src/options.c | 41 ++++++++++++++++-- 7 files changed, 167 insertions(+), 12 deletions(-) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 9fd2c8d05..b948331b0 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -1754,6 +1754,18 @@ floor container menu. .lp "" Autocompletes. Default key is \(oqM-T\(cq. +.lp "#toggle " +Toggle a boolean option on or off. +Requires a parameter in parenthesis, the name of the option to toggle. +The option must be settable in-game. +.lp "" +For example: +.sd +.ft CR +BIND=':toggle(price_quotes) +BIND=@:toggle(autopickup) +.ft +.ed .lp "#travel " Travel to a specific location on the map. Default key is \(oq_\(cq. \" underscore diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 62fc927a4..1c7841ae6 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -1826,6 +1826,21 @@ floor container menu. \\ %.lp "" Autocompletes. Default key is `\texttt{M-T}'. +%.lp +\item[\#toggle] +Toggle a boolean option on or off. +Requires a parameter in parenthesis, the name of the option to toggle. +The option must be settable in-game. + +%.lp "" +For example: +%.sd +\begin{verbatim} + BIND=':toggle(price_quotes) + BIND=@:toggle(autopickup) +\end{verbatim} +%.ed + %.lp \item[\#travel] Travel to a specific location on the map. diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index d5b973c6f..c314d079c 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -2912,6 +2912,7 @@ the game now automatically tracks which sell prices and buy prices you have new shields: shield of shock resistance, shield of drain resistance new wand: wand of stasis early-game monsters always miss with their first use of an attack wand +new command #toggle which can be used to toggle a boolean option Platform- and/or Interface-Specific New Features diff --git a/include/extern.h b/include/extern.h index 998e7698c..39e70fb0b 100644 --- a/include/extern.h +++ b/include/extern.h @@ -371,6 +371,7 @@ extern void change_palette(void); /* ### cmd.c ### */ extern void cmdbind_freeall(void); +extern int dotoggleoption(void); extern void set_move_cmd(int, int); extern int do_move_west(void); extern int do_move_northwest(void); @@ -2285,6 +2286,7 @@ extern char *get_option_value(const char *, boolean) NONNULLARG1; extern int doset_simple(void); extern int doset(void); extern int dotogglepickup(void); +extern int toggle_bool_option(const char *); extern void option_help(void); extern void all_options_strbuf(strbuf_t *) NONNULLARG1; extern void next_opt(winid, const char *) NONNULLARG2; diff --git a/include/func_tab.h b/include/func_tab.h index b5d1f9704..e5438ce58 100644 --- a/include/func_tab.h +++ b/include/func_tab.h @@ -22,6 +22,7 @@ #define MOUSECMD 0x0800 /* cmd allowed to be bound to mouse button */ #define CMD_INSANE 0x1000 /* suppress sanity check (for ^P and ^R) */ #define AUTOCOMP_ADJ 0x2000 /* user changed command autocompletion */ +#define CMD_PARAM 0x4000 /* command requires a param from key bind */ /* flags for extcmds_match() */ #define ECM_NOFLAGS 0 @@ -33,6 +34,7 @@ struct Cmd_bind { uchar key; boolean userbind; /* added by user */ + char *param; const struct ext_func_tab *cmd; struct Cmd_bind *next; }; diff --git a/src/cmd.c b/src/cmd.c index b9cd4c799..46200881f 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -1368,6 +1368,21 @@ dolookaround(void) return ECMD_OK; } +/* #toggle extended command + + BIND=':toggle(price_quotes) + BIND=@:toggle(autopickup) */ +int +dotoggleoption(void) +{ + if (gc.cmd_bind && gc.cmd_bind->param) { + return toggle_bool_option(gc.cmd_bind->param); + } else { + pline("Use #optionsfull to set any option instead."); + return ECMD_OK; + } +} + void set_move_cmd(int dir, int run) { @@ -1889,6 +1904,8 @@ struct ext_func_tab extcmdlist[] = { wiz_timeout_queue, IFBURIED | AUTOCOMPLETE | WIZMODECMD, NULL }, { M('T'), "tip", "empty a container", dotip, AUTOCOMPLETE | CMD_M_PREFIX, NULL }, + { '\0', "toggle", "toggle boolean option", + dotoggleoption, IFBURIED | GENERALCMD | CMD_PARAM, NULL }, { '_', "travel", "travel to a specific location on the map", dotravel, CMD_M_PREFIX, NULL }, { M('t'), "turn", "turn undead away", @@ -2119,11 +2136,16 @@ cmdbind_add(uchar key, const struct ext_func_tab *extcmd, boolean user) if (bind) { bind->cmd = extcmd; bind->userbind = user; + if (bind->param) { + free(bind->param); + bind->param = NULL; + } return; } else { bind = (struct Cmd_bind *) alloc(sizeof(struct Cmd_bind)); bind->key = key; bind->userbind = user; + bind->param = NULL; bind->cmd = extcmd; bind->next = gc.Cmd.cmdbinds; gc.Cmd.cmdbinds = bind; @@ -2142,6 +2164,8 @@ cmdbind_remove(uchar key) prev->next = bind->next; else gc.Cmd.cmdbinds = bind->next; + if (bind->param) + free(bind->param); free(bind); return; } @@ -2157,6 +2181,8 @@ cmdbind_freeall(void) while (gc.Cmd.cmdbinds) { next = gc.Cmd.cmdbinds->next; + if (gc.Cmd.cmdbinds->param) + free(gc.Cmd.cmdbinds->param); free(gc.Cmd.cmdbinds); gc.Cmd.cmdbinds = next; } @@ -2222,9 +2248,15 @@ get_changed_key_binds(strbuf_t *sbuf) while (bind) { keys[bind->key] = 1; if (bind->userbind && bind->cmd && bind->cmd->key != bind->key) { - Sprintf(buf, "BIND=%s:%s%s", key2txt(bind->key, buf2), - bind->cmd->ef_txt, - sbuf ? "\n" : ""); + if ((bind->cmd->flags & CMD_PARAM) != 0) + Sprintf(buf, "BIND=%s:%s(%s)%s", key2txt(bind->key, buf2), + bind->cmd->ef_txt, + bind->param, + sbuf ? "\n" : ""); + else + Sprintf(buf, "BIND=%s:%s%s", key2txt(bind->key, buf2), + bind->cmd->ef_txt, + sbuf ? "\n" : ""); if (sbuf) strbuf_append(sbuf, buf); else @@ -2319,18 +2351,31 @@ handler_rebind_keys_add(boolean keyfirst) destroy_nhwindow(win); if (npick > 0) { struct Cmd_bind *prevcmd; - const char *cmdstr; + char cmdstr[BUFSZ]; i = picks->item.a_int; free((genericptr_t) picks); if (i == -1) { ec = NULL; - cmdstr = "nothing"; + Strcat(cmdstr, "nothing"); goto bindit; } else { ec = &extcmdlist[i-1]; - cmdstr = ec->ef_txt; + + if ((ec->flags & CMD_PARAM) != 0) { + char parambuf[BUFSZ]; + char querybuf[BUFSZ]; + + parambuf[0] = '\0'; + Sprintf(querybuf, "Command %s requires a parameter:", ec->ef_txt); + getlin(querybuf, parambuf); + (void) mungspaces(parambuf); + Snprintf(cmdstr, BUFSZ-1, "%s(%s)", ec->ef_txt, parambuf); + cmdstr[BUFSZ-1] = '\0'; + } else { + Strcat(cmdstr, ec->ef_txt); + } } bindit: if (!key) { @@ -2615,6 +2660,8 @@ boolean bind_key(uchar key, const char *command, boolean user) { struct ext_func_tab *extcmd; + long len; + char *buf, *p = NULL, *lastp = NULL; /* special case: "nothing" is reserved for unbinding */ if (!strcmpi(command, "nothing")) { @@ -2622,12 +2669,46 @@ bind_key(uchar key, const char *command, boolean user) return TRUE; } + /* copy command to buf for modification */ + len = strlen(command) + 1; + buf = (char *)alloc(len); + (void) strncpy(buf, command, len); + + /* does buf have a parameter in parenthesis? */ + if ((p = strchr(buf, '(')) != 0 + && (lastp = strrchr(buf, ')')) != 0 + && (lastp > p)) { + *p = '\0'; + *lastp = '\0'; + /* p points to the parameter */ + p++; + } + for (extcmd = extcmdlist; extcmd->ef_txt; extcmd++) { - if (strcmpi(command, extcmd->ef_txt)) + if (strcmpi(buf, extcmd->ef_txt)) continue; if ((extcmd->flags & INTERNALCMD) != 0) continue; cmdbind_add(key, extcmd, user); + + if ((extcmd->flags & CMD_PARAM) != 0) { + if (!p) { + config_error_add("'%s' requires a parameter", buf); + } else { + struct Cmd_bind *bind = cmdbind_get(key); + int maxlen = min(30, strlen(p)) + 1; + + if (maxlen <= 1) { + config_error_add("Required parameter cannot be empty"); + } else { + bind->param = (char *) alloc(maxlen); + (void) strncpy(bind->param, p, maxlen); + bind->param[maxlen-1] = '\0'; + } + } + } else if (p && strlen(p) > 0) + config_error_add("'%s' does not take a parameter", buf); + #if 0 /* silently accept key binding for unavailable command (!SHELL,&c) */ if ((extcmd->flags & CMD_NOT_AVAILABLE) != 0) { char buf[BUFSZ]; @@ -2636,9 +2717,11 @@ bind_key(uchar key, const char *command, boolean user) config_error_add("%s", buf); } #endif + free(buf); return TRUE; } + free(buf); return FALSE; } @@ -2742,8 +2825,13 @@ keylist_putcmds(winid datawin, boolean docount, count++; continue; } - Sprintf(buf, "%-7s %-13s %s", key2txt(key, buf2), - bind->cmd->ef_txt, bind->cmd->ef_desc); + if ((bind->cmd->flags & CMD_PARAM) != 0) + Sprintf(buf, "%-7s %-13s %s \"%s\"", key2txt(key, buf2), + bind->cmd->ef_txt, bind->cmd->ef_desc, + bind->param); + else + Sprintf(buf, "%-7s %-13s %s", key2txt(key, buf2), + bind->cmd->ef_txt, bind->cmd->ef_desc); putstr(datawin, 0, buf); keys_used[i] = TRUE; } diff --git a/src/options.c b/src/options.c index 51faccca8..6f5a565ae 100644 --- a/src/options.c +++ b/src/options.c @@ -390,6 +390,7 @@ staticfn boolean parse_role_opt(int, boolean, const char *, char *, char **); staticfn char *get_cnf_role_opt(int); staticfn unsigned int longest_option_name(int, int); staticfn int doset_simple_menu(void); +staticfn void reset_needed_visuals(void); staticfn void doset_add_menu(winid, const char *, const char *, int, int); staticfn int handle_add_list_remove(const char *, int); staticfn void all_options_conds(strbuf_t *); @@ -9012,6 +9013,15 @@ doset(void) /* changing options via menu by Per Liboriussen */ goto rerun; } + reset_needed_visuals(); + return ECMD_OK; +} + +#undef HELP_IDX + +staticfn void +reset_needed_visuals(void) +{ if (go.opt_need_glyph_reset) { reset_glyphmap(gm_optionchange); } @@ -9039,11 +9049,13 @@ doset(void) /* changing options via menu by Per Liboriussen */ if (disp.botl || disp.botlx) { bot(); } - return ECMD_OK; + go.opt_need_redraw = FALSE; + go.opt_need_glyph_reset = FALSE; + go.opt_reset_customcolors = FALSE; + go.opt_reset_customsymbols = FALSE; + go.opt_update_basic_palette = FALSE; } -#undef HELP_IDX - /* doset(#optionsfull command) menu entries for compound options */ staticfn void doset_add_menu( @@ -9304,6 +9316,29 @@ dotogglepickup(void) return ECMD_OK; } +/* toggle any (settable in-game) boolean option by name */ +int +toggle_bool_option(const char *p) +{ + int i; + int ret = ECMD_FAIL; + + for (i = 0; i < OPTCOUNT; i++) + if (!strncmpi(allopt[i].name, p, strlen(p)) + && allopt[i].opttyp == BoolOpt + && allopt[i].setwhere == set_in_game + && allopt[i].addr != 0) { + char buf[BUFSZ]; + + Sprintf(buf, "%s%s", *allopt[i].addr ? "!" : "", allopt[i].name); + if (parseoptions(buf, FALSE, FALSE)) + ret = ECMD_OK; + + reset_needed_visuals(); + } + return ret; +} + int add_autopickup_exception(const char *mapping) { From 9fc15142533e348e37d345d80556b30cc808d311 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sat, 21 Mar 2026 13:18:26 +0200 Subject: [PATCH 324/442] Amulet of magical breathing increases energy regen Normally energy regen is 1d2 or 1d3, so wearing the amulet increases that to 1d4 or 1d5. The only way to get energy regeneration is via The Eye of the Aethiopica, so you cannot have both at the same time. --- doc/fixes3-7-0.txt | 1 + src/allmain.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index c314d079c..30abe683d 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1585,6 +1585,7 @@ give experience if opening Schroedinger's Box causes death of the cat inside priest donation amounts are explicitly stated, randomized slightly, based on peak rather than current level, and allow for bulk donations (buying larger amounts of clairvoyance/protection) if you have a lot of gold +amulet of magical breathing increases power regeneration Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/allmain.c b/src/allmain.c index 2db536e5e..0b7dd4cab 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -607,6 +607,9 @@ regen_pw(int wtcap) / 6)))) || Energy_regeneration)) { int upper = (int) (ACURR(A_WIS) + ACURR(A_INT)) / 15 + 1; + if (EMagical_breathing) + upper += 2; + u.uen += rn1(upper, 1); if (u.uen > u.uenmax) u.uen = u.uenmax; From 1eadfa962db78025aabf129fd503183a5806d854 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 21 Mar 2026 11:11:28 -0400 Subject: [PATCH 325/442] don't include size_t * and time_t * in _Generic for now There are behavior differences with macOS and other plaforms that would need to be addressed if they were included. --- src/sfbase.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sfbase.c b/src/sfbase.c index 0922839d1..0cd70af4e 100644 --- a/src/sfbase.c +++ b/src/sfbase.c @@ -98,8 +98,6 @@ void sf_log(NHFILE *, const char *, size_t, int, char *); #define Sfvalue_uint16(a) sfvalue(a) #define Sfvalue_uint32(a) sfvalue(a) #define Sfvalue_uint64(a) sfvalue(a) -#define Sfvalue_size_t(a) sfvalue(a) -#define Sfvalue_time_t(a) sfvalue(a) #define Sfvalue_short(a) sfvalue(a) #define Sfvalue_ushort(a) sfvalue(a) #define Sfvalue_int(a) sfvalue(a) @@ -115,6 +113,8 @@ void sf_log(NHFILE *, const char *, size_t, int, char *); #define Sfvalue_boolean(a) sfvalue_boolean(a) #define Sfvalue_schar(a) sfvalue_schar(a) #define Sfvalue_bitfield(a) sfvalue_bitfield(a) +#define Sfvalue_time_t(a) sfvalue_time_t(a) +#define Sfvalue_size_t(a) sfvalue_size_t(a) #define SF_A(dtyp) \ void sfo_##dtyp(NHFILE *nhfp, dtyp *d_##dtyp, const char *myname) \ From 8e4c7f9fb525c3f413653cc46580f8dbc1e7ee3d Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sat, 21 Mar 2026 17:54:16 +0200 Subject: [PATCH 326/442] Change some command keys Change 'v' from #versionshort to #chronicle. Change 'V' from #history to #versionshort. History can still be accessed either directly with the extended command, or via the help menu. Versionshort now accepts the m-prefix, and then shows the longer version. --- dat/hh | 4 ++-- doc/Guidebook.mn | 4 ++-- doc/Guidebook.tex | 6 +++--- doc/fixes3-7-0.txt | 2 ++ src/cmd.c | 8 ++++---- src/version.c | 3 +++ 6 files changed, 16 insertions(+), 11 deletions(-) diff --git a/dat/hh b/dat/hh index 951e91460..847ca02b7 100644 --- a/dat/hh +++ b/dat/hh @@ -34,8 +34,8 @@ O options set options / what-is tell what a map symbol represents \ known display list of what's been discovered | perminv interact with persistent inventory window instead of hero+map -v version display version number -V history display game history +v chronicle display a list of important events +V version display version number ^A again redo the previous command ^R redraw redraw the screen ^P prevmsg repeat previous message (consecutive ^P's repeat earlier ones) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index b948331b0..22256c17c 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -1282,6 +1282,7 @@ Talk to someone. Default key is \(oqM-c\(cq. .lp "#chronicle" Show a list of important game events. +Default key is \(oqv\(cq. .lp "#close " Close a door. Default key is \(oqc\(cq. @@ -1382,7 +1383,6 @@ option is On, clicking on the hero (or steed when mounted) will execute this command. .lp "#history " Show long version and game history. -Default key is \(oqV\(cq. .lp #inventory Show your inventory. Default key is \(oqi\(cq. @@ -1835,7 +1835,7 @@ Default key is \(oqM-v\(cq. .lp #versionshort Show the program's version number, plus the date and time that the running copy was built from sources (not the version's release date). -Default key is \(oqv\(cq. +Default key is \(oqV\(cq. .lp "#vision " Show vision array. Autocompletes. diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 1c7841ae6..be5da967b 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -1321,7 +1321,7 @@ Cast a spell. Default key is `\texttt{Z}'. Talk to someone. Default key is `\texttt{M-c}'. %.lp \item[\#chronicle] -Show a list of important game events. +Show a list of important game events. Default key is `\texttt{v}'. %.lp \item[\#close] Close a door. Default key is `\texttt{c}'. @@ -1425,7 +1425,7 @@ option is On, clicking on the hero (or steed when mounted) will execute this command. %.lp \item[\#history] -Show long version and game history. Default key is `\texttt{V}'. +Show long version and game history. %.lp \item[\#inventory] Show your inventory. Default key is `\texttt{i}'. @@ -1913,7 +1913,7 @@ Autocompletes. Default key is `\texttt{M-v}'. \item[\#versionshort] Show the program's version number, plus the date and time that the running copy was built from sources (not the version's release date). -Default key is `\texttt{v}'. +Default key is `\texttt{V}'. %.lp \item[\#vision] Show vision array. diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 30abe683d..f4d2a35ca 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1586,6 +1586,8 @@ priest donation amounts are explicitly stated, randomized slightly, based on peak rather than current level, and allow for bulk donations (buying larger amounts of clairvoyance/protection) if you have a lot of gold amulet of magical breathing increases power regeneration +change some command keys, 'v' is now chronicle, 'V' is versionshort, + m-prefix 'V' is longer version, remove key binding of #history Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/cmd.c b/src/cmd.c index 46200881f..a88b2d7a7 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -1690,7 +1690,7 @@ struct ext_func_tab extcmdlist[] = { docast, IFBURIED, NULL }, { M('c'), "chat", "talk to someone", dotalk, IFBURIED | AUTOCOMPLETE, NULL }, - { '\0', "chronicle", "show journal of major events", + { 'v', "chronicle", "show journal of major events", do_gamelog, IFBURIED | AUTOCOMPLETE | GENERALCMD, NULL }, { 'c', "close", "close a door", doclose, 0, NULL }, @@ -1735,7 +1735,7 @@ struct ext_func_tab extcmdlist[] = { dohelp, IFBURIED | GENERALCMD, NULL }, { '\0', "herecmdmenu", "show menu of commands you can do here", doherecmdmenu, IFBURIED | AUTOCOMPLETE | GENERALCMD, NULL }, - { 'V', "history", "show long version and game history", + { '\0', "history", "show long version and game history", dohistory, IFBURIED | GENERALCMD, NULL }, { 'i', "inventory", "show your inventory", ddoinv, IFBURIED | GENERALCMD, NULL }, @@ -1923,8 +1923,8 @@ struct ext_func_tab extcmdlist[] = { { M('v'), "version", "list compile time options for this version of NetHack", doextversion, IFBURIED | AUTOCOMPLETE | GENERALCMD, NULL }, - { 'v', "versionshort", "show version and date+time program was built", - doversion, IFBURIED | GENERALCMD, NULL }, + { 'V', "versionshort", "show version and date+time program was built", + doversion, IFBURIED | GENERALCMD | CMD_M_PREFIX, NULL }, { '\0', "vision", "show vision array", wiz_show_vision, IFBURIED | AUTOCOMPLETE | WIZMODECMD, NULL }, { '.', "wait", "rest one move while doing nothing", diff --git a/src/version.c b/src/version.c index 99c36efdf..0b82963b8 100644 --- a/src/version.c +++ b/src/version.c @@ -157,6 +157,9 @@ doversion(void) { char buf[BUFSZ]; + if (iflags.menu_requested) + return doextversion(); + pline("%s", getversionstring(buf, sizeof buf)); return ECMD_OK; } From dffb40c0e73b78c9751e25be203cad04eb17eda5 Mon Sep 17 00:00:00 2001 From: nhw_cron Date: Sat, 21 Mar 2026 12:16:08 -0400 Subject: [PATCH 327/442] This is cron-daily v1-Jan-12-2026. 005guidebook updated: doc/Guidebook.txt --- doc/Guidebook.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/Guidebook.txt b/doc/Guidebook.txt index 13e160360..73535edf7 100644 --- a/doc/Guidebook.txt +++ b/doc/Guidebook.txt @@ -1367,7 +1367,7 @@ Talk to someone. Default key is `M-c'. #chronicle - Show a list of important game events. + Show a list of important game events. Default key is `v'. #close Close a door. Default key is `c'. @@ -1486,7 +1486,7 @@ command. #history - Show long version and game history. Default key is `V'. + Show long version and game history. #inventory Show your inventory. Default key is `i'. @@ -1987,7 +1987,7 @@ #versionshort Show the program's version number, plus the date and time that the running copy was built from sources (not the version's - release date). Default key is `v'. + release date). Default key is `V'. #vision Show vision array. Autocompletes. Debug mode only. From 33b24fbd13e8cd1b80674aebb61be99334880d52 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 21 Mar 2026 18:43:04 -0400 Subject: [PATCH 328/442] update Xcode project to reflect recent file addition --- sys/unix/NetHack.xcodeproj/project.pbxproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sys/unix/NetHack.xcodeproj/project.pbxproj b/sys/unix/NetHack.xcodeproj/project.pbxproj index 3139be9b4..3c8b036a5 100644 --- a/sys/unix/NetHack.xcodeproj/project.pbxproj +++ b/sys/unix/NetHack.xcodeproj/project.pbxproj @@ -202,6 +202,7 @@ BAE8015627B99CAE002B3786 /* lstrlib.c in Sources */ = {isa = PBXBuildFile; fileRef = BAE8013427B99CAD002B3786 /* lstrlib.c */; }; BAE8015727B99CAE002B3786 /* lzio.c in Sources */ = {isa = PBXBuildFile; fileRef = BAE8013527B99CAD002B3786 /* lzio.c */; }; BAE8015A27B9C872002B3786 /* date.c in Sources */ = {isa = PBXBuildFile; fileRef = 5439B3BB275AADC600B8FB2F /* date.c */; }; + F50818312F6F564300D2AAFA /* iactions.c in Sources */ = {isa = PBXBuildFile; fileRef = F50818302F6F564300D2AAFA /* iactions.c */; }; F5457B212DED146B00039D83 /* hacklib.c in Sources */ = {isa = PBXBuildFile; fileRef = F5457B202DED146B00039D83 /* hacklib.c */; }; F5457B2C2DED16F200039D83 /* libhacklib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F5457B1C2DED143E00039D83 /* libhacklib.a */; }; F5457B2D2DED171800039D83 /* libhacklib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F5457B1C2DED143E00039D83 /* libhacklib.a */; }; @@ -595,6 +596,7 @@ BAE8013327B99CAD002B3786 /* lmathlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lmathlib.c; path = "../../lib/lua-5.4.8/src/lmathlib.c"; sourceTree = ""; }; BAE8013427B99CAD002B3786 /* lstrlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lstrlib.c; path = "../../lib/lua-5.4.8/src/lstrlib.c"; sourceTree = ""; }; BAE8013527B99CAD002B3786 /* lzio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lzio.c; path = "../../lib/lua-5.4.8/src/lzio.c"; sourceTree = ""; }; + F50818302F6F564300D2AAFA /* iactions.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = iactions.c; path = ../../src/iactions.c; sourceTree = ""; }; F515A6F32DED074D006E1F63 /* sfctool.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = sfctool.c; path = ../../util/sfctool.c; sourceTree = ""; }; F515A6F52DED0916006E1F63 /* date.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = date.c; path = ../../src/date.c; sourceTree = ""; }; F515A6F62DED0916006E1F63 /* sfbase.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = sfbase.c; path = ../../src/sfbase.c; sourceTree = ""; }; @@ -691,6 +693,7 @@ 3189576821A1FCC100FB2ABE = { isa = PBXGroup; children = ( + F50818302F6F564300D2AAFA /* iactions.c */, F515A73B2DED0B47006E1F63 /* hacklib.c */, F5457B202DED146B00039D83 /* hacklib.c */, F515A72A2DED0A7C006E1F63 /* sftags.c */, @@ -1980,6 +1983,7 @@ 31B8A38421A238060055BD01 /* do_name.c in Sources */, 31B8A3C421A238060055BD01 /* apply.c in Sources */, 31B8A3F721A23DD10055BD01 /* unixres.c in Sources */, + F50818312F6F564300D2AAFA /* iactions.c in Sources */, 31B8A3BD21A238060055BD01 /* files.c in Sources */, 31B8A39221A238060055BD01 /* do_wear.c in Sources */, 31B8A3E521A238B30055BD01 /* dlb.c in Sources */, From 8ee179a59428fb1f34b4d9d57ef2d4df23dce205 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 22 Mar 2026 09:30:16 -0400 Subject: [PATCH 329/442] Guidebook timestamp to reflect the most recent update --- doc/Guidebook.mn | 2 +- doc/Guidebook.tex | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 22256c17c..314567f71 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -60,7 +60,7 @@ .ds f0 \*(vr .ds f1 \" empty .\"DO NOT REMOVE NH_DATESUB .ds f2 Date(%B %-d, %Y) -.ds f2 February 11, 2026 +.ds f2 March 21, 2026 . .\" A note on some special characters: .\" \(lq = left double quote diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index be5da967b..7fa1724af 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -25,7 +25,7 @@ \author{Original version - Eric S. Raymond\\ (Edited and expanded for 3.7.0 by Mike Stephenson and others)} %DO NOT REMOVE NH_DATESUB \date{Date(%B %-d, %Y)} -\date{February 11, 2026} +\date{March 21, 2026} \maketitle From f9cebc5890987137244a012c825bf34a83b1ccdf Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 22 Mar 2026 11:33:26 -0400 Subject: [PATCH 330/442] avoid a warning with Xcode build --- src/hacklib.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/hacklib.c b/src/hacklib.c index 50be4169b..25891684e 100644 --- a/src/hacklib.c +++ b/src/hacklib.c @@ -936,11 +936,17 @@ case_insensitive_comp(const char *s1, const char *s2) return u1 - u2; } +#if defined(MACOS) +#define RETTYPE ssize_t +#else +#define RETTYPE int +#endif + boolean copy_bytes(int ifd, int ofd) { char buf[BUFSIZ]; - int nfrom, nto; + RETTYPE nfrom, nto; do { nto = 0; @@ -950,9 +956,11 @@ copy_bytes(int ifd, int ofd) nto = write(ofd, buf, nfrom); if (nto != nfrom || nfrom < 0) return FALSE; - } while (nfrom == BUFSIZ); + } while (nfrom == (RETTYPE) BUFSIZ); return TRUE; } +#undef RETTYPE + #define MAX_D 5 struct datamodel_information { int sz[MAX_D]; From e94a2f2bf2d849c1340c9a87c36f516cd35621cb Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 22 Mar 2026 11:37:07 -0400 Subject: [PATCH 331/442] doc/Guidebook.txt update Mar03-2026 --- doc/Guidebook.txt | 334 +++++++++++++++++++++++----------------------- 1 file changed, 167 insertions(+), 167 deletions(-) diff --git a/doc/Guidebook.txt b/doc/Guidebook.txt index 73535edf7..6ac7457db 100644 --- a/doc/Guidebook.txt +++ b/doc/Guidebook.txt @@ -15,7 +15,7 @@ Original version - Eric S. Raymond (Edited and expanded for NetHack 3.7.0 by Mike Stephenson and others) - February 11, 2026 + March 21, 2026 @@ -126,7 +126,7 @@ employ to great advantage. - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -192,7 +192,7 @@ NetHack continues this fine tradition. Unlike text adventure games - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -258,7 +258,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -324,7 +324,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -390,7 +390,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -456,7 +456,7 @@ The number of turns elapsed so far, displayed if you have the - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -522,7 +522,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -588,7 +588,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -654,7 +654,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -720,7 +720,7 @@ instead. Only these one-step movement commands cause you to - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -786,7 +786,7 @@ ing . ^ is used as shorthand elsewhere in the - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -852,7 +852,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -918,7 +918,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -984,7 +984,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -1050,7 +1050,7 @@ at an adjacent "remembered, unseen monster" marker. - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -1116,7 +1116,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -1182,7 +1182,7 @@ (R)UNIX is a registered trademark of The Open Group. - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -1248,7 +1248,7 @@ menu_next_page, and menu_last_page keys (`^', `<', `>', `|' by - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -1314,7 +1314,7 @@ doesn't and give that name to the result, while splitting (count - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -1380,7 +1380,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -1446,7 +1446,7 @@ extinct. - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -1512,7 +1512,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -1578,7 +1578,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -1644,7 +1644,7 @@ right away.) Since using this command by accident can cause - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -1710,7 +1710,7 @@ Default key is `M-R'. - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -1776,7 +1776,7 @@ (worn blindfold or towel or lenses, lit lamp(s) and/or candle(s), - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -1842,7 +1842,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -1900,15 +1900,15 @@ Autocompletes. Default key is `M-T'. - #travel - Travel to a specific location on the map. Default key is `_'. - Using the "request menu" prefix shows a menu of interesting tar- - gets in sight without asking to move the cursor. When picking a - target with cursor and the autodescribe option is on, the top - line will show "(no travel path)" if your character does not know + #toggle + Toggle a boolean option on or off. Requires a parameter in + parenthesis, the name of the option to toggle. The option must + be settable in-game. - NetHack 3.7.0 February 11, 2026 + + + NetHack 3.7.0 March 21, 2026 @@ -1918,6 +1918,17 @@ + For example: + + BIND=':toggle(price_quotes) + BIND=@:toggle(autopickup) + + #travel + Travel to a specific location on the map. Default key is `_'. + Using the "request menu" prefix shows a menu of interesting tar- + gets in sight without asking to move the cursor. When picking a + target with cursor and the autodescribe option is on, the top + line will show "(no travel path)" if your character does not know of a path to that location. See also #retravel. #turn @@ -1960,6 +1971,19 @@ quished monsters answering `a' will let you choose from the sort menu. + + + + NetHack 3.7.0 March 21, 2026 + + + + + + NetHack Guidebook 31 + + + Autocompletes. Default key is `M-V'. #version @@ -1972,18 +1996,6 @@ Autocompletes. Default key is `M-v'. - - - NetHack 3.7.0 February 11, 2026 - - - - - - NetHack Guidebook 31 - - - #versionshort Show the program's version number, plus the date and time that the running copy was built from sources (not the version's @@ -2026,6 +2038,18 @@ #wizdetect Reveal hidden things (secret doors or traps or unseen monsters) within a modest radius. No time elapses. Autocompletes. Debug + + + NetHack 3.7.0 March 21, 2026 + + + + + + NetHack Guidebook 32 + + + mode only. Default key is `^E'. #wizgenesis @@ -2038,18 +2062,6 @@ #wizintrinsic Set one or more intrinsic attributes. Autocompletes. Debug mode - - - NetHack 3.7.0 February 11, 2026 - - - - - - NetHack Guidebook 32 - - - only. #wizkill @@ -2093,6 +2105,17 @@ #wmode Show wall modes. Autocompletes. Debug mode only. + + NetHack 3.7.0 March 21, 2026 + + + + + + NetHack Guidebook 33 + + + #zap Zap a wand. Default key is `z'. @@ -2104,18 +2127,6 @@ If your keyboard has a meta key (which, when pressed in combina- tion with another key, modifies it by setting the "meta" [8th, or "high"] bit), you can invoke many extended commands by meta-ing the - - - NetHack 3.7.0 February 11, 2026 - - - - - - NetHack Guidebook 33 - - - first letter of the command. On Windows and MS-DOS, the "Alt" key can be used in this fashion. @@ -2161,18 +2172,7 @@ - - - - - - - - - - - - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -2238,7 +2238,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -2304,7 +2304,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -2370,7 +2370,7 @@ them). Some monsters who can open doors can also use unlocking tools. - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -2436,7 +2436,7 @@ been nullified, giving access to whatever is beyond them. In the - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -2502,7 +2502,7 @@ play. - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -2568,7 +2568,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -2634,7 +2634,7 @@ attack, when guessing where an unseen monster is or when deliberately - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -2700,7 +2700,7 @@ noid_confirmation:attack option to require a response of "yes" - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -2766,7 +2766,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -2832,7 +2832,7 @@ are encumbered, one of the conditions Burdened, Stressed, Strained, - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -2898,7 +2898,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -2964,7 +2964,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -3030,7 +3030,7 @@ aklys, lucern hammer, and bec-de-corbin) are defined in an appendix to - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -3096,7 +3096,7 @@ you'll just end up shooting the same number (3, here) as if no limit - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -3162,7 +3162,7 @@ for that matter--wielding two weapons at once. The primary is your - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -3228,7 +3228,7 @@ unenchanted but all cloaks offer some protection against rust or cor- - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -3294,7 +3294,7 @@ (except for blank ones, without magic spells on them). - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -3360,7 +3360,7 @@ may be recharged by using suitable magic, but doing so runs the risk - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -3426,7 +3426,7 @@ with your naked mind. Some of the magical energy released comes from - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -3492,7 +3492,7 @@ tool of this sort can be opened with the "#loot" extended command when - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -3558,7 +3558,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -3624,7 +3624,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -3690,7 +3690,7 @@ items listed above, they may eat any kind of pudding (`P') other than - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -3756,7 +3756,7 @@ spellbooks (and knowledge of spells) in your starting inventory is - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -3822,7 +3822,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -3888,7 +3888,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -3954,7 +3954,7 @@ (the closing `]' can be followed by whitespace and then an arbitrary - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -4020,7 +4020,7 @@ problems is kept. Defaults to HACKDIR, must be writable. - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -4086,7 +4086,7 @@ Custom symbols for the rogue level's symbol set. See SYMBOLS below. - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -4152,7 +4152,7 @@ values for the various options. Some can only be turned on or off. - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -4218,7 +4218,7 @@ align:chaotic). You may specify just the first letter. Many roles - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -4284,7 +4284,7 @@ untrap and you omit apply-key or you lack a key or you - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -4350,7 +4350,7 @@ Start the character permanently deaf (default false). Persistent. - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -4416,7 +4416,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -4482,7 +4482,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -4548,7 +4548,7 @@ above the name and title with a hard-coded color scheme. (As of - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -4614,7 +4614,7 @@ Enable coloring menu lines (default off). See "Configuring Menu - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -4680,7 +4680,7 @@ display symbol for the individual item in each menu entry. For a - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -4746,7 +4746,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -4812,7 +4812,7 @@ Send padding nulls to the terminal (default on). Persistent. - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -4878,7 +4878,7 @@ trap - require `y' to confirm an attempt to move into or onto - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -4944,7 +4944,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -5010,7 +5010,7 @@ unintentionally pick such up. - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -5076,7 +5076,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -5142,7 +5142,7 @@ run - update the map after every seven or so steps; - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -5208,7 +5208,7 @@ lines. Persistent. - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -5274,7 +5274,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -5340,7 +5340,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -5406,7 +5406,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -5472,7 +5472,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -5538,7 +5538,7 @@ If NetHack can, it should try to display on the entire screen rather - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -5604,7 +5604,7 @@ shows status conditions on their own line. A display capable of - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -5670,7 +5670,7 @@ in the message window. - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -5736,7 +5736,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -5802,7 +5802,7 @@ Set the video mode used (PC NetHack only). Values are "autodetect", - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -5868,7 +5868,7 @@ the first character in the pattern, specifically: - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -5934,7 +5934,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -6000,7 +6000,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -6066,7 +6066,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -6132,7 +6132,7 @@ placed ." is not shown at all. - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -6198,7 +6198,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -6264,7 +6264,7 @@ (That example is actually specifying red&normal for <=30% and no- - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -6330,7 +6330,7 @@ tion. - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -6396,7 +6396,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -6462,7 +6462,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -6528,7 +6528,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -6594,7 +6594,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -6660,7 +6660,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -6726,7 +6726,7 @@ playing UTF-8 character sequences and explicit red-green-blue colors - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -6792,7 +6792,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -6858,7 +6858,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -6924,7 +6924,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -6990,7 +6990,7 @@ machine, depending on how it is set up. In the latter case, each - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -7056,7 +7056,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -7122,7 +7122,7 @@ for PC NetHack 3.0. Johnny Lee ported NetHack 3.0 to the Macintosh. - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -7188,7 +7188,7 @@ quently been picked up by various other games. NetHack's tiles sup- - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -7254,7 +7254,7 @@ names in the current game.) - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -7320,7 +7320,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -7386,7 +7386,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -7452,7 +7452,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -7518,7 +7518,7 @@ sends a particularly intriguing modification to help out with the - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -7584,7 +7584,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -7650,7 +7650,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 @@ -7716,7 +7716,7 @@ - NetHack 3.7.0 February 11, 2026 + NetHack 3.7.0 March 21, 2026 From 10eedbe2f0a64802e2571742e5dee1db43dce38f Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 22 Mar 2026 22:01:40 -0700 Subject: [PATCH 332/442] fix #S15496 - #chronicle oversight Add a livelog/#chronicle message for quest leader opening the quest. A similar message for being expelled doesn't seem possible to be triggered. Log the initial visit to each level in the quest branch. They record when the hero actually visits the quest levels, beyond the new one about permission to do the quest. --- src/do.c | 16 +++++++++++++--- src/quest.c | 16 +++++++++++++++- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/do.c b/src/do.c index 26485069c..cacab11c7 100644 --- a/src/do.c +++ b/src/do.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 do.c $NHDT-Date: 1737287889 2025/01/19 03:58:09 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.399 $ */ +/* NetHack 3.7 do.c $NHDT-Date: 1774269965 2026/03/23 04:46:05 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.404 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1942,8 +1942,18 @@ goto_level( if (new) { char dloc[QBUFSZ]; /* Astral is excluded as a major event here because entry to it - is already one due to that being an achievement */ - boolean major = In_endgame(&u.uz) && !Is_astralevel(&u.uz); + is already one such due to that being an achievement; + for the quest, listing the start, locate, and goal levels would + seem reasonable but all quest levels are included for simplicity-- + level 2 (or 3 if hero level teleports after obtaining permission + to enter) is useful to show since it indicates that hero has + actually entered the quest rather than just received permission + to do so, and listing the goal level could be used to figure out + whether level 5 is the end or there's another level (ESP reveals + the same thing, but is part of normal game play as opposed to + #chronicle leaking information that hero hasn't discovered yet) */ + boolean major = ((In_endgame(&u.uz) && !Is_astralevel(&u.uz)) + || In_quest(&u.uz)); (void) describe_level(dloc, 2); livelog_printf(major ? LL_ACHIEVE : LL_DEBUG, "entered %s", dloc); diff --git a/src/quest.c b/src/quest.c index a436e2787..2b54baa86 100644 --- a/src/quest.c +++ b/src/quest.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 quest.c $NHDT-Date: 1687036547 2023/06/17 21:15:47 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.38 $ */ +/* NetHack 3.7 quest.c $NHDT-Date: 1774269965 2026/03/23 04:46:05 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.46 $ */ /* Copyright 1991, M. Stephenson */ /* NetHack may be freely redistributed. See license for details. */ @@ -339,6 +339,12 @@ chat_with_leader(struct monst *mtmp) com_pager("banished"); Qstat(pissed_off) = TRUE; expulsion(FALSE); + + /* being expelled is hardly an achievement but none of the + other livelog classifications fit */ + livelog_printf(LL_ACHIEVE, + "%s has expelled you from the quest", + noit_mon_nam(mtmp)); } } else if (purity == 0) { qt_pager("badalign"); @@ -349,6 +355,14 @@ chat_with_leader(struct monst *mtmp) qt_pager("assignquest"); exercise(A_WIS, TRUE); Qstat(got_quest) = TRUE; + + /* phrasing is a bit clumsy but allows #chronicle to provide a + clue to players who are reaching the quest for first time; + matters most for Home 1 that has stairs down which aren't + easily found */ + livelog_printf(LL_ACHIEVE, + "%s has granted access to proceed deeper into the quest", + noit_mon_nam(mtmp)); } } } From d6cbaca654eba33dc08a53ae143bb41f6124bbb4 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Mon, 23 Mar 2026 11:42:51 +0200 Subject: [PATCH 333/442] Reorganize mcastu code Merge the two different enums, so all monster spells are in one list. This should have no functional difference, but cleans up the code a bit. --- src/mcastu.c | 350 +++++++++++++++++++++++---------------------------- 1 file changed, 155 insertions(+), 195 deletions(-) diff --git a/src/mcastu.c b/src/mcastu.c index bffe7d790..605fb1ab5 100644 --- a/src/mcastu.c +++ b/src/mcastu.c @@ -5,44 +5,40 @@ #include "hack.h" -/* monster mage spells */ -enum mcast_mage_spells { - MGC_PSI_BOLT = 0, - MGC_CURE_SELF, - MGC_HASTE_SELF, - MGC_STUN_YOU, - MGC_DISAPPEAR, - MGC_WEAKEN_YOU, - MGC_DESTRY_ARMR, - MGC_CURSE_ITEMS, - MGC_AGGRAVATION, - MGC_SUMMON_MONS, - MGC_CLONE_WIZ, - MGC_DEATH_TOUCH -}; +enum mcast_spells { + MCAST_PSI_BOLT = 0, + MCAST_OPEN_WOUNDS, + MCAST_LIGHTNING, + MCAST_FIRE_PILLAR, + MCAST_GEYSER, + MCAST_DEATH_TOUCH, -/* monster cleric spells */ -enum mcast_cleric_spells { - CLC_OPEN_WOUNDS = 0, - CLC_CURE_SELF, - CLC_CONFUSE_YOU, - CLC_PARALYZE, - CLC_BLIND_YOU, - CLC_INSECTS, - CLC_CURSE_ITEMS, - CLC_LIGHTNING, - CLC_FIRE_PILLAR, - CLC_GEYSER + MCAST_CURE_SELF, + MCAST_HASTE_SELF, + MCAST_DISAPPEAR, + + MCAST_AGGRAVATION, + MCAST_STUN_YOU, + MCAST_WEAKEN_YOU, + MCAST_CONFUSE_YOU, + MCAST_PARALYZE, + MCAST_BLIND_YOU, + + MCAST_DESTRY_ARMR, + MCAST_CURSE_ITEMS, + + MCAST_INSECTS, + MCAST_SUMMON_MONS, + MCAST_CLONE_WIZ }; staticfn void cursetxt(struct monst *, boolean); -staticfn int choose_magic_spell(int); -staticfn int choose_clerical_spell(int); +staticfn int choose_magic_spell(struct monst *); +staticfn int choose_clerical_spell(struct monst *); staticfn int m_cure_self(struct monst *, int); -staticfn void cast_wizard_spell(struct monst *, int, int); -staticfn void cast_cleric_spell(struct monst *, int, int); -staticfn boolean is_undirected_spell(unsigned int, int); -staticfn boolean spell_would_be_useless(struct monst *, unsigned int, int); +staticfn void mcast_spell(struct monst *, int, int); +staticfn boolean is_undirected_spell(int); +staticfn boolean spell_would_be_useless(struct monst *, int); /* feedback when frustrated monster couldn't cast a spell */ staticfn void @@ -73,8 +69,10 @@ cursetxt(struct monst *mtmp, boolean undirected) /* convert a level-based random selection into a specific mage spell; inappropriate choices will be screened out by spell_would_be_useless() */ staticfn int -choose_magic_spell(int spellval) +choose_magic_spell(struct monst *mtmp) { + int spellval = rn2(mtmp->m_lev); + /* for 3.4.3 and earlier, val greater than 22 selected default spell */ while (spellval > 24 && rn2(25)) spellval = rn2(spellval); @@ -83,52 +81,54 @@ choose_magic_spell(int spellval) case 24: case 23: if (Antimagic || Hallucination) - return MGC_PSI_BOLT; + return MCAST_PSI_BOLT; FALLTHROUGH; /*FALLTHRU*/ case 22: case 21: case 20: - return MGC_DEATH_TOUCH; + return MCAST_DEATH_TOUCH; case 19: case 18: - return MGC_CLONE_WIZ; + return MCAST_CLONE_WIZ; case 17: case 16: case 15: - return MGC_SUMMON_MONS; + return MCAST_SUMMON_MONS; case 14: case 13: - return MGC_AGGRAVATION; + return MCAST_AGGRAVATION; case 12: case 11: case 10: - return MGC_CURSE_ITEMS; + return MCAST_CURSE_ITEMS; case 9: case 8: - return MGC_DESTRY_ARMR; + return MCAST_DESTRY_ARMR; case 7: case 6: - return MGC_WEAKEN_YOU; + return MCAST_WEAKEN_YOU; case 5: case 4: - return MGC_DISAPPEAR; + return MCAST_DISAPPEAR; case 3: - return MGC_STUN_YOU; + return MCAST_STUN_YOU; case 2: - return MGC_HASTE_SELF; + return MCAST_HASTE_SELF; case 1: - return MGC_CURE_SELF; + return MCAST_CURE_SELF; case 0: default: - return MGC_PSI_BOLT; + return MCAST_PSI_BOLT; } } /* convert a level-based random selection into a specific cleric spell */ staticfn int -choose_clerical_spell(int spellnum) +choose_clerical_spell(struct monst *mtmp) { + int spellnum = rn2(mtmp->m_lev); + /* for 3.4.3 and earlier, num greater than 13 selected the default spell */ while (spellnum > 15 && rn2(16)) @@ -138,34 +138,34 @@ choose_clerical_spell(int spellnum) case 15: case 14: if (rn2(3)) - return CLC_OPEN_WOUNDS; + return MCAST_OPEN_WOUNDS; FALLTHROUGH; /*FALLTHRU*/ case 13: - return CLC_GEYSER; + return MCAST_GEYSER; case 12: - return CLC_FIRE_PILLAR; + return MCAST_FIRE_PILLAR; case 11: - return CLC_LIGHTNING; + return MCAST_LIGHTNING; case 10: case 9: - return CLC_CURSE_ITEMS; + return MCAST_CURSE_ITEMS; case 8: - return CLC_INSECTS; + return MCAST_INSECTS; case 7: case 6: - return CLC_BLIND_YOU; + return MCAST_BLIND_YOU; case 5: case 4: - return CLC_PARALYZE; + return MCAST_PARALYZE; case 3: case 2: - return CLC_CONFUSE_YOU; + return MCAST_CONFUSE_YOU; case 1: - return CLC_CURE_SELF; + return MCAST_CURE_SELF; case 0: default: - return CLC_OPEN_WOUNDS; + return MCAST_OPEN_WOUNDS; } } @@ -200,15 +200,14 @@ castmu( int cnt = 40; do { - spellnum = rn2(ml); if (mattk->adtyp == AD_SPEL) - spellnum = choose_magic_spell(spellnum); + spellnum = choose_magic_spell(mtmp); else - spellnum = choose_clerical_spell(spellnum); + spellnum = choose_clerical_spell(mtmp); /* not trying to attack? don't allow directed spells */ if (!thinks_it_foundyou) { - if (!is_undirected_spell(mattk->adtyp, spellnum) - || spell_would_be_useless(mtmp, mattk->adtyp, spellnum)) { + if (!is_undirected_spell(spellnum) + || spell_would_be_useless(mtmp, spellnum)) { if (foundyou) impossible( "spellcasting monster found you and doesn't know it?"); @@ -217,7 +216,7 @@ castmu( break; } } while (--cnt > 0 - && spell_would_be_useless(mtmp, mattk->adtyp, spellnum)); + && spell_would_be_useless(mtmp, spellnum)); if (cnt == 0) return M_ATTK_MISS; } @@ -225,10 +224,12 @@ castmu( /* monster unable to cast spells? */ if (mtmp->mcan || mtmp->mspec_used || !ml || m_seenres(mtmp, cvt_adtyp_to_mseenres(mattk->adtyp))) { - cursetxt(mtmp, is_undirected_spell(mattk->adtyp, spellnum)); + cursetxt(mtmp, is_undirected_spell(spellnum)); return M_ATTK_MISS; } + debugpline3("castmu:%s,lvl:%i,spell:%i", noit_Monnam(mtmp), ml, spellnum); + if (mattk->adtyp == AD_SPEL || mattk->adtyp == AD_CLRC) { /* monst->m_lev is unsigned (uchar), monst->mspec_used is int */ mtmp->mspec_used = (int) ((mtmp->m_lev < 8) ? (10 - mtmp->m_lev) : 2); @@ -245,7 +246,7 @@ castmu( * for fire mis-aimed at ice. */ if (!foundyou && thinks_it_foundyou - && !is_undirected_spell(mattk->adtyp, spellnum)) { + && !is_undirected_spell(spellnum)) { pline_mon(mtmp, "%s casts a spell at %s!", canseemon(mtmp) ? Monnam(mtmp) : "Something", is_waterwall(mtmp->mux, mtmp->muy) ? "empty water" @@ -262,10 +263,10 @@ castmu( } return M_ATTK_MISS; } - if (canspotmon(mtmp) || !is_undirected_spell(mattk->adtyp, spellnum)) { + if (canspotmon(mtmp) || !is_undirected_spell(spellnum)) { pline_mon(mtmp, "%s casts a spell%s!", canspotmon(mtmp) ? Monnam(mtmp) : "Something", - is_undirected_spell(mattk->adtyp, spellnum) ? "" + is_undirected_spell(spellnum) ? "" : (Invis && !perceives(mtmp->data) && !u_at(mtmp->mux, mtmp->muy)) ? " at a spot near you" @@ -344,10 +345,7 @@ castmu( break; case AD_SPEL: /* wizard spell */ case AD_CLRC: /* clerical spell */ - if (mattk->adtyp == AD_SPEL) - cast_wizard_spell(mtmp, dmg, spellnum); - else - cast_cleric_spell(mtmp, dmg, spellnum); + mcast_spell(mtmp, dmg, spellnum); dmg = 0; /* done by the spell casting functions */ break; } /* switch */ @@ -446,20 +444,22 @@ death_inflicted_by( and spell_would_be_useless(). */ staticfn void -cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum) +mcast_spell(struct monst *mtmp, int dmg, int spellnum) { + int orig_dmg = 0; + if (dmg < 0) { - impossible("monster cast wizard spell (%d) with negative dmg (%d)?", + impossible("monster cast spell (%d) with negative dmg (%d)?", spellnum, dmg); return; } - if (dmg == 0 && !is_undirected_spell(AD_SPEL, spellnum)) { + if (dmg == 0 && !is_undirected_spell(spellnum)) { impossible("cast directed wizard spell (%d) with dmg=0?", spellnum); return; } switch (spellnum) { - case MGC_DEATH_TOUCH: + case MCAST_DEATH_TOUCH: pline("Oh no, %s's using the touch of death!", mhe(mtmp)); if (nonliving(gy.youmonst.data) || is_demon(gy.youmonst.data)) { You("seem no deader than before."); @@ -479,7 +479,7 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum) } dmg = 0; break; - case MGC_CLONE_WIZ: + case MCAST_CLONE_WIZ: if (mtmp->iswiz && svc.context.no_of_wizards == 1) { pline("Double Trouble..."); clonewiz(); @@ -487,7 +487,7 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum) } else impossible("bad wizard cloning?"); break; - case MGC_SUMMON_MONS: { + case MCAST_SUMMON_MONS: { int count = nasty(mtmp); if (!count) { @@ -515,17 +515,17 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum) dmg = 0; break; } - case MGC_AGGRAVATION: + case MCAST_AGGRAVATION: You_feel("that monsters are aware of your presence."); aggravate(); dmg = 0; break; - case MGC_CURSE_ITEMS: + case MCAST_CURSE_ITEMS: You_feel("as if you need some help."); rndcurse(); dmg = 0; break; - case MGC_DESTRY_ARMR: + case MCAST_DESTRY_ARMR: if (Antimagic) { shieldeff(u.ux, u.uy); monstseesu(M_SEEN_MAGR); @@ -539,7 +539,7 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum) } dmg = 0; break; - case MGC_WEAKEN_YOU: /* drain strength */ + case MCAST_WEAKEN_YOU: /* drain strength */ if (Antimagic) { shieldeff(u.ux, u.uy); monstseesu(M_SEEN_MAGR); @@ -561,7 +561,7 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum) } dmg = 0; break; - case MGC_DISAPPEAR: /* makes self invisible */ + case MCAST_DISAPPEAR: /* makes self invisible */ if (!mtmp->minvis && !mtmp->invis_blkd) { if (canseemon(mtmp)) pline_mon(mtmp, "%s suddenly %s!", Monnam(mtmp), @@ -573,7 +573,7 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum) } else impossible("no reason for monster to cast disappear spell?"); break; - case MGC_STUN_YOU: + case MCAST_STUN_YOU: if (Antimagic || Free_action) { shieldeff(u.ux, u.uy); monstseesu(M_SEEN_MAGR); @@ -590,14 +590,14 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum) } dmg = 0; break; - case MGC_HASTE_SELF: + case MCAST_HASTE_SELF: mon_adjust_speed(mtmp, 1, (struct obj *) 0); dmg = 0; break; - case MGC_CURE_SELF: + case MCAST_CURE_SELF: dmg = m_cure_self(mtmp, dmg); break; - case MGC_PSI_BOLT: + case MCAST_PSI_BOLT: /* prior to 3.4.0 Antimagic was setting the damage to 1--this made the spell virtually harmless to players with magic res. */ if (Antimagic) { @@ -616,35 +616,7 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum) else Your("%s suddenly aches very painfully!", body_part(HEAD)); break; - default: - impossible("mcastu: invalid magic spell (%d)", spellnum); - dmg = 0; - break; - } - - if (dmg) - mdamageu(mtmp, dmg); -} - -DISABLE_WARNING_FORMAT_NONLITERAL - -staticfn void -cast_cleric_spell(struct monst *mtmp, int dmg, int spellnum) -{ - int orig_dmg = 0; - - if (dmg < 0) { - impossible("monster cast cleric spell (%d) with negative dmg (%d)?", - spellnum, dmg); - return; - } - if (dmg == 0 && !is_undirected_spell(AD_CLRC, spellnum)) { - impossible("cast directed cleric spell (%d) with dmg=0?", spellnum); - return; - } - - switch (spellnum) { - case CLC_GEYSER: + case MCAST_GEYSER: /* this is physical damage (force not heat), * not magical damage or fire damage */ @@ -657,7 +629,7 @@ cast_cleric_spell(struct monst *mtmp, int dmg, int spellnum) water_damage_chain(level.objects[u.ux][u.uy], TRUE); #endif break; - case CLC_FIRE_PILLAR: + case MCAST_FIRE_PILLAR: pline("A pillar of fire strikes all around you!"); orig_dmg = dmg = d(8, 6); if (Fire_resistance) { @@ -677,7 +649,7 @@ cast_cleric_spell(struct monst *mtmp, int dmg, int spellnum) /* burn up flammable items on the floor, melt ice terrain */ mon_spell_hits_spot(mtmp, AD_FIRE, u.ux, u.uy); break; - case CLC_LIGHTNING: { + case MCAST_LIGHTNING: { boolean reflects; Soundeffect(se_bolt_of_lightning, 80); @@ -708,12 +680,7 @@ cast_cleric_spell(struct monst *mtmp, int dmg, int spellnum) (void) flashburn((long) rnd(100), TRUE); break; } - case CLC_CURSE_ITEMS: - You_feel("as if you need some help."); - rndcurse(); - dmg = 0; - break; - case CLC_INSECTS: { + case MCAST_INSECTS: { /* Try for insects, and if there are none left, go for (sticks to) snakes. -3. */ struct permonst *pm = mkclass(S_ANT, 0); @@ -788,13 +755,15 @@ cast_cleric_spell(struct monst *mtmp, int dmg, int spellnum) } else { fmt = "%s summons %s!"; } - if (fmt) + if (fmt) { + DISABLE_WARNING_FORMAT_NONLITERAL; pline_mon(mtmp, fmt, Monnam(mtmp), what); - + RESTORE_WARNING_FORMAT_NONLITERAL; + } dmg = 0; break; } - case CLC_BLIND_YOU: + case MCAST_BLIND_YOU: /* note: resists_blnd() doesn't apply here */ if (!Blinded) { int num_eyes = eyecount(gy.youmonst.data); @@ -809,7 +778,7 @@ cast_cleric_spell(struct monst *mtmp, int dmg, int spellnum) } else impossible("no reason for monster to cast blindness spell?"); break; - case CLC_PARALYZE: + case MCAST_PARALYZE: if (Antimagic || Free_action) { shieldeff(u.ux, u.uy); monstseesu(M_SEEN_MAGR); @@ -829,7 +798,7 @@ cast_cleric_spell(struct monst *mtmp, int dmg, int spellnum) gn.nomovemsg = 0; dmg = 0; break; - case CLC_CONFUSE_YOU: + case MCAST_CONFUSE_YOU: if (Antimagic) { shieldeff(u.ux, u.uy); monstseesu(M_SEEN_MAGR); @@ -849,10 +818,7 @@ cast_cleric_spell(struct monst *mtmp, int dmg, int spellnum) } dmg = 0; break; - case CLC_CURE_SELF: - dmg = m_cure_self(mtmp, dmg); - break; - case CLC_OPEN_WOUNDS: + case MCAST_OPEN_WOUNDS: if (Antimagic) { shieldeff(u.ux, u.uy); monstseesu(M_SEEN_MAGR); @@ -870,7 +836,7 @@ cast_cleric_spell(struct monst *mtmp, int dmg, int spellnum) Your("body is covered with painful wounds!"); break; default: - impossible("mcastu: invalid clerical spell (%d)", spellnum); + impossible("mcastu: invalid magic spell (%d)", spellnum); dmg = 0; break; } @@ -879,38 +845,27 @@ cast_cleric_spell(struct monst *mtmp, int dmg, int spellnum) mdamageu(mtmp, dmg); } -RESTORE_WARNING_FORMAT_NONLITERAL - staticfn boolean -is_undirected_spell(unsigned int adtyp, int spellnum) +is_undirected_spell(int spellnum) { - if (adtyp == AD_SPEL) { - switch (spellnum) { - case MGC_CLONE_WIZ: - case MGC_SUMMON_MONS: - case MGC_AGGRAVATION: - case MGC_DISAPPEAR: - case MGC_HASTE_SELF: - case MGC_CURE_SELF: - return TRUE; - default: - break; - } - } else if (adtyp == AD_CLRC) { - switch (spellnum) { - case CLC_INSECTS: - case CLC_CURE_SELF: - return TRUE; - default: - break; - } + switch (spellnum) { + case MCAST_CLONE_WIZ: + case MCAST_SUMMON_MONS: + case MCAST_AGGRAVATION: + case MCAST_DISAPPEAR: + case MCAST_HASTE_SELF: + case MCAST_CURE_SELF: + case MCAST_INSECTS: + return TRUE; + default: + break; } return FALSE; } /* Some spells are useless under some circumstances. */ staticfn boolean -spell_would_be_useless(struct monst *mtmp, unsigned int adtyp, int spellnum) +spell_would_be_useless(struct monst *mtmp, int spellnum) { /* Some spells don't require the player to really be there and can be cast * by the monster when you're invisible, yet still shouldn't be cast when @@ -920,58 +875,63 @@ spell_would_be_useless(struct monst *mtmp, unsigned int adtyp, int spellnum) */ boolean mcouldseeu = couldsee(mtmp->mx, mtmp->my); - if (adtyp == AD_SPEL) { + switch (spellnum) { + case MCAST_CLONE_WIZ: + /* only the Wizard is allowed to clone himself */ + if (!mtmp->iswiz || svc.context.no_of_wizards > 1) + return TRUE; + if (!mcouldseeu) + return TRUE; + break; + case MCAST_SUMMON_MONS: + /* don't summon monsters if it doesn't think you're around */ + if (!mcouldseeu || mtmp->mpeaceful) + return TRUE; + break; + case MCAST_AGGRAVATION: /* aggravate monsters, etc. won't be cast by peaceful monsters */ - if (mtmp->mpeaceful - && (spellnum == MGC_AGGRAVATION || spellnum == MGC_SUMMON_MONS - || spellnum == MGC_CLONE_WIZ)) + if (!mcouldseeu || mtmp->mpeaceful) return TRUE; + /* aggravation (global wakeup) when everyone is already active */ + /* if nothing needs to be awakened then this spell is useless + but caster might not realize that [chance to pick it then + must be very small otherwise caller's many retry attempts + will eventually end up picking it too often] */ + if (!has_aggravatables(mtmp)) + return rn2(100) ? TRUE : FALSE; + break; + case MCAST_HASTE_SELF: /* haste self when already fast */ - if (mtmp->permspeed == MFAST && spellnum == MGC_HASTE_SELF) + if (mtmp->permspeed == MFAST) return TRUE; + break; + case MCAST_DISAPPEAR: /* invisibility when already invisible */ - if ((mtmp->minvis || mtmp->invis_blkd) && spellnum == MGC_DISAPPEAR) + if (mtmp->minvis || mtmp->invis_blkd) return TRUE; /* peaceful monster won't cast invisibility if you can't see invisible, same as when monsters drink potions of invisibility. This doesn't really make a lot of sense, but lets the player avoid hitting peaceful monsters by mistake */ - if (mtmp->mpeaceful && !See_invisible && spellnum == MGC_DISAPPEAR) + if (mtmp->mpeaceful && !See_invisible) return TRUE; + break; + case MCAST_CURE_SELF: /* healing when already healed */ - if (mtmp->mhp == mtmp->mhpmax && spellnum == MGC_CURE_SELF) + if (mtmp->mhp == mtmp->mhpmax) return TRUE; - /* don't summon monsters if it doesn't think you're around */ - if (!mcouldseeu && (spellnum == MGC_SUMMON_MONS - || (!mtmp->iswiz && spellnum == MGC_CLONE_WIZ))) + break; + case MCAST_INSECTS: + /* summon insects/sticks to snakes won't be cast by peaceful monsters */ + if (!mcouldseeu || mtmp->mpeaceful) return TRUE; - if ((!mtmp->iswiz || svc.context.no_of_wizards > 1) - && spellnum == MGC_CLONE_WIZ) - return TRUE; - /* aggravation (global wakeup) when everyone is already active */ - if (spellnum == MGC_AGGRAVATION) { - /* if nothing needs to be awakened then this spell is useless - but caster might not realize that [chance to pick it then - must be very small otherwise caller's many retry attempts - will eventually end up picking it too often] */ - if (!has_aggravatables(mtmp)) - return rn2(100) ? TRUE : FALSE; - } - } else if (adtyp == AD_CLRC) { - /* summon insects/sticks to snakes won't be cast by peaceful monsters - */ - if (mtmp->mpeaceful && spellnum == CLC_INSECTS) - return TRUE; - /* healing when already healed */ - if (mtmp->mhp == mtmp->mhpmax && spellnum == CLC_CURE_SELF) - return TRUE; - /* don't summon insects if it doesn't think you're around */ - if (!mcouldseeu && spellnum == CLC_INSECTS) - return TRUE; - /* blindness spell on blinded player */ - if (Blinded && spellnum == CLC_BLIND_YOU) + break; + case MCAST_BLIND_YOU: + if (Blinded) return TRUE; + break; + default: } return FALSE; } From 1ba9be09fabd8f17525dcdb8d1a5ce0406ad3b7c Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 23 Mar 2026 06:42:11 -0400 Subject: [PATCH 334/442] pre-C23 build fix Prior to C23, a statement is required after a label. --- src/mcastu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mcastu.c b/src/mcastu.c index 605fb1ab5..50aafc7f3 100644 --- a/src/mcastu.c +++ b/src/mcastu.c @@ -932,6 +932,7 @@ spell_would_be_useless(struct monst *mtmp, int spellnum) return TRUE; break; default: + break; } return FALSE; } From c0f5d2dd9286f1d7365483b70f62cf2f1c729474 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Mon, 23 Mar 2026 12:44:14 +0200 Subject: [PATCH 335/442] Allow monster level adjustments in special levels Add a new parameter to des.monster, m_lev_adj, which is a level adjustment for the monster. This only applies to the monster's level, so basically only affects the spellcasting, it does not change the monster's hit die or inventory. Change one of the shamans in Orctown to be 3 levels higher. --- dat/minetn-1.lua | 6 ++++-- doc/fixes3-7-0.txt | 1 + doc/lua.adoc | 1 + include/sp_lev.h | 1 + src/sp_lev.c | 9 +++++++++ 5 files changed, 16 insertions(+), 2 deletions(-) diff --git a/dat/minetn-1.lua b/dat/minetn-1.lua index 590817bf4..960553d4e 100644 --- a/dat/minetn-1.lua +++ b/dat/minetn-1.lua @@ -130,8 +130,10 @@ for i=1,5 + math.random(1 - 1,1*10) do end end -- shamans can be hanging out in/near the temple -for i=1,math.random(2 - 1,2*3) do - des.monster({ id = "orc shaman", coord = near_temple:rndcoord(0), peaceful=0 }); +-- one of the shamans is higher level +for i=1,math.random(1,6) do + des.monster({ id = "orc shaman", coord = near_temple:rndcoord(0), peaceful=0, + m_lev_adj = (i == 1) and 3 or 0 }); end -- these are not such a big deal -- to run into outside the bars diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index f4d2a35ca..9d7415ba5 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1588,6 +1588,7 @@ priest donation amounts are explicitly stated, randomized slightly, based on amulet of magical breathing increases power regeneration change some command keys, 'v' is now chronicle, 'V' is versionshort, m-prefix 'V' is longer version, remove key binding of #history +one orc-town shaman has a higher level, affecting spellcasting Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/doc/lua.adoc b/doc/lua.adoc index 3d03342b8..9cfa1cd8e 100644 --- a/doc/lua.adoc +++ b/doc/lua.adoc @@ -819,6 +819,7 @@ The hash parameter accepts the following keys: | stunned | boolean | | confused | boolean | | waiting | boolean | monster will wait until hero gets next to it +| m_lev_adj | integer | monster's level adjustment | tail | boolean | generate worm without a tail? | group | boolean | generate a group of monsters? | adjacentok | boolean | is adjacent location ok, if given one is not suitable? diff --git a/include/sp_lev.h b/include/sp_lev.h index 769c6a1c7..52e89978c 100644 --- a/include/sp_lev.h +++ b/include/sp_lev.h @@ -145,6 +145,7 @@ typedef struct { schar peaceful, asleep; short female, invis, cancelled, revived, avenge, fleeing, blinded, paralyzed, stunned, confused, waiting; + short m_lev_adj; long seentraps; short has_invent; mmflags_nht mm_flags; /* makemon flags */ diff --git a/src/sp_lev.c b/src/sp_lev.c index 2e1b4ab65..d6324827e 100644 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -2165,6 +2165,14 @@ create_monster(monster *m, struct mkroom *croom) if (vampshifted(mtmp) && m->appear != M_AP_MONSTER) (void) newcham(mtmp, &mons[mtmp->cham], NO_NC_FLAGS); } + if (m->m_lev_adj) { + if (mtmp->m_lev + m->m_lev_adj > 49) + mtmp->m_lev = 49; + else if (mtmp->m_lev + m->m_lev_adj < 0) + mtmp->m_lev = 0; + else + mtmp->m_lev += m->m_lev_adj; + } if (!(m->has_invent & DEFAULT_INVENT)) { /* guard against someone accidentally specifying e.g. quest nemesis * with custom inventory that lacks Bell or quest artifact but @@ -3298,6 +3306,7 @@ lspo_monster(lua_State *L) tmpmons.stunned = get_table_boolean_opt(L, "stunned", FALSE); tmpmons.confused = get_table_boolean_opt(L, "confused", FALSE); tmpmons.waiting = get_table_boolean_opt(L, "waiting", FALSE); + tmpmons.m_lev_adj = get_table_int_opt(L, "m_lev_adj", 0); tmpmons.seentraps = 0; /* TODO: list of trap names to bitfield */ keep_default_invent = get_table_boolean_opt(L, "keep_default_invent", -1); From d80e1947354595b5cbc2599d919cba680ef2ff7f Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Mon, 23 Mar 2026 14:51:48 +0200 Subject: [PATCH 336/442] Split mcast_spell into separate functions --- src/mcastu.c | 769 +++++++++++++++++++++++++++++---------------------- 1 file changed, 443 insertions(+), 326 deletions(-) diff --git a/src/mcastu.c b/src/mcastu.c index 50aafc7f3..3e32f2e4f 100644 --- a/src/mcastu.c +++ b/src/mcastu.c @@ -36,6 +36,22 @@ staticfn void cursetxt(struct monst *, boolean); staticfn int choose_magic_spell(struct monst *); staticfn int choose_clerical_spell(struct monst *); staticfn int m_cure_self(struct monst *, int); +staticfn void mcast_death_touch(struct monst *); +staticfn void mcast_clone_wiz(struct monst *); +staticfn void mcast_summon_mons(struct monst *); +staticfn void mcast_destroy_armor(void); +staticfn void mcast_weaken_you(struct monst *, int); +staticfn void mcast_disappear(struct monst *); +staticfn void mcast_stun_you(int); +staticfn int mcast_geyser(int); +staticfn int mcast_fire_pillar(struct monst *, int); +staticfn int mcast_lightning(struct monst *, int); +staticfn int mcast_psi_bolt(int); +staticfn int mcast_open_wounds(int); +staticfn void mcast_insects(struct monst *); +staticfn void mcast_blind_you(void); +staticfn int mcast_paralyze(struct monst *); +staticfn void mcast_confuse_you(struct monst *); staticfn void mcast_spell(struct monst *, int, int); staticfn boolean is_undirected_spell(int); staticfn boolean spell_would_be_useless(struct monst *, int); @@ -435,6 +451,410 @@ death_inflicted_by( * Monster wizard and cleric spellcasting functions. */ +staticfn void +mcast_death_touch(struct monst *mtmp) +{ + pline("Oh no, %s's using the touch of death!", mhe(mtmp)); + if (nonliving(gy.youmonst.data) || is_demon(gy.youmonst.data)) { + You("seem no deader than before."); + } else if (!Antimagic && rn2(mtmp->m_lev) > 12) { + if (Hallucination) { + You("have an out of body experience."); + } else { + touch_of_death(mtmp); + } + monstunseesu(M_SEEN_MAGR); + } else { + if (Antimagic) { + shieldeff(u.ux, u.uy); + monstseesu(M_SEEN_MAGR); + } + pline("Lucky for you, it didn't work!"); + } +} + +staticfn void +mcast_clone_wiz(struct monst *mtmp) +{ + if (mtmp->iswiz && svc.context.no_of_wizards == 1) { + pline("Double Trouble..."); + clonewiz(); + } else + impossible("bad wizard cloning?"); +} + +staticfn void +mcast_summon_mons(struct monst *mtmp) +{ + int count = nasty(mtmp); + + if (!count) { + ; /* nothing was created? */ + } else if (mtmp->iswiz) { + SetVoice(mtmp, 0, 80, 0); + verbalize("Destroy the thief, my pet%s!", plur(count)); + } else { + boolean one = (count == 1); + const char *mappear = one ? "A monster appears" + : "Monsters appear"; + + /* messages not quite right if plural monsters created but + only a single monster is seen */ + if (Invis && !perceives(mtmp->data) + && (mtmp->mux != u.ux || mtmp->muy != u.uy)) + pline("%s %s a spot near you!", mappear, + one ? "at" : "around"); + else if (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy)) + pline("%s %s your displaced image!", mappear, + one ? "by" : "around"); + else + pline("%s from nowhere!", mappear); + } +} + +staticfn void +mcast_destroy_armor(void) +{ + if (Antimagic) { + shieldeff(u.ux, u.uy); + monstseesu(M_SEEN_MAGR); + pline("A field of force surrounds you!"); + } else if (!destroy_arm()) { + Your("skin itches."); + } else { + /* monsters only realize you aren't magic-protected if armor is + actually destroyed */ + monstunseesu(M_SEEN_MAGR); + } +} + +staticfn void +mcast_weaken_you(struct monst *mtmp, int dmg) +{ + if (Antimagic) { + shieldeff(u.ux, u.uy); + monstseesu(M_SEEN_MAGR); + You_feel("momentarily weakened."); + } else { + char kbuf[BUFSZ]; + + You("suddenly feel weaker!"); + dmg = mtmp->m_lev - 6; + if (dmg < 1) /* paranoia since only chosen when m_lev is high */ + dmg = 1; + if (Half_spell_damage) + dmg = (dmg + 1) / 2; + losestr(rnd(dmg), + death_inflicted_by(kbuf, "strength loss", mtmp), + KILLED_BY); + svk.killer.name[0] = '\0'; /* not killed if we get here... */ + monstunseesu(M_SEEN_MAGR); + } +} + +staticfn void +mcast_disappear(struct monst *mtmp) +{ + if (!mtmp->minvis && !mtmp->invis_blkd) { + if (canseemon(mtmp)) + pline_mon(mtmp, "%s suddenly %s!", Monnam(mtmp), + !See_invisible ? "disappears" : "becomes transparent"); + mon_set_minvis(mtmp, FALSE); + if (cansee(mtmp->mx, mtmp->my) && !canspotmon(mtmp)) + map_invisible(mtmp->mx, mtmp->my); + } else + impossible("no reason for monster to cast disappear spell?"); +} + +staticfn void +mcast_stun_you(int dmg) +{ + if (Antimagic || Free_action) { + shieldeff(u.ux, u.uy); + monstseesu(M_SEEN_MAGR); + if (!Stunned) + You_feel("momentarily disoriented."); + make_stunned(1L, FALSE); + } else { + You(Stunned ? "struggle to keep your balance." : "reel..."); + dmg = d(ACURR(A_DEX) < 12 ? 6 : 4, 4); + if (Half_spell_damage) + dmg = (dmg + 1) / 2; + make_stunned((HStun & TIMEOUT) + (long) dmg, FALSE); + monstunseesu(M_SEEN_MAGR); + } +} + +staticfn int +mcast_geyser(int dmg) +{ + /* this is physical damage (force not heat), + * not magical damage or fire damage + */ + pline("A sudden geyser slams into you from nowhere!"); + dmg = d(8, 6); + if (Half_physical_damage) + dmg = (dmg + 1) / 2; +#if 0 /* since inventory items aren't affected, don't include this */ + /* make floor items wet */ + water_damage_chain(level.objects[u.ux][u.uy], TRUE); +#endif + return dmg; +} + +staticfn int +mcast_fire_pillar(struct monst *mtmp, int dmg) +{ + int orig_dmg; + + pline("A pillar of fire strikes all around you!"); + orig_dmg = dmg = d(8, 6); + if (Fire_resistance) { + shieldeff(u.ux, u.uy); + monstseesu(M_SEEN_FIRE); + dmg = 0; + } else { + monstunseesu(M_SEEN_FIRE); + } + if (Half_spell_damage) + dmg = (dmg + 1) / 2; + burn_away_slime(); + (void) burnarmor(&gy.youmonst); + /* item destruction dmg */ + (void) destroy_items(&gy.youmonst, AD_FIRE, orig_dmg); + ignite_items(gi.invent); + /* burn up flammable items on the floor, melt ice terrain */ + mon_spell_hits_spot(mtmp, AD_FIRE, u.ux, u.uy); + return dmg; +} + +staticfn int +mcast_lightning(struct monst *mtmp, int dmg) +{ + int orig_dmg; + boolean reflects; + + Soundeffect(se_bolt_of_lightning, 80); + pline("A bolt of lightning strikes down at you from above!"); + reflects = ureflects("It bounces off your %s%s.", ""); + orig_dmg = dmg = d(8, 6); + if (reflects || Shock_resistance) { + shieldeff(u.ux, u.uy); + dmg = 0; + if (reflects) { + monstseesu(M_SEEN_REFL); + return dmg; + } + monstunseesu(M_SEEN_REFL); + monstseesu(M_SEEN_ELEC); + } else { + monstunseesu(M_SEEN_ELEC | M_SEEN_REFL); + } + if (Half_spell_damage) + dmg = (dmg + 1) / 2; + (void) destroy_items(&gy.youmonst, AD_ELEC, orig_dmg); + /* lightning might destroy iron bars if hero is on such a spot; + reflection protects terrain here [execution won't get here due + to 'if (reflects) break' above] but hero resistance doesn't; + do this before maybe blinding the hero via flashburn() */ + mon_spell_hits_spot(mtmp, AD_ELEC, u.ux, u.uy); + /* blind hero; no effect if already blind */ + (void) flashburn((long) rnd(100), TRUE); + return dmg; +} + +staticfn int +mcast_psi_bolt(int dmg) +{ + /* prior to 3.4.0 Antimagic was setting the damage to 1--this + made the spell virtually harmless to players with magic res. */ + if (Antimagic) { + shieldeff(u.ux, u.uy); + monstseesu(M_SEEN_MAGR); + dmg = (dmg + 1) / 2; + } else { + monstunseesu(M_SEEN_MAGR); + } + if (dmg <= 5) + You("get a slight %sache.", body_part(HEAD)); + else if (dmg <= 10) + Your("brain is on fire!"); + else if (dmg <= 20) + Your("%s suddenly aches painfully!", body_part(HEAD)); + else + Your("%s suddenly aches very painfully!", body_part(HEAD)); + return dmg; +} + +staticfn int +mcast_open_wounds(int dmg) +{ + if (Antimagic) { + shieldeff(u.ux, u.uy); + monstseesu(M_SEEN_MAGR); + dmg = (dmg + 1) / 2; + } else { + monstunseesu(M_SEEN_MAGR); + } + if (dmg <= 5) + Your("skin itches badly for a moment."); + else if (dmg <= 10) + pline("Wounds appear on your body!"); + else if (dmg <= 20) + pline("Severe wounds appear on your body!"); + else + Your("body is covered with painful wounds!"); + return dmg; +} + +staticfn void +mcast_insects(struct monst *mtmp) +{ + /* Try for insects, and if there are none + left, go for (sticks to) snakes. -3. */ + struct permonst *pm = mkclass(S_ANT, 0); + struct monst *mtmp2 = (struct monst *) 0; + char whatbuf[QBUFSZ], let = (pm ? S_ANT : S_SNAKE); + boolean success = FALSE, seecaster; + int i, quan, oldseen, newseen; + coord bypos; + const char *fmt, *what; + + oldseen = monster_census(TRUE); + quan = (mtmp->m_lev < 2) ? 1 : rnd((int) mtmp->m_lev / 2); + if (quan < 3) + quan = 3; + for (i = 0; i <= quan; i++) { + if (!enexto(&bypos, mtmp->mux, mtmp->muy, mtmp->data)) + return; + if ((pm = mkclass(let, 0)) != 0 + && (mtmp2 = makemon(pm, bypos.x, bypos.y, MM_ANGRY | MM_NOMSG)) + != 0) { + success = TRUE; + mtmp2->msleeping = mtmp2->mpeaceful = mtmp2->mtame = 0; + set_malign(mtmp2); + } + } + newseen = monster_census(TRUE); + + /* not canspotmon() which includes unseen things sensed via warning */ + seecaster = canseemon(mtmp) || tp_sensemon(mtmp) || Detect_monsters; + what = (let == S_SNAKE) ? "snakes" : "insects"; + if (Hallucination) + what = makeplural(bogusmon(whatbuf, (char *) 0)); + + fmt = 0; + if (!seecaster) { + if (newseen <= oldseen || Unaware) { + /* unseen caster fails or summons unseen critters, + or unconscious hero ("You dream that you hear...") */ + You_hear("someone summoning %s.", what); + } else { + char *arg; + + if (what != whatbuf) + what = strcpy(whatbuf, what); + /* unseen caster summoned seen critter(s) */ + arg = (newseen == oldseen + 1) ? an(makesingular(what)) + : whatbuf; + if (!Deaf) { + Soundeffect(se_someone_summoning, 100); + You_hear("someone summoning something, and %s %s.", arg, + vtense(arg, "appear")); + } else { + pline("%s %s.", upstart(arg), vtense(arg, "appear")); + } + } + + /* seen caster, possibly producing unseen--or just one--critters; + hero is told what the caster is doing and doesn't necessarily + observe complete accuracy of that caster's results (in other + words, no need to fuss with visibility or singularization; + player is told what's happening even if hero is unconscious) */ + } else if (!success) { + fmt = "%s casts at a clump of sticks, but nothing happens.%s"; + what = ""; + } else if (let == S_SNAKE) { + fmt = "%s transforms a clump of sticks into %s!"; + } else if (Invis && !perceives(mtmp->data) + && (mtmp->mux != u.ux || mtmp->muy != u.uy)) { + fmt = "%s summons %s around a spot near you!"; + } else if (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy)) { + fmt = "%s summons %s around your displaced image!"; + } else { + fmt = "%s summons %s!"; + } + if (fmt) { + DISABLE_WARNING_FORMAT_NONLITERAL; + pline_mon(mtmp, fmt, Monnam(mtmp), what); + RESTORE_WARNING_FORMAT_NONLITERAL; + } +} + +staticfn void +mcast_blind_you(void) +{ + /* note: resists_blnd() doesn't apply here */ + if (!Blinded) { + int num_eyes = eyecount(gy.youmonst.data); + + pline("Scales cover your %s!", (num_eyes == 1) + ? body_part(EYE) + : makeplural(body_part(EYE))); + make_blinded(Half_spell_damage ? 100L : 200L, FALSE); + if (!Blind) + Your1(vision_clears); + } else + impossible("no reason for monster to cast blindness spell?"); +} + +staticfn int +mcast_paralyze(struct monst *mtmp) +{ + int dmg = 0; + + if (Antimagic || Free_action) { + shieldeff(u.ux, u.uy); + monstseesu(M_SEEN_MAGR); + if (gm.multi >= 0) + You("stiffen briefly."); + dmg = 1; /* to produce nomul(-1), not actual damage */ + } else { + if (gm.multi >= 0) + You("are frozen in place!"); + dmg = 4 + (int) mtmp->m_lev; + if (Half_spell_damage) + dmg = (dmg + 1) / 2; + monstunseesu(M_SEEN_MAGR); + } + nomul(-dmg); + gm.multi_reason = "paralyzed by a monster"; + gn.nomovemsg = 0; + return dmg; +} + +staticfn void +mcast_confuse_you(struct monst *mtmp) +{ + if (Antimagic) { + shieldeff(u.ux, u.uy); + monstseesu(M_SEEN_MAGR); + You_feel("momentarily dizzy."); + } else { + boolean oldprop = !!Confusion; + int dmg = (int) mtmp->m_lev; + + if (Half_spell_damage) + dmg = (dmg + 1) / 2; + make_confused(HConfusion + dmg, TRUE); + if (Hallucination) + You_feel("%s!", oldprop ? "trippier" : "trippy"); + else + You_feel("%sconfused!", oldprop ? "more " : ""); + monstunseesu(M_SEEN_MAGR); + } +} + /* If dmg is zero, then the monster is not casting at you. If the monster is intentionally not casting at you, we have previously @@ -446,8 +866,6 @@ death_inflicted_by( staticfn void mcast_spell(struct monst *mtmp, int dmg, int spellnum) { - int orig_dmg = 0; - if (dmg < 0) { impossible("monster cast spell (%d) with negative dmg (%d)?", spellnum, dmg); @@ -460,61 +878,17 @@ mcast_spell(struct monst *mtmp, int dmg, int spellnum) switch (spellnum) { case MCAST_DEATH_TOUCH: - pline("Oh no, %s's using the touch of death!", mhe(mtmp)); - if (nonliving(gy.youmonst.data) || is_demon(gy.youmonst.data)) { - You("seem no deader than before."); - } else if (!Antimagic && rn2(mtmp->m_lev) > 12) { - if (Hallucination) { - You("have an out of body experience."); - } else { - touch_of_death(mtmp); - } - monstunseesu(M_SEEN_MAGR); - } else { - if (Antimagic) { - shieldeff(u.ux, u.uy); - monstseesu(M_SEEN_MAGR); - } - pline("Lucky for you, it didn't work!"); - } + mcast_death_touch(mtmp); dmg = 0; break; case MCAST_CLONE_WIZ: - if (mtmp->iswiz && svc.context.no_of_wizards == 1) { - pline("Double Trouble..."); - clonewiz(); - dmg = 0; - } else - impossible("bad wizard cloning?"); - break; - case MCAST_SUMMON_MONS: { - int count = nasty(mtmp); - - if (!count) { - ; /* nothing was created? */ - } else if (mtmp->iswiz) { - SetVoice(mtmp, 0, 80, 0); - verbalize("Destroy the thief, my pet%s!", plur(count)); - } else { - boolean one = (count == 1); - const char *mappear = one ? "A monster appears" - : "Monsters appear"; - - /* messages not quite right if plural monsters created but - only a single monster is seen */ - if (Invis && !perceives(mtmp->data) - && (mtmp->mux != u.ux || mtmp->muy != u.uy)) - pline("%s %s a spot near you!", mappear, - one ? "at" : "around"); - else if (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy)) - pline("%s %s your displaced image!", mappear, - one ? "by" : "around"); - else - pline("%s from nowhere!", mappear); - } + mcast_clone_wiz(mtmp); + dmg = 0; + break; + case MCAST_SUMMON_MONS: + mcast_summon_mons(mtmp); dmg = 0; break; - } case MCAST_AGGRAVATION: You_feel("that monsters are aware of your presence."); aggravate(); @@ -526,68 +900,19 @@ mcast_spell(struct monst *mtmp, int dmg, int spellnum) dmg = 0; break; case MCAST_DESTRY_ARMR: - if (Antimagic) { - shieldeff(u.ux, u.uy); - monstseesu(M_SEEN_MAGR); - pline("A field of force surrounds you!"); - } else if (!destroy_arm()) { - Your("skin itches."); - } else { - /* monsters only realize you aren't magic-protected if armor is - actually destroyed */ - monstunseesu(M_SEEN_MAGR); - } + mcast_destroy_armor(); dmg = 0; break; case MCAST_WEAKEN_YOU: /* drain strength */ - if (Antimagic) { - shieldeff(u.ux, u.uy); - monstseesu(M_SEEN_MAGR); - You_feel("momentarily weakened."); - } else { - char kbuf[BUFSZ]; - - You("suddenly feel weaker!"); - dmg = mtmp->m_lev - 6; - if (dmg < 1) /* paranoia since only chosen when m_lev is high */ - dmg = 1; - if (Half_spell_damage) - dmg = (dmg + 1) / 2; - losestr(rnd(dmg), - death_inflicted_by(kbuf, "strength loss", mtmp), - KILLED_BY); - svk.killer.name[0] = '\0'; /* not killed if we get here... */ - monstunseesu(M_SEEN_MAGR); - } + mcast_weaken_you(mtmp, dmg); dmg = 0; break; case MCAST_DISAPPEAR: /* makes self invisible */ - if (!mtmp->minvis && !mtmp->invis_blkd) { - if (canseemon(mtmp)) - pline_mon(mtmp, "%s suddenly %s!", Monnam(mtmp), - !See_invisible ? "disappears" : "becomes transparent"); - mon_set_minvis(mtmp, FALSE); - if (cansee(mtmp->mx, mtmp->my) && !canspotmon(mtmp)) - map_invisible(mtmp->mx, mtmp->my); - dmg = 0; - } else - impossible("no reason for monster to cast disappear spell?"); + mcast_disappear(mtmp); + dmg = 0; break; case MCAST_STUN_YOU: - if (Antimagic || Free_action) { - shieldeff(u.ux, u.uy); - monstseesu(M_SEEN_MAGR); - if (!Stunned) - You_feel("momentarily disoriented."); - make_stunned(1L, FALSE); - } else { - You(Stunned ? "struggle to keep your balance." : "reel..."); - dmg = d(ACURR(A_DEX) < 12 ? 6 : 4, 4); - if (Half_spell_damage) - dmg = (dmg + 1) / 2; - make_stunned((HStun & TIMEOUT) + (long) dmg, FALSE); - monstunseesu(M_SEEN_MAGR); - } + mcast_stun_you(dmg); dmg = 0; break; case MCAST_HASTE_SELF: @@ -598,242 +923,34 @@ mcast_spell(struct monst *mtmp, int dmg, int spellnum) dmg = m_cure_self(mtmp, dmg); break; case MCAST_PSI_BOLT: - /* prior to 3.4.0 Antimagic was setting the damage to 1--this - made the spell virtually harmless to players with magic res. */ - if (Antimagic) { - shieldeff(u.ux, u.uy); - monstseesu(M_SEEN_MAGR); - dmg = (dmg + 1) / 2; - } else { - monstunseesu(M_SEEN_MAGR); - } - if (dmg <= 5) - You("get a slight %sache.", body_part(HEAD)); - else if (dmg <= 10) - Your("brain is on fire!"); - else if (dmg <= 20) - Your("%s suddenly aches painfully!", body_part(HEAD)); - else - Your("%s suddenly aches very painfully!", body_part(HEAD)); + dmg = mcast_psi_bolt(dmg); break; case MCAST_GEYSER: - /* this is physical damage (force not heat), - * not magical damage or fire damage - */ - pline("A sudden geyser slams into you from nowhere!"); - dmg = d(8, 6); - if (Half_physical_damage) - dmg = (dmg + 1) / 2; -#if 0 /* since inventory items aren't affected, don't include this */ - /* make floor items wet */ - water_damage_chain(level.objects[u.ux][u.uy], TRUE); -#endif + dmg = mcast_geyser(dmg); break; case MCAST_FIRE_PILLAR: - pline("A pillar of fire strikes all around you!"); - orig_dmg = dmg = d(8, 6); - if (Fire_resistance) { - shieldeff(u.ux, u.uy); - monstseesu(M_SEEN_FIRE); - dmg = 0; - } else { - monstunseesu(M_SEEN_FIRE); - } - if (Half_spell_damage) - dmg = (dmg + 1) / 2; - burn_away_slime(); - (void) burnarmor(&gy.youmonst); - /* item destruction dmg */ - (void) destroy_items(&gy.youmonst, AD_FIRE, orig_dmg); - ignite_items(gi.invent); - /* burn up flammable items on the floor, melt ice terrain */ - mon_spell_hits_spot(mtmp, AD_FIRE, u.ux, u.uy); + dmg = mcast_fire_pillar(mtmp, dmg); break; - case MCAST_LIGHTNING: { - boolean reflects; - - Soundeffect(se_bolt_of_lightning, 80); - pline("A bolt of lightning strikes down at you from above!"); - reflects = ureflects("It bounces off your %s%s.", ""); - orig_dmg = dmg = d(8, 6); - if (reflects || Shock_resistance) { - shieldeff(u.ux, u.uy); - dmg = 0; - if (reflects) { - monstseesu(M_SEEN_REFL); - break; - } - monstunseesu(M_SEEN_REFL); - monstseesu(M_SEEN_ELEC); - } else { - monstunseesu(M_SEEN_ELEC | M_SEEN_REFL); - } - if (Half_spell_damage) - dmg = (dmg + 1) / 2; - (void) destroy_items(&gy.youmonst, AD_ELEC, orig_dmg); - /* lightning might destroy iron bars if hero is on such a spot; - reflection protects terrain here [execution won't get here due - to 'if (reflects) break' above] but hero resistance doesn't; - do this before maybe blinding the hero via flashburn() */ - mon_spell_hits_spot(mtmp, AD_ELEC, u.ux, u.uy); - /* blind hero; no effect if already blind */ - (void) flashburn((long) rnd(100), TRUE); + case MCAST_LIGHTNING: + dmg = mcast_lightning(mtmp, dmg); break; - } - case MCAST_INSECTS: { - /* Try for insects, and if there are none - left, go for (sticks to) snakes. -3. */ - struct permonst *pm = mkclass(S_ANT, 0); - struct monst *mtmp2 = (struct monst *) 0; - char whatbuf[QBUFSZ], let = (pm ? S_ANT : S_SNAKE); - boolean success = FALSE, seecaster; - int i, quan, oldseen, newseen; - coord bypos; - const char *fmt, *what; - - oldseen = monster_census(TRUE); - quan = (mtmp->m_lev < 2) ? 1 : rnd((int) mtmp->m_lev / 2); - if (quan < 3) - quan = 3; - for (i = 0; i <= quan; i++) { - if (!enexto(&bypos, mtmp->mux, mtmp->muy, mtmp->data)) - break; - if ((pm = mkclass(let, 0)) != 0 - && (mtmp2 = makemon(pm, bypos.x, bypos.y, MM_ANGRY | MM_NOMSG)) - != 0) { - success = TRUE; - mtmp2->msleeping = mtmp2->mpeaceful = mtmp2->mtame = 0; - set_malign(mtmp2); - } - } - newseen = monster_census(TRUE); - - /* not canspotmon() which includes unseen things sensed via warning */ - seecaster = canseemon(mtmp) || tp_sensemon(mtmp) || Detect_monsters; - what = (let == S_SNAKE) ? "snakes" : "insects"; - if (Hallucination) - what = makeplural(bogusmon(whatbuf, (char *) 0)); - - fmt = 0; - if (!seecaster) { - if (newseen <= oldseen || Unaware) { - /* unseen caster fails or summons unseen critters, - or unconscious hero ("You dream that you hear...") */ - You_hear("someone summoning %s.", what); - } else { - char *arg; - - if (what != whatbuf) - what = strcpy(whatbuf, what); - /* unseen caster summoned seen critter(s) */ - arg = (newseen == oldseen + 1) ? an(makesingular(what)) - : whatbuf; - if (!Deaf) { - Soundeffect(se_someone_summoning, 100); - You_hear("someone summoning something, and %s %s.", arg, - vtense(arg, "appear")); - } else { - pline("%s %s.", upstart(arg), vtense(arg, "appear")); - } - } - - /* seen caster, possibly producing unseen--or just one--critters; - hero is told what the caster is doing and doesn't necessarily - observe complete accuracy of that caster's results (in other - words, no need to fuss with visibility or singularization; - player is told what's happening even if hero is unconscious) */ - } else if (!success) { - fmt = "%s casts at a clump of sticks, but nothing happens.%s"; - what = ""; - } else if (let == S_SNAKE) { - fmt = "%s transforms a clump of sticks into %s!"; - } else if (Invis && !perceives(mtmp->data) - && (mtmp->mux != u.ux || mtmp->muy != u.uy)) { - fmt = "%s summons %s around a spot near you!"; - } else if (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy)) { - fmt = "%s summons %s around your displaced image!"; - } else { - fmt = "%s summons %s!"; - } - if (fmt) { - DISABLE_WARNING_FORMAT_NONLITERAL; - pline_mon(mtmp, fmt, Monnam(mtmp), what); - RESTORE_WARNING_FORMAT_NONLITERAL; - } + case MCAST_INSECTS: + mcast_insects(mtmp); dmg = 0; break; - } case MCAST_BLIND_YOU: - /* note: resists_blnd() doesn't apply here */ - if (!Blinded) { - int num_eyes = eyecount(gy.youmonst.data); - - pline("Scales cover your %s!", (num_eyes == 1) - ? body_part(EYE) - : makeplural(body_part(EYE))); - make_blinded(Half_spell_damage ? 100L : 200L, FALSE); - if (!Blind) - Your1(vision_clears); - dmg = 0; - } else - impossible("no reason for monster to cast blindness spell?"); + mcast_blind_you(); + dmg = 0; break; case MCAST_PARALYZE: - if (Antimagic || Free_action) { - shieldeff(u.ux, u.uy); - monstseesu(M_SEEN_MAGR); - if (gm.multi >= 0) - You("stiffen briefly."); - dmg = 1; /* to produce nomul(-1), not actual damage */ - } else { - if (gm.multi >= 0) - You("are frozen in place!"); - dmg = 4 + (int) mtmp->m_lev; - if (Half_spell_damage) - dmg = (dmg + 1) / 2; - monstunseesu(M_SEEN_MAGR); - } - nomul(-dmg); - gm.multi_reason = "paralyzed by a monster"; - gn.nomovemsg = 0; - dmg = 0; + dmg = mcast_paralyze(mtmp); break; case MCAST_CONFUSE_YOU: - if (Antimagic) { - shieldeff(u.ux, u.uy); - monstseesu(M_SEEN_MAGR); - You_feel("momentarily dizzy."); - } else { - boolean oldprop = !!Confusion; - - dmg = (int) mtmp->m_lev; - if (Half_spell_damage) - dmg = (dmg + 1) / 2; - make_confused(HConfusion + dmg, TRUE); - if (Hallucination) - You_feel("%s!", oldprop ? "trippier" : "trippy"); - else - You_feel("%sconfused!", oldprop ? "more " : ""); - monstunseesu(M_SEEN_MAGR); - } + mcast_confuse_you(mtmp); dmg = 0; break; case MCAST_OPEN_WOUNDS: - if (Antimagic) { - shieldeff(u.ux, u.uy); - monstseesu(M_SEEN_MAGR); - dmg = (dmg + 1) / 2; - } else { - monstunseesu(M_SEEN_MAGR); - } - if (dmg <= 5) - Your("skin itches badly for a moment."); - else if (dmg <= 10) - pline("Wounds appear on your body!"); - else if (dmg <= 20) - pline("Severe wounds appear on your body!"); - else - Your("body is covered with painful wounds!"); + dmg = mcast_open_wounds(dmg); break; default: impossible("mcastu: invalid magic spell (%d)", spellnum); From e07eb1a5502721a12346d17bd3f3e7d8630d92d1 Mon Sep 17 00:00:00 2001 From: Ingo Paschke Date: Mon, 23 Mar 2026 20:46:08 +0100 Subject: [PATCH 337/442] Fix vision: remove NO_MACRO_CPATH The non-macro q_path() function wrappers in vision.c pass the first two arguments in the wrong order to the _q*_path() functions, swapping rows and columns. This causes the Bresenham line-of-sight code to use column values as row indices into viz_clear_rows[ROWNO], producing out-of-bounds access and infinite loops. NO_MACRO_CPATH selects these broken function wrappers. It was only defined for Amiga (in config1.h), so the bug never triggered on other platforms. Remove the define to use the correct macro versions. --- include/config1.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/config1.h b/include/config1.h index 3a8632564..402e0e73c 100644 --- a/include/config1.h +++ b/include/config1.h @@ -95,7 +95,7 @@ #undef UNIX #define DLB #define HACKDIR "NetHack:" -#define NO_MACRO_CPATH + #endif /* From 1ca15d99ff125c52c4bcb4f63d1f2e5ee817c0de Mon Sep 17 00:00:00 2001 From: Ingo Paschke Date: Mon, 23 Mar 2026 20:46:31 +0100 Subject: [PATCH 338/442] Fix clipping: move cliparound after vision_recalc cliparound() was called before rhack(), triggering a map redraw with stale vision data followed by a second correct redraw. Move it after vision_recalc() so the map is redrawn once with correct data. --- src/allmain.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/allmain.c b/src/allmain.c index 2db536e5e..4dd27680c 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -13,6 +13,7 @@ staticfn void moveloop_preamble(boolean); staticfn void u_calc_moveamt(int); + staticfn void maybe_do_tutorial(void); #ifdef POSITIONBAR staticfn void do_positionbar(void); @@ -511,11 +512,6 @@ moveloop_core(void) return; } -#ifdef CLIPPING - /* just before rhack */ - cliparound(u.ux, u.uy); -#endif - u.umoved = FALSE; if (gm.multi > 0) { @@ -546,6 +542,11 @@ moveloop_core(void) if (gv.vision_full_recalc) vision_recalc(0); /* vision! */ +#ifdef CLIPPING + /* after rhack() and vision_recalc() so that the map is redrawn + once with correct vision data, not twice (overshoot+correct) */ + cliparound(u.ux, u.uy); +#endif /* when running in non-tport mode, this gets done through domove() */ if ((!svc.context.run || flags.runmode == RUN_TPORT) && (gm.multi && (!svc.context.travel ? !(gm.multi % 7) From 7b89255ea8a7ec89b4989b64d3c12a017f2d9018 Mon Sep 17 00:00:00 2001 From: Ingo Paschke Date: Mon, 23 Mar 2026 20:46:56 +0100 Subject: [PATCH 339/442] Move Amiga port files from outdated/ to sys/amiga/ Move the active Amiga source files back into their proper locations. Legacy native build files (Makefile.ami, Build.ami, etc.) remain in outdated/ as they are not used by the cross-compilation build. --- {outdated/include => include}/amiconf.h | 0 {outdated/sys => sys}/amiga/amidos.c | 0 {outdated/sys => sys}/amiga/amidos.p | 0 {outdated/sys => sys}/amiga/amifont.uu | 0 {outdated/sys => sys}/amiga/amifont8.uu | 0 {outdated/sys => sys}/amiga/amigst.c | 0 {outdated/sys => sys}/amiga/amii.hlp | 0 {outdated/sys => sys}/amiga/amimenu.c | 0 {outdated/sys => sys}/amiga/amirip.c | 0 {outdated/sys => sys}/amiga/amistack.c | 0 {outdated/sys => sys}/amiga/amitty.c | 0 {outdated/sys => sys}/amiga/amiwind.c | 0 {outdated/sys => sys}/amiga/amiwind.p | 0 {outdated/sys => sys}/amiga/clipwin.c | 0 {outdated/sys => sys}/amiga/colorwin.c | 0 {outdated/sys => sys}/amiga/grave16.xpm | 0 {outdated/sys => sys}/amiga/txt2iff.c | 0 {outdated/sys => sys}/amiga/winamenu.c | 0 {outdated/sys => sys}/amiga/winami.c | 0 {outdated/sys => sys}/amiga/winami.p | 0 {outdated/sys => sys}/amiga/winchar.c | 0 {outdated/sys => sys}/amiga/windefs.h | 0 {outdated/sys => sys}/amiga/winext.h | 0 {outdated/sys => sys}/amiga/winfuncs.c | 0 {outdated/sys => sys}/amiga/winkey.c | 0 {outdated/sys => sys}/amiga/winproto.h | 0 {outdated/sys => sys}/amiga/winreq.c | 0 {outdated/sys => sys}/amiga/winstr.c | 0 {outdated/sys => sys}/amiga/xpm2iff.c | 0 29 files changed, 0 insertions(+), 0 deletions(-) rename {outdated/include => include}/amiconf.h (100%) rename {outdated/sys => sys}/amiga/amidos.c (100%) rename {outdated/sys => sys}/amiga/amidos.p (100%) rename {outdated/sys => sys}/amiga/amifont.uu (100%) rename {outdated/sys => sys}/amiga/amifont8.uu (100%) rename {outdated/sys => sys}/amiga/amigst.c (100%) rename {outdated/sys => sys}/amiga/amii.hlp (100%) rename {outdated/sys => sys}/amiga/amimenu.c (100%) rename {outdated/sys => sys}/amiga/amirip.c (100%) rename {outdated/sys => sys}/amiga/amistack.c (100%) rename {outdated/sys => sys}/amiga/amitty.c (100%) rename {outdated/sys => sys}/amiga/amiwind.c (100%) rename {outdated/sys => sys}/amiga/amiwind.p (100%) rename {outdated/sys => sys}/amiga/clipwin.c (100%) rename {outdated/sys => sys}/amiga/colorwin.c (100%) rename {outdated/sys => sys}/amiga/grave16.xpm (100%) rename {outdated/sys => sys}/amiga/txt2iff.c (100%) rename {outdated/sys => sys}/amiga/winamenu.c (100%) rename {outdated/sys => sys}/amiga/winami.c (100%) rename {outdated/sys => sys}/amiga/winami.p (100%) rename {outdated/sys => sys}/amiga/winchar.c (100%) rename {outdated/sys => sys}/amiga/windefs.h (100%) rename {outdated/sys => sys}/amiga/winext.h (100%) rename {outdated/sys => sys}/amiga/winfuncs.c (100%) rename {outdated/sys => sys}/amiga/winkey.c (100%) rename {outdated/sys => sys}/amiga/winproto.h (100%) rename {outdated/sys => sys}/amiga/winreq.c (100%) rename {outdated/sys => sys}/amiga/winstr.c (100%) rename {outdated/sys => sys}/amiga/xpm2iff.c (100%) diff --git a/outdated/include/amiconf.h b/include/amiconf.h similarity index 100% rename from outdated/include/amiconf.h rename to include/amiconf.h diff --git a/outdated/sys/amiga/amidos.c b/sys/amiga/amidos.c similarity index 100% rename from outdated/sys/amiga/amidos.c rename to sys/amiga/amidos.c diff --git a/outdated/sys/amiga/amidos.p b/sys/amiga/amidos.p similarity index 100% rename from outdated/sys/amiga/amidos.p rename to sys/amiga/amidos.p diff --git a/outdated/sys/amiga/amifont.uu b/sys/amiga/amifont.uu similarity index 100% rename from outdated/sys/amiga/amifont.uu rename to sys/amiga/amifont.uu diff --git a/outdated/sys/amiga/amifont8.uu b/sys/amiga/amifont8.uu similarity index 100% rename from outdated/sys/amiga/amifont8.uu rename to sys/amiga/amifont8.uu diff --git a/outdated/sys/amiga/amigst.c b/sys/amiga/amigst.c similarity index 100% rename from outdated/sys/amiga/amigst.c rename to sys/amiga/amigst.c diff --git a/outdated/sys/amiga/amii.hlp b/sys/amiga/amii.hlp similarity index 100% rename from outdated/sys/amiga/amii.hlp rename to sys/amiga/amii.hlp diff --git a/outdated/sys/amiga/amimenu.c b/sys/amiga/amimenu.c similarity index 100% rename from outdated/sys/amiga/amimenu.c rename to sys/amiga/amimenu.c diff --git a/outdated/sys/amiga/amirip.c b/sys/amiga/amirip.c similarity index 100% rename from outdated/sys/amiga/amirip.c rename to sys/amiga/amirip.c diff --git a/outdated/sys/amiga/amistack.c b/sys/amiga/amistack.c similarity index 100% rename from outdated/sys/amiga/amistack.c rename to sys/amiga/amistack.c diff --git a/outdated/sys/amiga/amitty.c b/sys/amiga/amitty.c similarity index 100% rename from outdated/sys/amiga/amitty.c rename to sys/amiga/amitty.c diff --git a/outdated/sys/amiga/amiwind.c b/sys/amiga/amiwind.c similarity index 100% rename from outdated/sys/amiga/amiwind.c rename to sys/amiga/amiwind.c diff --git a/outdated/sys/amiga/amiwind.p b/sys/amiga/amiwind.p similarity index 100% rename from outdated/sys/amiga/amiwind.p rename to sys/amiga/amiwind.p diff --git a/outdated/sys/amiga/clipwin.c b/sys/amiga/clipwin.c similarity index 100% rename from outdated/sys/amiga/clipwin.c rename to sys/amiga/clipwin.c diff --git a/outdated/sys/amiga/colorwin.c b/sys/amiga/colorwin.c similarity index 100% rename from outdated/sys/amiga/colorwin.c rename to sys/amiga/colorwin.c diff --git a/outdated/sys/amiga/grave16.xpm b/sys/amiga/grave16.xpm similarity index 100% rename from outdated/sys/amiga/grave16.xpm rename to sys/amiga/grave16.xpm diff --git a/outdated/sys/amiga/txt2iff.c b/sys/amiga/txt2iff.c similarity index 100% rename from outdated/sys/amiga/txt2iff.c rename to sys/amiga/txt2iff.c diff --git a/outdated/sys/amiga/winamenu.c b/sys/amiga/winamenu.c similarity index 100% rename from outdated/sys/amiga/winamenu.c rename to sys/amiga/winamenu.c diff --git a/outdated/sys/amiga/winami.c b/sys/amiga/winami.c similarity index 100% rename from outdated/sys/amiga/winami.c rename to sys/amiga/winami.c diff --git a/outdated/sys/amiga/winami.p b/sys/amiga/winami.p similarity index 100% rename from outdated/sys/amiga/winami.p rename to sys/amiga/winami.p diff --git a/outdated/sys/amiga/winchar.c b/sys/amiga/winchar.c similarity index 100% rename from outdated/sys/amiga/winchar.c rename to sys/amiga/winchar.c diff --git a/outdated/sys/amiga/windefs.h b/sys/amiga/windefs.h similarity index 100% rename from outdated/sys/amiga/windefs.h rename to sys/amiga/windefs.h diff --git a/outdated/sys/amiga/winext.h b/sys/amiga/winext.h similarity index 100% rename from outdated/sys/amiga/winext.h rename to sys/amiga/winext.h diff --git a/outdated/sys/amiga/winfuncs.c b/sys/amiga/winfuncs.c similarity index 100% rename from outdated/sys/amiga/winfuncs.c rename to sys/amiga/winfuncs.c diff --git a/outdated/sys/amiga/winkey.c b/sys/amiga/winkey.c similarity index 100% rename from outdated/sys/amiga/winkey.c rename to sys/amiga/winkey.c diff --git a/outdated/sys/amiga/winproto.h b/sys/amiga/winproto.h similarity index 100% rename from outdated/sys/amiga/winproto.h rename to sys/amiga/winproto.h diff --git a/outdated/sys/amiga/winreq.c b/sys/amiga/winreq.c similarity index 100% rename from outdated/sys/amiga/winreq.c rename to sys/amiga/winreq.c diff --git a/outdated/sys/amiga/winstr.c b/sys/amiga/winstr.c similarity index 100% rename from outdated/sys/amiga/winstr.c rename to sys/amiga/winstr.c diff --git a/outdated/sys/amiga/xpm2iff.c b/sys/amiga/xpm2iff.c similarity index 100% rename from outdated/sys/amiga/xpm2iff.c rename to sys/amiga/xpm2iff.c From 2d597cb9fa99e90517b4b024157f4ae280704b0b Mon Sep 17 00:00:00 2001 From: Ingo Paschke Date: Mon, 23 Mar 2026 20:48:06 +0100 Subject: [PATCH 340/442] Revive Amiga port for NetHack 3.7 Update the Amiga Intuition window port (AMII/AMIV) for the 3.7 window_procs API. Key changes: - Update all window function signatures for 3.7 - Add assembly trampolines for AmigaOS register-based callbacks - Convert all K&R function definitions to C99 - Add cross-compilation build system (cross-pre1/pre2/post.370) using bebbo's m68k-amigaos-gcc with -noixemul -std=gnu17 -m68000 - Clipping fixes: viewport centering, simplified ScrollRaster, duplicate Ctrl-R suppression, glyph buffer invalidation - Add menucolor support in menu rendering - Move native txt2iff.c and xpm2iff.c to outdated/ - Add nethack.cnf and README.amiga --- include/amiconf.h | 8 +- include/winami.h | 1 + include/winprocs.h | 5 +- {sys => outdated/sys}/amiga/txt2iff.c | 0 {sys => outdated/sys}/amiga/xpm2iff.c | 0 src/rip.c | 3 +- sys/amiga/README.amiga | 106 ++++++ sys/amiga/amidos.c | 94 +++-- sys/amiga/amirip.c | 33 +- sys/amiga/amistack.c | 12 +- sys/amiga/amitty.c | 9 +- sys/amiga/amiwind.c | 147 ++++--- sys/amiga/nethack.cnf | 44 +++ sys/amiga/winamenu.c | 149 ++++---- sys/amiga/winami.c | 135 +++---- sys/amiga/winami.p | 8 +- sys/amiga/winchar.c | 371 +++++++----------- sys/amiga/windefs.h | 3 +- sys/amiga/winfuncs.c | 527 ++++++++++++++------------ sys/amiga/winkey.c | 13 +- sys/amiga/winproto.h | 27 +- sys/amiga/winreq.c | 91 ++--- sys/amiga/winstr.c | 48 +-- sys/share/pcmain.c | 5 + sys/unix/hints/include/cross-post.370 | 70 ++++ sys/unix/hints/include/cross-pre1.370 | 10 + sys/unix/hints/include/cross-pre2.370 | 90 +++++ 27 files changed, 1101 insertions(+), 908 deletions(-) rename {sys => outdated/sys}/amiga/txt2iff.c (100%) rename {sys => outdated/sys}/amiga/xpm2iff.c (100%) create mode 100644 sys/amiga/README.amiga create mode 100644 sys/amiga/nethack.cnf diff --git a/include/amiconf.h b/include/amiconf.h index d09801509..b607fd228 100644 --- a/include/amiconf.h +++ b/include/amiconf.h @@ -48,13 +48,9 @@ typedef long off_t; #define PATHLEN 130 /* data librarian defs */ -#ifndef NOCWD_ASSUMPTIONS -#define DLBFILE "NetHack:nhdat" /* main library */ -#define DLBFILE2 "NetHack:nhsdat" /* sound library */ -#else #define DLBFILE "nhdat" /* main library */ -#define DLBFILE2 "nhsdat" /* sound library */ -#endif +/* nhsdat sound library not used in 3.7 */ +#undef DLBFILE2 #ifndef CROSS_TO_AMIGA #define FILENAME_CMP stricmp /* case insensitive */ diff --git a/include/winami.h b/include/winami.h index b9fbfb65c..1cfd85078 100644 --- a/include/winami.h +++ b/include/winami.h @@ -22,6 +22,7 @@ typedef struct amii_mi { char gselector; /* Group selector */ char canselect; /* Can user select this entry. */ char attr; /* Attribute for the line. */ + int color; /* Color for the line (from menucolors). */ char *str; /* The text of the item. */ } amii_menu_item; diff --git a/include/winprocs.h b/include/winprocs.h index c77288891..688d76138 100644 --- a/include/winprocs.h +++ b/include/winprocs.h @@ -13,8 +13,11 @@ enum wp_ids { wp_tty = 1, wp_X11, wp_Qt, wp_mswin, wp_curses, wp_chainin, wp_chainout, wp_safestartup, wp_shim, wp_hup, wp_guistubs, wp_ttystubs, +#if defined(AMIGA) + wp_amii, wp_amiv, +#endif #ifdef OUTDATED_STUFF - wp_mac, wp_Gem, wp_Gnome, wp_amii, wp_amiv, + wp_mac, wp_Gem, wp_Gnome, #endif wp_trace // XXX do we need this? should chainin/out get an id? TBD }; diff --git a/sys/amiga/txt2iff.c b/outdated/sys/amiga/txt2iff.c similarity index 100% rename from sys/amiga/txt2iff.c rename to outdated/sys/amiga/txt2iff.c diff --git a/sys/amiga/xpm2iff.c b/outdated/sys/amiga/xpm2iff.c similarity index 100% rename from sys/amiga/xpm2iff.c rename to outdated/sys/amiga/xpm2iff.c diff --git a/src/rip.c b/src/rip.c index d482eb0a2..c9c64dfae 100644 --- a/src/rip.c +++ b/src/rip.c @@ -9,7 +9,8 @@ necessarily have to be used by a binary with multiple window-ports */ #if defined(TTY_GRAPHICS) || defined(X11_GRAPHICS) || defined(GEM_GRAPHICS) \ - || defined(DUMPLOG) || defined(CURSES_GRAPHICS) || defined(SHIM_GRAPHICS) + || defined(DUMPLOG) || defined(CURSES_GRAPHICS) || defined(SHIM_GRAPHICS) \ + || defined(AMII_GRAPHICS) #define TEXT_TOMBSTONE #endif #if defined(mac) || defined(__BEOS__) diff --git a/sys/amiga/README.amiga b/sys/amiga/README.amiga new file mode 100644 index 000000000..165501014 --- /dev/null +++ b/sys/amiga/README.amiga @@ -0,0 +1,106 @@ + NetHack 3.7 for Amiga + ==================== + +Requirements +------------ + - AmigaOS 3.0 or later (Kickstart 39+) + - 6 MB free RAM minimum (8 MB recommended) + - Hard drive with ~5 MB free space + +Recommended: RTG graphics card (Picasso96/CyberGraphX) for best +tile rendering. Native AGA/OCS/ECS chipsets are supported. + + +Installation +------------ +Extract NH370AMI.ZIP to a directory and assign it: + + assign NetHack: + +The directory should contain: + nethack - game binary + nhdat - data library + symbols - symbol set definitions + sysconf - system configuration + nethack.cnf - game options + hack.font - font descriptor + hack/8 - font bitmap (8pt) + tiles/tiles16.iff - 16-color tiles (OCS/ECS) + tiles/tiles32.iff - 32-color tiles (AGA/RTG) + tomb.iff - tombstone image + record - high score file + +To play: + cd NetHack: + nethack + + +Display Modes +------------- +Two display modes are available, selected in nethack.cnf: + + Text mode (AMII): + OPTIONS=symset:AmigaFont + + Tile mode (AMIV): + OPTIONS=windowtype:amiv + +Tile mode auto-selects tiles32.iff (32 colors, 5 bitplanes) when the +screen supports 32+ colors, otherwise tiles16.iff (16 colors, 4 planes). + + +Configuration +------------- +Edit nethack.cnf for game options. Key settings: + + OPTIONS=windowtype:amiv - tile graphics (default) + OPTIONS=symset:AmigaFont - text mode with line-drawing chars + OPTIONS=menucolors - colored inventory items + OPTIONS=time,lit_corridor - show turn count, lit corridors + OPTIONS=boulder:0 - display boulders as '0' + +Menu color examples (add after OPTIONS=menucolors): + MENUCOLOR=" blessed "=green + MENUCOLOR=" cursed "=red + MENUCOLOR=" uncursed "=cyan + + +Building from Source +-------------------- +Cross-compilation from Linux using bebbo's m68k-amigaos-gcc toolchain +(https://franke.ms/git/bebbo/amiga-gcc): + + # Install toolchain to /opt/amiga + # Clone NetHack 3.7 source + cd NetHack + make fetch-lua + sys/unix/setup.sh sys/unix/hints/linux.370 + make CROSS_TO_AMIGA=1 all + make CROSS_TO_AMIGA=1 package + +The distribution ZIP is created at targets/amiga/NH370AMI.ZIP. + +Toolchain requirements: + - m68k-amigaos-gcc (bebbo's amigaos-cross-toolchain in /opt/amiga) + - -noixemul (libnix) for linking + - -m68000 for maximum compatibility (or -m68020/040/060) + + +Known Issues +------------ + - Native Amiga compilation (SAS/C, DICE) is not supported; + cross-compilation with GCC is required + + +Credits +------- +Olaf Seibert first ported NetHack 2.3 and 3.0 to the Amiga. +Richard Addison, Andrew Church, Jochen Erwied, Mark Gooderum, +Ken Lorber, Greg Olson, Mike Passaretti, and Gregg Wonderly +polished and extended the 3.0 and 3.1 ports. Andrew Church, +Ken Lorber, and Gregg Wonderly are responsible for the 3.2 port. +Janne Salmijärvi resurrected the Amiga port for 3.3 and +Teemu Suikki joined before 3.4.0. + +Updated for NetHack 3.7, cross compile fixes and 32 color tile support by Ingo +Paschke in 2026. diff --git a/sys/amiga/amidos.c b/sys/amiga/amidos.c index 6ec41b5de..3d465ddd2 100644 --- a/sys/amiga/amidos.c +++ b/sys/amiga/amidos.c @@ -9,6 +9,7 @@ #include "hack.h" #include "winami.h" +#include "windefs.h" /* Defined in config.h, let's undefine it here (static function below) */ #undef strcmpi @@ -18,11 +19,17 @@ #include #undef COUNT + #if defined(__SASC_60) || defined(__GNUC__) #include #include #endif +/* POSIX stubs needed by libnix (-noixemul) */ +#ifdef __noixemul__ +int getpid(void) { return (int)FindTask(NULL); } +#endif + #ifdef AZTEC_50 #include #undef strcmpi @@ -34,11 +41,12 @@ #include "NH:sys/amiga/winami.p" #include "NH:sys/amiga/amidos.p" #else -#include "winami.p" +#include "amiwind.p" #include "winami.p" #include "amidos.p" #endif + extern char Initialized; extern struct window_procs amii_procs; struct ami_sysflags sysflags = {0}; @@ -54,13 +62,14 @@ char PATH[PATHLEN] = "NetHack:"; static boolean record_exists(void); void -flushout() +flushout(void) { (void) fflush(stdout); } #ifndef getuid -getuid() +int +getuid(void) { return 1; } @@ -68,26 +77,19 @@ getuid() #ifndef getlogin char * -getlogin() +getlogin(void) { return ((char *) NULL); } #endif -#ifndef AZTEC_50 -int -abs(x) -int x; -{ - return x < 0 ? -x : x; -} -#endif +/* abs() provided by libnix/stdlib */ #ifdef SHELL int -dosh() +dosh(void) { - int i; + int i = 0; char buf[BUFSZ]; extern struct ExecBase *SysBase; @@ -121,8 +123,7 @@ dosh() */ /* TODO: update this for FFS */ long -freediskspace(path) -char *path; +freediskspace(char *path) { #ifdef UNTESTED /* these changes from Patric Mueller for AROS to @@ -131,9 +132,9 @@ char *path; */ unsigned long long freeBytes = 0; #else - register long freeBytes = 0; + long freeBytes = 0; #endif - register struct InfoData *infoData; /* Remember... longword aligned */ + struct InfoData *infoData; /* Remember... longword aligned */ char fileName[32]; /* @@ -145,7 +146,7 @@ char *path; * so must be on the current device, so "" is enough... */ { - register char *colon; + char *colon; strncpy(fileName, path, sizeof(fileName) - 1); fileName[31] = 0; @@ -191,12 +192,11 @@ char *path; } long -filesize(file) -char *file; +filesize(char *file) { - register BPTR fileLock; - register struct FileInfoBlock *fileInfoBlock; - register long size = 0; + BPTR fileLock; + struct FileInfoBlock *fileInfoBlock; + long size = 0; fileInfoBlock = (struct FileInfoBlock *) alloc(sizeof(struct FileInfoBlock)); @@ -212,8 +212,8 @@ char *file; #if 0 void -eraseall(path, files) -const char *path, *files; +void +eraseall(const char *path, const char *files) { BPTR dirLock, dirLock2; struct FileInfoBlock *fibp; @@ -248,12 +248,11 @@ const char *path, *files; #if 0 /* Unused */ #define COPYSIZE 4096 -char *CopyFile(from, to) -const char *from, *to; +char *CopyFile(const char *from, const char *to) { - register BPTR fromFile, toFile; - register char *buffer; - register long size; + BPTR fromFile, toFile; + char *buffer; + long size; char *error = NULL; buffer = (char *) alloc(COPYSIZE); @@ -282,7 +281,8 @@ const char *from, *to; #ifdef MFLOPPY /* this should be replaced */ -saveDiskPrompt(start) +int +saveDiskPrompt(int start) { char buf[BUFSIZ], *bp; BPTR fileLock; @@ -338,7 +338,7 @@ saveDiskPrompt(start) /* Return 1 if the record file was found */ static boolean -record_exists() +record_exists(void) { FILE *file; @@ -355,7 +355,7 @@ record_exists() * For Amiga: do nothing, but called from restore.c */ void -gameDiskPrompt() +gameDiskPrompt(void) { } #endif @@ -365,8 +365,7 @@ gameDiskPrompt() * be room for the /. */ void -append_slash(name) -char *name; +append_slash(char *name) { char *ptr; @@ -381,8 +380,7 @@ char *name; } void -getreturn(str) -const char *str; +getreturn(const char *str) { int ch; @@ -396,12 +394,11 @@ const char *str; #define PATHSEP ';' FILE * -fopenp(name, mode) -register const char *name, *mode; +fopenp(const char *name, const char *mode) { - register char *bp, *pp, lastch; - register FILE *fp; - register BPTR theLock; + char *bp, *pp, lastch = 0; + FILE *fp; + BPTR theLock; char buf[BUFSIZ]; /* Try the default directory first. Then look along PATH. @@ -452,7 +449,8 @@ register const char *name, *mode; static BPTR OrgDirLock = NO_LOCK; -chdir(dir) char *dir; +int +chdir(char *dir) { extern char orgdir[]; @@ -489,7 +487,7 @@ chdir(dir) char *dir; */ #undef exit void -nethack_exit(code) +nethack_exit(int code) { #ifdef CHDIR extern char orgdir[]; @@ -506,10 +504,10 @@ nethack_exit(code) exit(code); } -void regularize(s) /* normalize file name - we don't like :'s or /'s */ -register char *s; +void +regularize(char *s) /* normalize file name - we don't like :'s or /'s */ { - register char *lp; + char *lp; while ((lp = strchr(s, ':')) || (lp = strchr(s, '/'))) *lp = '_'; diff --git a/sys/amiga/amirip.c b/sys/amiga/amirip.c index b14689f80..5f4aa52af 100644 --- a/sys/amiga/amirip.c +++ b/sys/amiga/amirip.c @@ -43,15 +43,15 @@ static struct RastPort *rp; #include #endif -static char *load_list[] = { "tomb.iff", 0 }; static BitMapHeader tomb_bmhd; -static struct BitMap *tbmp[1] = { 0 }; +static struct BitMap *tombimg = NULL; -static int cols[2] = { 154, 319 }; /* X location of center of columns */ +static const int cols_base[2] = { 154, 319 }; /* X location of center of columns */ +static int cols[2]; /* cols_base[] + xoff, computed per call */ static int cno = 0; /* current column */ #define TEXT_TOP (65 + yoff) -static xoff, yoff; /* image centering */ +static int xoff, yoff; /* image centering */ /* terrible kludge */ /* this is why prototypes should have ONLY types in them! */ @@ -100,19 +100,16 @@ int wh; /* was local in outrip, but needed for SCALE macro */ int cmap_white, cmap_black; void -amii_outrip(tmpwin, how, when) -winid tmpwin; -int how; -time_t when; +amii_outrip(winid tmpwin, int how, time_t when) { int just_return = 0; int done, rtxth; + struct IntuiMessage *imsg; int i; - register char *dpx; + char *dpx; char buf[200]; int line, tw, ww; - char *errstr = NULL; long year; if (!WINVERS_AMIV || HackScreen->RastPort.BitMap->Depth < 4) @@ -141,9 +138,7 @@ time_t when; SetFont(rp, HackFont); #endif - tomb_bmhd = ReadImageFiles(load_list, tbmp, &errstr); - if (errstr) - goto cleanup; + tomb_bmhd = ReadImageFile("tomb.iff", &tombimg); if (tomb_bmhd.w > ww || tomb_bmhd.h > wh) goto cleanup; @@ -151,12 +146,12 @@ time_t when; xoff = GENOFF(ww, tomb_bmhd.w); yoff = GENOFF(wh, tomb_bmhd.h); for (i = 0; i < SIZE(cols); i++) - cols[i] += xoff; + cols[i] = cols_base[i] + xoff; cmap_white = search_cmap(0, 0, 0); cmap_black = search_cmap(15, 15, 15); - BltBitMap(*tbmp, 0, 0, rp->BitMap, xoff, yoff, tomb_bmhd.w, tomb_bmhd.h, + BltBitMap(tombimg, 0, 0, rp->BitMap, xoff, yoff, tomb_bmhd.w, tomb_bmhd.h, 0xc0, 0xff, NULL); /* Put together death description */ @@ -199,7 +194,7 @@ time_t when; /* Put death type on stone */ for (line = DEATH_LINE, dpx = buf; line < YEAR_LINE; line++) { - register int i, i0; + int i, i0; char tmpchar; if ((i0 = strlen(dpx)) > STONE_LINE_LEN) { @@ -280,8 +275,7 @@ cleanup: } LoadRGB4(&HackScreen->ViewPort, sysflags.amii_curmap, amii_numcolors); - if (tbmp[0]) - FreeImageFiles(load_list, tbmp); + FreeImageFile(&tombimg); if (just_return) return; /* fall back to the straight-ASCII version */ @@ -289,8 +283,7 @@ cleanup: } static void -tomb_text(p) -char *p; +tomb_text(char *p) { char buf[STONE_LINE_LEN * 2]; int l; diff --git a/sys/amiga/amistack.c b/sys/amiga/amistack.c index 33769697f..e0ea10a7b 100644 --- a/sys/amiga/amistack.c +++ b/sys/amiga/amistack.c @@ -1,5 +1,5 @@ /* NetHack 3.6 amistack.c $NHDT-Date: 1432512795 2015/05/25 00:13:15 $ $NHDT-Branch: master $:$NHDT-Revision: 1.8 $ */ -/* Copyright (c) Janne Salmijärvi, Tampere, Finland, 2000 */ +/* Copyright (c) Janne Salmij�rvi, Tampere, Finland, 2000 */ /* NetHack may be freely redistributed. See license for details. */ /* @@ -12,10 +12,16 @@ #ifdef __SASC_60 #include +#endif /* - * At the moment 90*1024 would suffice, but just to be on the safe side ... + * Increase stack size to allow deep recursions. + * NetHack 3.7 with Lua needs significantly more stack than 3.6. */ -long __stack = 128 * 1024; +#ifdef __SASC_60 +long __stack = 256 * 1024; +#else +/* For GCC with -noixemul (libnix), __stack is also recognized */ +unsigned long __stack = 256 * 1024; #endif diff --git a/sys/amiga/amitty.c b/sys/amiga/amitty.c index e3ff882ef..22036c8f5 100644 --- a/sys/amiga/amitty.c +++ b/sys/amiga/amitty.c @@ -23,12 +23,12 @@ void tty_change_color(void); char *tty_get_color_string(void); -#ifdef TTY_GRAPHICS - int amibbs = 0; /* BBS mode */ char bbs_id[80] = ""; /* BBS uid equivalent */ long afh_in, afh_out; /* BBS mode Amiga filehandles */ +#ifdef TTY_GRAPHICS + void settty(const char *s) { @@ -58,9 +58,10 @@ setftty() } char kill_char = 'X' - '@'; char erase_char = '\b'; -tgetch() +int +tgetch(void) { - char x; + unsigned char x; Read(afh_in, &x, 1); return (x == '\r') ? '\n' : x; } diff --git a/sys/amiga/amiwind.c b/sys/amiga/amiwind.c index 6a64667eb..e06630675 100644 --- a/sys/amiga/amiwind.c +++ b/sys/amiga/amiwind.c @@ -23,18 +23,20 @@ static struct Message *GetFMsg(struct MsgPort *); #endif static int BufferGetchar(void); -static void ProcessMessage(register struct IntuiMessage *message); +static void ProcessMessage(struct IntuiMessage *message); #define BufferQueueChar(ch) (KbdBuffer[KbdBuffered++] = (ch)) -#ifndef CROSS_TO_AMIGA -struct Library *ConsoleDevice; -#else -struct Device * -# ifdef __CONSTLIBBASEDECL__ - __CONSTLIBBASEDECL__ -# endif /* __CONSTLIBBASEDECL__ */ - ConsoleDevice; -#endif + +struct Device *ConsoleDevice = NULL; + +/* Library bases - opened by amii_init_nhwindows, closed by amii_cleanup. + DOSBase is provided by newlib's startup code. + The rest must be defined here. */ +struct IntuitionBase *IntuitionBase = NULL; +struct GfxBase *GfxBase = NULL; +struct Library *GadToolsBase = NULL; +struct Library *LayersBase = NULL; +struct Library *AslBase = NULL; #ifndef CROSS_TO_AMIGA #include "NH:sys/amiga/amimenu.c" @@ -44,7 +46,6 @@ struct Device * /* Now our own variables */ -struct IntuitionBase *IntuitionBase; struct Screen *HackScreen; struct Window *pr_WindowPtr; struct MsgPort *HackPort; @@ -55,7 +56,6 @@ char Initialized = 0; WEVENT lastevent; #ifdef HACKFONT -struct GfxBase *GfxBase; struct Library *DiskfontBase; #endif @@ -100,11 +100,10 @@ static enum { NoAction, CloseOver } delayed_key_action = NoAction; */ struct Window * -OpenShWindow(nw) -struct NewWindow *nw; +OpenShWindow(struct NewWindow *nw) { - register struct Window *win; - register ULONG idcmpflags; + struct Window *win; + ULONG idcmpflags; if (!HackPort) /* Sanity check */ return (struct Window *) 0; @@ -126,12 +125,10 @@ struct NewWindow *nw; * Close a window that shared the HackPort IDCMP port. */ -void CloseShWindow(struct Window *); void -CloseShWindow(win) -struct Window *win; +CloseShWindow(struct Window *win) { - register struct IntuiMessage *msg; + struct IntuiMessage *msg; if (!HackPort) panic("HackPort NULL in CloseShWindow"); @@ -152,9 +149,9 @@ struct Window *win; } static int -BufferGetchar() +BufferGetchar(void) { - register int c; + int c; if (KbdBuffered > 0) { c = KbdBuffer[0]; @@ -180,8 +177,7 @@ BufferGetchar() */ int -ConvertKey(message) -register struct IntuiMessage *message; +ConvertKey(struct IntuiMessage *message) { static struct InputEvent theEvent; static char numpad[] = "bjnh.lyku"; @@ -190,8 +186,8 @@ register struct IntuiMessage *message; unsigned char buffer[10]; struct Window *w = message->IDCMPWindow; - register int length; - register ULONG qualifier; + int length; + ULONG qualifier; char numeric_pad, shift, control, alt; if (amii_wins[WIN_MAP]) @@ -355,10 +351,7 @@ register struct IntuiMessage *message; return (-1); } } - printf("Unrecognized key: %d ", (int) buffer[0]); - for (i = 1; i < length; ++i) - printf("%d ", (int) buffer[i]); - printf("\n"); + /* unrecognized key — silently ignore */ } return (-1); } @@ -373,8 +366,7 @@ register struct IntuiMessage *message; */ static void -ProcessMessage(message) -register struct IntuiMessage *message; +ProcessMessage(struct IntuiMessage *message) { int c; int cnt; @@ -385,15 +377,7 @@ register struct IntuiMessage *message; switch (message->Class) { case ACTIVEWINDOW: - if (alwaysinvent && WIN_INVEN != WIN_ERR - && w == amii_wins[WIN_INVEN]->win) { - cnt = DoMenuScroll(WIN_INVEN, 0, PICK_NONE, &mip); - } else if (scrollmsg && WIN_MESSAGE != WIN_ERR - && w == amii_wins[WIN_MESSAGE]->win) { - cnt = DoMenuScroll(WIN_MESSAGE, 0, PICK_NONE, &mip); - } else { - skip_mouse = 1; - } + skip_mouse = 1; break; case MOUSEBUTTONS: { @@ -429,12 +413,8 @@ register struct IntuiMessage *message; } } break; - case REFRESHWINDOW: { - if (scrollmsg && amii_wins[WIN_MESSAGE] - && w == amii_wins[WIN_MESSAGE]->win) { - cnt = DoMenuScroll(WIN_MESSAGE, 0, PICK_NONE, &mip); - } - } break; + case REFRESHWINDOW: + break; case CLOSEWINDOW: if (WIN_INVEN != WIN_ERR && w == amii_wins[WIN_INVEN]->win) { @@ -458,11 +438,6 @@ register struct IntuiMessage *message; break; case GADGETDOWN: - if (WIN_MESSAGE != WIN_ERR && w == amii_wins[WIN_MESSAGE]->win) { - cnt = DoMenuScroll(WIN_MESSAGE, 0, PICK_NONE, &mip); - } else if (WIN_INVEN != WIN_ERR && w == amii_wins[WIN_INVEN]->win) { - cnt = DoMenuScroll(WIN_INVEN, 0, PICK_NONE, &mip); - } break; case NEWSIZE: @@ -481,7 +456,17 @@ register struct IntuiMessage *message; ReDisplayData(WIN_INVEN); } else if (WINVERS_AMIV && (WIN_OVER != WIN_ERR && w == amii_wins[WIN_OVER]->win)) { - BufferQueueChar('R' - 64); + { + int i, have_redraw = 0; + for (i = 0; i < KbdBuffered; i++) { + if (KbdBuffer[i] == 'R' - 64) { + have_redraw = 1; + break; + } + } + if (!have_redraw) + BufferQueueChar('R' - 64); + } } else if (WIN_MAP != WIN_ERR && w == amii_wins[WIN_MAP]->win) { #ifdef CLIPPING CO = (w->Width - w->BorderLeft - w->BorderRight) / mxsize; @@ -494,7 +479,17 @@ register struct IntuiMessage *message; clipping = FALSE; clipx = clipy = 0; } - BufferQueueChar('R' - 64); + { + int i, have_redraw = 0; + for (i = 0; i < KbdBuffered; i++) { + if (KbdBuffer[i] == 'R' - 64) { + have_redraw = 1; + break; + } + } + if (!have_redraw) + BufferQueueChar('R' - 64); + } #endif } break; @@ -506,8 +501,9 @@ register struct IntuiMessage *message; amii_destroy_nhwindow(WIN_OVER); WIN_OVER = WIN_ERR; delayed_key_action = NoAction; + break; case NoAction: - ; /* null */ + break; } } @@ -522,13 +518,13 @@ register struct IntuiMessage *message; #if defined(TTY_GRAPHICS) && !defined(AMII_GRAPHICS) int -kbhit() +kbhit(void) { return 0; } #else int -kbhit() +kbhit(void) { int c; #ifdef TTY_GRAPHICS @@ -547,9 +543,9 @@ kbhit() #ifdef AMII_GRAPHICS int -amikbhit() +amikbhit(void) { - register struct IntuiMessage *message; + struct IntuiMessage *message; while (KbdBuffered < KBDBUFFER / 2) { #ifdef AMIFLUSH message = (struct IntuiMessage *) GetFMsg(HackPort); @@ -572,7 +568,7 @@ amikbhit() */ int -WindowGetchar() +WindowGetchar(void) { while ((lastevent.type = WEUNK), amikbhit() <= 0) { WaitPort(HackPort); @@ -581,7 +577,7 @@ WindowGetchar() } WETYPE -WindowGetevent() +WindowGetevent(void) { lastevent.type = WEUNK; while (amikbhit() == 0) { @@ -601,9 +597,9 @@ WindowGetevent() */ void -amii_cleanup() +amii_cleanup(void) { - register struct IntuiMessage *msg; + struct IntuiMessage *msg; /* Close things up */ if (HackPort) { @@ -713,8 +709,7 @@ amii_cleanup() #ifndef SHAREDLIB void -Abort(rc) -long rc; +Abort(long rc) { int fault = 1; #ifdef CHDIR @@ -765,7 +760,7 @@ long rc; } void -CleanUp() +CleanUp(void) { amii_cleanup(); } @@ -776,8 +771,7 @@ CleanUp() #ifdef AMIFLUSH /* This routine adapted from AmigaMail IV-37 by Michael Sinz */ static struct Message * -GetFMsg(port) -struct MsgPort *port; +GetFMsg(struct MsgPort *port) { struct IntuiMessage *msg, *succ, *succ1; @@ -803,8 +797,7 @@ struct MsgPort *port; #endif struct NewWindow * -DupNewWindow(win) -struct NewWindow *win; +DupNewWindow(struct NewWindow *win) { struct NewWindow *nwin; struct Gadget *ngd, *gd, *pgd = NULL; @@ -845,11 +838,10 @@ struct NewWindow *win; } void -FreeNewWindow(win) -struct NewWindow *win; +FreeNewWindow(struct NewWindow *win) { - register struct Gadget *gd, *pgd; - register struct StringInfo *sip; + struct Gadget *gd, *pgd; + struct StringInfo *sip; for (gd = win->FirstGadget; gd; gd = pgd) { pgd = gd->NextGadget; @@ -868,7 +860,7 @@ struct NewWindow *win; } void -bell() +bell(void) { if (flags.silent) return; @@ -876,15 +868,14 @@ bell() } void -amii_delay_output() +amii_delay_output(void) { /* delay 50 ms */ Delay(2L); } void -amii_number_pad(state) -int state; +amii_number_pad(int state) { } #endif /* AMII_GRAPHICS */ diff --git a/sys/amiga/nethack.cnf b/sys/amiga/nethack.cnf new file mode 100644 index 000000000..dad37d4d5 --- /dev/null +++ b/sys/amiga/nethack.cnf @@ -0,0 +1,44 @@ +# NetHack 3.7 Amiga Configuration +# +# For a full list of options, see the opthelp file or press '?' +# then 'o' during gameplay. + +# *** DISPLAY MODE *** +# +# Tile mode (graphical tiles, recommended): +OPTIONS=windowtype:amiv +# +# Text mode (uses hack.font line-drawing characters): +#OPTIONS=windowtype:amii +#OPTIONS=symset:AmigaFont + +# *** GENERAL OPTIONS *** +OPTIONS=time,showexp,lit_corridor +OPTIONS=boulder:0 +OPTIONS=autopickup,pickup_types:$"=/!?+ +OPTIONS=catname:Kaori + +# *** MENU COLORS *** +# Colour-code inventory items by BUC status and value. +OPTIONS=menucolors +MENUCOLOR=" blessed "=green +MENUCOLOR=" holy "=green +MENUCOLOR=" uncursed "=cyan +MENUCOLOR=" cursed "=red +MENUCOLOR=" unholy "=red +MENUCOLOR=" cursed .* (being worn)"=red&underline +MENUCOLOR=" cursed .* (wielded)"=red&underline +MENUCOLOR="loadstone"=red&underline +MENUCOLOR="gold piece"=brown +MENUCOLOR="worthless"=brown +MENUCOLOR="wand of wishing"=magenta +MENUCOLOR="magic lamp"=magenta +MENUCOLOR="magic marker"=magenta +MENUCOLOR="bag of holding"=magenta +MENUCOLOR="amulet of life saving"=magenta +MENUCOLOR="cloak of magic resistance"=magenta +MENUCOLOR="silver dragon scale"=cyan +MENUCOLOR="gray dragon scale"=cyan +MENUCOLOR="speed boots"=magenta +MENUCOLOR="luckstone"=green +MENUCOLOR="unicorn horn"=green diff --git a/sys/amiga/winamenu.c b/sys/amiga/winamenu.c index 013e3c792..d391bdec4 100644 --- a/sys/amiga/winamenu.c +++ b/sys/amiga/winamenu.c @@ -15,14 +15,12 @@ /* Start building the text for a menu */ void -amii_start_menu(window, mbehavior) -register winid window; -unsigned long mbehavior UNUSED; - +amii_start_menu(winid window, unsigned long mbehavior UNUSED) { - register int i; - register struct amii_WinDesc *cw; - register amii_menu_item *mip; + int i; + struct amii_WinDesc *cw; + amii_menu_item *mip; + if (window == WIN_ERR || (cw = amii_wins[window]) == NULL || cw->type != NHW_MENU) @@ -64,17 +62,12 @@ unsigned long mbehavior UNUSED; /* Add a string to a menu */ void -amii_add_menu(window, glyph, id, ch, gch, attr, str, itemflags) -register winid window; -register int glyph; -register const anything *id; -register char ch; -register char gch; -register int attr; -register const char *str; -register unsigned int itemflags; +amii_add_menu(winid window, const glyph_info *glyphinfo, const anything *id, + char ch, char gch, int attr, int clr, + const char *str, unsigned int itemflags) { - register struct amii_WinDesc *cw; + struct amii_WinDesc *cw; + boolean preselected = ((itemflags & MENU_ITEMFLAGS_SELECTED) != 0); amii_menu_item *mip; char buf[4 + BUFSZ]; @@ -90,7 +83,8 @@ register unsigned int itemflags; mip->identifier = *id; mip->selected = preselected; mip->attr = attr; - mip->glyph = Is_rogue_level(&u.uz) ? NO_GLYPH : glyph; + mip->color = clr; + mip->glyph = Is_rogue_level(&u.uz) ? NO_GLYPH : (glyphinfo ? glyphinfo->glyph : NO_GLYPH); mip->selector = 0; mip->gselector = gch; mip->count = -1; @@ -131,11 +125,10 @@ register unsigned int itemflags; /* Done building a menu. */ void -amii_end_menu(window, morestr) -register winid window; -register const char *morestr; +amii_end_menu(winid window, const char *morestr) { - register struct amii_WinDesc *cw; + struct amii_WinDesc *cw; + if (window == WIN_ERR || (cw = amii_wins[window]) == NULL || cw->type != NHW_MENU) @@ -151,8 +144,8 @@ register const char *morestr; mip = cw->menu.last; #endif any.a_void = 0; - amii_add_menu(window, NO_GLYPH, &any, 0, 0, ATR_NONE, morestr, - MENU_ITEMFLAGS_NONE); + amii_add_menu(window, (const glyph_info *) 0, &any, 0, 0, ATR_NONE, + NO_COLOR, morestr, MENU_ITEMFLAGS_NONE); #ifdef PROMPTFIRST /* Do some shuffling. Last first, push others one forward \ */ mip->next = NULL; @@ -178,13 +171,11 @@ register const char *morestr; /* Select something from the menu. */ int -amii_select_menu(window, how, mip) -register winid window; -register int how; -register menu_item **mip; +amii_select_menu(winid window, int how, menu_item **mip) { int cnt; - register struct amii_WinDesc *cw; + struct amii_WinDesc *cw; + if (window == WIN_ERR || (cw = amii_wins[window]) == NULL || cw->type != NHW_MENU) @@ -199,7 +190,7 @@ register menu_item **mip; } amii_menu_item * -find_menu_item(register struct amii_WinDesc *cw, int idx) +find_menu_item(struct amii_WinDesc *cw, int idx) { amii_menu_item *mip; for (mip = cw->menu.items; idx > 0 && mip; mip = mip->next) @@ -209,11 +200,11 @@ find_menu_item(register struct amii_WinDesc *cw, int idx) } int -make_menu_items(register struct amii_WinDesc *cw, register menu_item **rmip) +make_menu_items(struct amii_WinDesc *cw, menu_item **rmip) { - register int idx = 0; - register amii_menu_item *mip; - register menu_item *mmip; + int idx = 0; + amii_menu_item *mip; + menu_item *mmip; for (mip = cw->menu.items; mip; mip = mip->next) { if (mip->selected) @@ -236,19 +227,17 @@ make_menu_items(register struct amii_WinDesc *cw, register menu_item **rmip) } int -DoMenuScroll(win, blocking, how, retmip) -int win, blocking, how; -menu_item **retmip; +DoMenuScroll(int win, int blocking, int how, menu_item **retmip) { amii_menu_item *amip; - register struct Window *w; - register struct NewWindow *nw; + struct Window *w; + struct NewWindow *nw; struct PropInfo *pip; - register struct amii_WinDesc *cw; + struct amii_WinDesc *cw; struct IntuiMessage *imsg; struct Gadget *gd; - register int wheight, xsize, ysize, aredone = 0; - register int txwd, txh; + int wheight, xsize, ysize, aredone = 0; + int txwd, txh; long mics, secs, class, code; long oldmics = 0, oldsecs = 0; int aidx, oidx, topidx, hidden; @@ -819,7 +808,7 @@ menu_item **retmip; ++topidx; else break; - } else if (code = CTRL('U') + } else if (code == CTRL('U') || code == MENU_PREVIOUS_PAGE) { if (topidx > 0) --topidx; @@ -1007,11 +996,13 @@ menu_item **retmip; oldmics = mics; } else { amip = find_menu_item(cw, oidx); - amip->selected = 0; - amip->count = -1; - reset_counting = TRUE; - if (amip->canselect && amip->selector) - amip->str[SOFF + 2] = '-'; + if (amip) { + amip->selected = 0; + amip->count = -1; + reset_counting = TRUE; + if (amip->canselect && amip->selector) + amip->str[SOFF + 2] = '-'; + } } if (counting && amip->selected && amip->canselect && amip->selector) { @@ -1092,13 +1083,12 @@ menu_item **retmip; } void -ReDisplayData(win) -winid win; +ReDisplayData(winid win) { int totalvis; - register struct amii_WinDesc *cw; - register struct Window *w; - register struct Gadget *gd; + struct amii_WinDesc *cw; + struct Window *w; + struct Gadget *gd; unsigned long hidden, aidx, wheight; struct PropInfo *pip; @@ -1123,15 +1113,13 @@ winid win; } long -FindLine(win, line) -winid win; -int line; +FindLine(winid win, int line) { int txwd; - register char *t; - register struct amii_WinDesc *cw; - register struct Window *w; - register int i, disprow, len; + char *t; + struct amii_WinDesc *cw; + struct Window *w; + int i, disprow, len; int col = -1; if (win == WIN_ERR || !(cw = amii_wins[win]) || !(w = cw->win)) { @@ -1186,15 +1174,14 @@ int line; } long -CountLines(win) -winid win; +CountLines(winid win) { int txwd; amii_menu_item *mip; - register char *t; - register struct amii_WinDesc *cw; - register struct Window *w; - register int i, disprow, len; + char *t; + struct amii_WinDesc *cw; + struct Window *w; + int i, disprow, len; int col = -1; if (win == WIN_ERR || !(cw = amii_wins[win]) || !(w = cw->win)) { @@ -1250,17 +1237,15 @@ winid win; } void -DisplayData(win, start) -winid win; -int start; +DisplayData(winid win, int start) { int txwd; amii_menu_item *mip; - register char *t; - register struct amii_WinDesc *cw; - register struct Window *w; - register struct RastPort *rp; - register int i, disprow, len, wheight; + char *t; + struct amii_WinDesc *cw; + struct Window *w; + struct RastPort *rp; + int i, disprow, len, wheight; int whichcolor = -1; int col; @@ -1363,6 +1348,13 @@ int start; whichcolor = 2; } + /* Apply menucolor if set for this item */ + if (mip && mip->color != NO_COLOR && !(mip->selected)) { + extern const int foreg[]; + SetAPen(rp, foreg[mip->color]); + whichcolor = 0; /* force re-evaluation next item */ + } + /* Next line out, wrap if too long */ t = cw->data[i] + SOFF; @@ -1427,14 +1419,11 @@ int start; } void -SetPropInfo(win, gad, vis, total, top) -register struct Window *win; -register struct Gadget *gad; -register long vis, total, top; +SetPropInfo(struct Window *win, struct Gadget *gad, long vis, long total, long top) { long mflags; - register long hidden; - register int body, pot; + long hidden; + int body, pot; hidden = max(total - vis, 0); diff --git a/sys/amiga/winami.c b/sys/amiga/winami.c index 4b7866346..7770a3547 100644 --- a/sys/amiga/winami.c +++ b/sys/amiga/winami.c @@ -39,7 +39,8 @@ long amii_scrnmode; * the intuition interface for the amiga... */ struct window_procs amii_procs = { - "amii", WC_COLOR | WC_HILITE_PET | WC_INVERSE, + WPID(amii), + WC_COLOR | WC_HILITE_PET | WC_INVERSE, 0L, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* color availability */ amii_init_nhwindows, @@ -48,7 +49,7 @@ struct window_procs amii_procs = { amii_create_nhwindow, amii_clear_nhwindow, amii_display_nhwindow, amii_destroy_nhwindow, amii_curs, amii_putstr, genl_putmixed, amii_display_file, amii_start_menu, amii_add_menu, amii_end_menu, - amii_select_menu, genl_message_menu, amii_update_inventory, + amii_select_menu, genl_message_menu, amii_mark_synch, amii_wait_synch, #ifdef CLIPPING amii_cliparound, @@ -67,13 +68,16 @@ struct window_procs amii_procs = { genl_status_init, genl_status_finish, genl_status_enablefield, genl_status_update, genl_can_suspend_yes, + amii_update_inventory, + amii_ctrl_nhwindow, }; /* The view window layout uses the same function names so we can use * a shared library to allow the executable to be smaller. */ struct window_procs amiv_procs = { - "amitile", WC_COLOR | WC_HILITE_PET | WC_INVERSE, + WPID(amiv), + WC_COLOR | WC_HILITE_PET | WC_INVERSE, 0L, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* color availability */ amii_init_nhwindows, @@ -82,7 +86,7 @@ struct window_procs amiv_procs = { amii_create_nhwindow, amii_clear_nhwindow, amii_display_nhwindow, amii_destroy_nhwindow, amii_curs, amii_putstr, genl_putmixed, amii_display_file, amii_start_menu, amii_add_menu, amii_end_menu, - amii_select_menu, genl_message_menu, amii_update_inventory, + amii_select_menu, genl_message_menu, amii_mark_synch, amii_wait_synch, #ifdef CLIPPING amii_cliparound, @@ -101,6 +105,8 @@ struct window_procs amiv_procs = { genl_status_init, genl_status_finish, genl_status_enablefield, genl_status_update, genl_can_suspend_yes, + amii_update_inventory, + amii_ctrl_nhwindow, }; unsigned short amii_initmap[AMII_MAXCOLORS]; @@ -428,7 +434,7 @@ struct NewScreen NewHackScreen = { 0, 0, WIDTH, SCREENHEIGHT, 3, 0, * init_sound_disp_gamewindows(). */ void -amii_askname() +amii_askname(void) { char plnametmp[300]; /* From winreq.c: sizeof(StrStringSIBuff) */ *plnametmp = 0; @@ -454,12 +460,12 @@ amii_askname() #if 0 /* New function at the bottom */ void -amii_player_selection() +amii_player_selection(void) { - register struct Window *cwin; - register struct IntuiMessage *imsg; - register int aredone = 0; - register struct Gadget *gd; + struct Window *cwin; + struct IntuiMessage *imsg; + int aredone = 0; + struct Gadget *gd; static int once = 0; long class, code; @@ -499,7 +505,7 @@ amii_player_selection() #ifdef INTUI_NEW_LOOK Type_NewWindowStructure1.Extension = wintags; Type_NewWindowStructure1.Flags |= WFLG_NW_EXTENDED; - fillhook.h_Entry = (ULONG(*)())LayerFillHook; + fillhook.h_Entry = (void *) &LayerFillHook; fillhook.h_Data = (void *)-2; fillhook.h_SubEntry = 0; #endif @@ -595,8 +601,7 @@ amii_player_selection() #include "NH:sys/amiga/randwin.c" void -RandomWindow( name ) - char *name; +RandomWindow(char *name) { struct MsgPort *tport; struct timerequest *trq; @@ -672,7 +677,7 @@ allocerr: #ifdef INTUI_NEW_LOOK Rnd_NewWindowStructure1.Extension = wintags; Rnd_NewWindowStructure1.Flags |= WFLG_NW_EXTENDED; - fillhook.h_Entry = (ULONG(*)())LayerFillHook; + fillhook.h_Entry = (void *) &LayerFillHook; fillhook.h_Data = (void *)-2; fillhook.h_SubEntry = 0; #endif @@ -762,10 +767,11 @@ amii_get_ext_cmd(void) #endif int colx; int bottom = 0; + struct Window *w; char obufp[100]; - register char *bufp = obufp; - register int c; + char *bufp = obufp; + int c; int com_index, oindex; int did_comp = 0; /* did successful completion? */ int sel = -1; @@ -787,7 +793,8 @@ amii_get_ext_cmd(void) id.a_char = *extcmdlist[i].ef_txt; sprintf(buf, "%-10s - %s ", extcmdlist[i].ef_txt, extcmdlist[i].ef_desc); - amii_add_menu(win, NO_GLYPH, &id, extcmdlist[i].ef_txt[0], 0, 0, + amii_add_menu(win, (const glyph_info *) 0, &id, + extcmdlist[i].ef_txt[0], 0, 0, NO_COLOR, buf, MENU_ITEMFLAGS_NONE); } @@ -848,8 +855,9 @@ amii_get_ext_cmd(void) id.a_char = extcmdlist[i].ef_txt[0]; sprintf(buf, "%-10s - %s ", extcmdlist[i].ef_txt, extcmdlist[i].ef_desc); - amii_add_menu(win, NO_GLYPH, &id, extcmdlist[i].ef_txt[0], 0, - 0, buf, MENU_ITEMFLAGS_NONE); + amii_add_menu(win, (const glyph_info *) 0, &id, + extcmdlist[i].ef_txt[0], 0, 0, NO_COLOR, + buf, MENU_ITEMFLAGS_NONE); } amii_end_menu(win, (char *) 0); @@ -941,10 +949,7 @@ amii_get_ext_cmd(void) } static int -put_ext_cmd(obufp, colx, cw, bottom) -char * obufp; -int colx, bottom; -struct amii_WinDesc *cw; +put_ext_cmd(char *obufp, int colx, struct amii_WinDesc *cw, int bottom) { struct Window *w = cw->win; char *t; @@ -989,10 +994,9 @@ struct amii_WinDesc *cw; /* Ask a question and get a response */ char -amii_yn_function(query, resp, def) -const char * query, *resp; -char def; +amii_yn_function(const char *query, const char *resp, char def) { + /* * Generic yes/no function. 'def' is the default (returned by space or * return; 'esc' returns 'q', or 'n', or the default, depending on @@ -1004,11 +1008,11 @@ char def; * are allowed); if it includes an , anything beyond that won't * be shown in the prompt to the user but will be acceptable as input. */ - register char q; + char q; char rtmp[40]; boolean digit_ok, allow_num; char prompt[BUFSZ]; - register struct amii_WinDesc *cw; + struct amii_WinDesc *cw; if (cw = amii_wins[WIN_MESSAGE]) cw->disprows = 0; @@ -1138,16 +1142,15 @@ char def; } void -amii_display_file(fn, complain) -const char * fn; -boolean complain; +amii_display_file(const char *fn, boolean complain) { - register struct amii_WinDesc *cw; - register int win; - register dlb *fp; - register char *t; + struct amii_WinDesc *cw; + int win; + dlb *fp; + char *t; char buf[200]; + if (fn == NULL) panic("NULL file name in display_file()"); @@ -1194,12 +1197,11 @@ boolean complain; * are rendered in the up position by default. */ void -SetBorder(gd) -register struct Gadget * gd; +SetBorder(struct Gadget *gd) { - register struct Border *bp; - register short *sp; - register int i, inc = -1, dec = -1; + struct Border *bp; + short *sp; + int i, inc = -1, dec = -1; int borders = 6; int hipen = sysflags.amii_dripens[SHINEPEN], shadowpen = sysflags.amii_dripens[SHADOWPEN]; @@ -1337,7 +1339,7 @@ register struct Gadget * gd; /* Following function copied from wintty.c; Modified slightly to fit amiga needs */ void -amii_player_selection() +amii_player_selection(void) { int i, k, n; char pick4u = 'n', thisch, lastch = 0; @@ -1346,6 +1348,7 @@ amii_player_selection() anything any; menu_item *selected = 0; + rigid_role_checks(); /* Should we randomly pick for the player? */ @@ -1424,8 +1427,8 @@ amii_player_selection() } else Strcpy(rolenamebuf, roles[i].name.m); } - add_menu(win, NO_GLYPH, &any, thisch, 0, ATR_NONE, - an(rolenamebuf), MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, thisch, 0, ATR_NONE, + NO_COLOR, an(rolenamebuf), MENU_ITEMFLAGS_NONE); lastch = thisch; } } @@ -1433,11 +1436,11 @@ amii_player_selection() flags.initalign, PICK_RANDOM) + 1; if (any.a_int == 0) /* must be non-zero */ any.a_int = randrole(FALSE) + 1; - add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random", - MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, '*', 0, ATR_NONE, + NO_COLOR, "Random", MENU_ITEMFLAGS_NONE); any.a_int = i + 1; /* must be non-zero */ - add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit", - MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, 'q', 0, ATR_NONE, + NO_COLOR, "Quit", MENU_ITEMFLAGS_NONE); Sprintf(pbuf, "Pick a role for your %s", plbuf); end_menu(win, pbuf); n = select_menu(win, PICK_ONE, &selected); @@ -1498,19 +1501,19 @@ amii_player_selection() if (ok_race(flags.initrole, i, flags.initgend, flags.initalign)) { any.a_int = i + 1; /* must be non-zero */ - add_menu(win, NO_GLYPH, &any, races[i].noun[0], 0, - ATR_NONE, races[i].noun, + add_menu(win, &nul_glyphinfo, &any, races[i].noun[0], 0, + ATR_NONE, NO_COLOR, races[i].noun, MENU_ITEMFLAGS_NONE); } any.a_int = pick_race(flags.initrole, flags.initgend, flags.initalign, PICK_RANDOM) + 1; if (any.a_int == 0) /* must be non-zero */ any.a_int = randrace(flags.initrole) + 1; - add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random", - MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, '*', 0, ATR_NONE, + NO_COLOR, "Random", MENU_ITEMFLAGS_NONE); any.a_int = i + 1; /* must be non-zero */ - add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit", - MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, 'q', 0, ATR_NONE, + NO_COLOR, "Quit", MENU_ITEMFLAGS_NONE); Sprintf(pbuf, "Pick the race of your %s", plbuf); end_menu(win, pbuf); n = select_menu(win, PICK_ONE, &selected); @@ -1571,18 +1574,19 @@ amii_player_selection() if (ok_gend(flags.initrole, flags.initrace, i, flags.initalign)) { any.a_int = i + 1; - add_menu(win, NO_GLYPH, &any, genders[i].adj[0], 0, - ATR_NONE, genders[i].adj, MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, genders[i].adj[0], 0, + ATR_NONE, NO_COLOR, genders[i].adj, + MENU_ITEMFLAGS_NONE); } any.a_int = pick_gend(flags.initrole, flags.initrace, flags.initalign, PICK_RANDOM) + 1; if (any.a_int == 0) /* must be non-zero */ any.a_int = randgend(flags.initrole, flags.initrace) + 1; - add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random", - MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, '*', 0, ATR_NONE, + NO_COLOR, "Random", MENU_ITEMFLAGS_NONE); any.a_int = i + 1; /* must be non-zero */ - add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit", - MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, 'q', 0, ATR_NONE, + NO_COLOR, "Quit", MENU_ITEMFLAGS_NONE); Sprintf(pbuf, "Pick the gender of your %s", plbuf); end_menu(win, pbuf); n = select_menu(win, PICK_ONE, &selected); @@ -1642,18 +1646,19 @@ amii_player_selection() if (ok_align(flags.initrole, flags.initrace, flags.initgend, i)) { any.a_int = i + 1; - add_menu(win, NO_GLYPH, &any, aligns[i].adj[0], 0, - ATR_NONE, aligns[i].adj, MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, aligns[i].adj[0], 0, + ATR_NONE, NO_COLOR, aligns[i].adj, + MENU_ITEMFLAGS_NONE); } any.a_int = pick_align(flags.initrole, flags.initrace, flags.initgend, PICK_RANDOM) + 1; if (any.a_int == 0) /* must be non-zero */ any.a_int = randalign(flags.initrole, flags.initrace) + 1; - add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random", - MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, '*', 0, ATR_NONE, + NO_COLOR, "Random", MENU_ITEMFLAGS_NONE); any.a_int = i + 1; /* must be non-zero */ - add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit", - MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, 'q', 0, ATR_NONE, + NO_COLOR, "Quit", MENU_ITEMFLAGS_NONE); Sprintf(pbuf, "Pick the alignment of your %s", plbuf); end_menu(win, pbuf); n = select_menu(win, PICK_ONE, &selected); diff --git a/sys/amiga/winami.p b/sys/amiga/winami.p index 2e26cfb10..dfb588177 100644 --- a/sys/amiga/winami.p +++ b/sys/amiga/winami.p @@ -5,10 +5,10 @@ void amii_raw_print(const char *); void amii_raw_print_bold(const char *); void amii_start_menu(winid , unsigned long ); -void amii_add_menu(winid , char , int , const char *, unsigned int); +void amii_add_menu(winid, const glyph_info *, const anything *, char, char, int, int, const char *, unsigned int); void amii_end_menu(winid , char , const char * , const char *); char amii_select_menu(winid ); -void amii_update_inventory (void); +void amii_update_inventory(int); void amii_mark_synch (void); void amii_wait_synch (void); void amii_setclipped (void); @@ -27,7 +27,7 @@ void amii_putstr(winid , int , const char *); void amii_putsym(winid , int , int , CHAR_P ); void amii_clear_nhwindow(winid ); void amii_exit_nhwindows(const char *); -int amii_nh_poskey(int * , int * , int *); +int amii_nh_poskey(coordxy *, coordxy *, int *); int amii_nhgetch (void); void amii_get_nh_event (void); void amii_remember_topl (void); @@ -35,7 +35,7 @@ int amii_doprev_message (void); void amii_display_nhwindow(winid , boolean ); void amii_display_file(const char * , boolean ); void amii_curs(winid , int , int ); -void amii_print_glyph(winid , coordxy , coordxy , int, int ); +void amii_print_glyph(winid, coordxy, coordxy, const glyph_info *, const glyph_info *); void DoMenuScroll(int , int ); void DisplayData(int , int , int ); void SetPropInfo(struct Window * , struct Gadget * , long , long , long ); diff --git a/sys/amiga/winchar.c b/sys/amiga/winchar.c index e4e56b8a2..75c35c87b 100644 --- a/sys/amiga/winchar.c +++ b/sys/amiga/winchar.c @@ -41,7 +41,8 @@ int main(int, char **); struct BitMap *MyAllocBitMap(int, int, int, long); void MyFreeBitMap(struct BitMap *); -void FreeImageFiles(char **, struct BitMap **); +BitMapHeader ReadImageFile(const char *, struct BitMap **); +void FreeImageFile(struct BitMap **); void amiv_flush_glyph_buffer(struct Window *); void amiv_lprint_glyph(winid, int, int); void amii_lprint_glyph(winid, int, int); @@ -91,17 +92,9 @@ extern int maxmontile, maxobjtile, maxothtile; /* from tile.c */ struct PDAT pictdata; -#define NUMTILEIMAGES 3 -char *tileimages[] = { -#define TBLMONTILE 0 - "NetHack:tiles/monsters.iff", -#define TBLOBJTILE 1 - "NetHack:tiles/objects.iff", -#define TBLOTHTILE 2 - "NetHack:tiles/other.iff", 0, -}; - -struct BitMap *ifftimg[NUMTILEIMAGES], *tile; +/* Single tile image file, set at runtime by amii_init_nhwindows */ +char *tilefile; +struct BitMap *tileimg, *tile; #ifdef TESTING short pens[NUMDRIPENS] = { 8, 3, 15, 0, 15, 7, 7, 8, 0 }; @@ -146,7 +139,7 @@ main(int argc, char **argv) x = x % IMGCOLUMNS; dx = i % (IMGCOLUMNS * 2); dy = i / (IMGCOLUMNS * 2); - BltBitMapRastPort(ifftimg[tbl], x * pictdata.xsize, + BltBitMapRastPort(tileimg, x * pictdata.xsize, y * pictdata.ysize, w->RPort, w->BorderLeft + 1 + dx * pictdata.xsize, w->BorderTop + 1 + dy * pictdata.ysize, @@ -176,175 +169,112 @@ main(int argc, char **argv) CloseScreen(scr); } - FreeImageFiles(tileimages, ifftimg); + FreeTileImageFiles(); return (0); } #endif +/* + * Read a single BMAP IFF file into a BitMap. + * Returns the BitMapHeader; *bmp receives the bitmap. + * Caller frees via FreeImageFile(). + */ BitMapHeader -ReadTileImageFiles() +ReadImageFile(const char *filename, struct BitMap **bmp) { - char *errstr = NULL; - BitMapHeader ret = ReadImageFiles(tileimages, ifftimg, &errstr); - if (errstr) { - panic(errstr); - } - return ret; -} - -BitMapHeader -ReadImageFiles(char **filenames, struct BitMap **iffimg, char **errstrp) -{ - BitMapHeader *bmhd = NULL, bmhds; - unsigned char *cmap; - extern int errno; - register int i, j; + BitMapHeader *bmhd, bmhds; + int j, np; struct IFFHandle *iff; struct StoredProperty *prop; IFFParseBase = OpenLibrary("iffparse.library", 0L); - if (!IFFParseBase) { - *errstrp = "No iffparse.library"; - return bmhds; + if (!IFFParseBase) + panic("No iffparse.library"); + + iff = AllocIFF(); + if (!iff) + panic("can't start IFF processing"); + + iff->iff_Stream = Open(filename, MODE_OLDFILE); + if (iff->iff_Stream == 0) + panic("Can't open %s", filename); + + InitIFFasDOS(iff); + OpenIFF(iff, IFFF_READ); + PropChunk(iff, ID_BMAP, ID_BMHD); + PropChunk(iff, ID_BMAP, ID_CMAP); + PropChunk(iff, ID_BMAP, ID_PDAT); + StopChunk(iff, ID_BMAP, ID_PLNE); + if ((j = ParseIFF(iff, IFFPARSE_SCAN)) != 0) + panic("ParseIFF failed on %s, code %d", + filename, j); + + prop = FindProp(iff, ID_BMAP, ID_BMHD); + if (!prop) + panic("No BMHD chunk in %s", filename); + bmhd = (BitMapHeader *) prop->sp_Data; + np = bmhd->nPlanes; + + /* Load CMAP into palette arrays if present */ + prop = FindProp(iff, ID_BMAP, ID_CMAP); + if (prop) { + unsigned char *cmap = prop->sp_Data; + for (j = 0; j < (1L << np) * 3; j += 3) { + amii_initmap[j / 3] = + amiv_init_map[j / 3] = + ((cmap[j+0] >> 4) << 8) + | ((cmap[j+1] >> 4) << 4) + | (cmap[j+2] >> 4); + } } - /* - for( i = 0; filenames[i]; ++i ) - memset( iffimg[i], 0, sizeof( struct BitMap ) ); - */ - for (i = 0; filenames[i]; ++i) { - iff = AllocIFF(); - if (!iff) { - FreeImageFiles(filenames, iffimg); - *errstrp = "can't start IFF processing"; - return bmhds; - } - iff->iff_Stream = Open(filenames[i], MODE_OLDFILE); - if (iff->iff_Stream == 0) { - char *buf = malloc(100 + strlen(filenames[i])); - FreeImageFiles(filenames, iffimg); - sprintf(buf, "Can't open %s: %s", filenames[i], strerror(errno)); - *errstrp = buf; - return bmhds; - } - InitIFFasDOS(iff); - OpenIFF(iff, IFFF_READ); - PropChunk(iff, ID_BMAP, ID_BMHD); - PropChunk(iff, ID_BMAP, ID_CMAP); - PropChunk(iff, ID_BMAP, ID_CAMG); - PropChunk(iff, ID_BMAP, ID_PDAT); - StopChunk(iff, ID_BMAP, ID_PLNE); - if ((j = ParseIFF(iff, IFFPARSE_SCAN)) != 0) { - char *buf = malloc(100); - FreeImageFiles(filenames, iffimg); - sprintf(buf, "ParseIFF failed for image %d, failure code: %d", i, - j); - *errstrp = buf; - return bmhds; - } + /* Load PDAT if present */ + prop = FindProp(iff, ID_BMAP, ID_PDAT); + if (prop) + pictdata = *(struct PDAT *) prop->sp_Data; - if (prop = FindProp(iff, ID_BMAP, ID_BMHD)) { - bmhd = (BitMapHeader *) prop->sp_Data; - } else { - FreeImageFiles(filenames, iffimg); - CloseIFF(iff); - Close(iff->iff_Stream); - FreeIFF(iff); - *errstrp = "No BMHD CHUNK in file"; - return bmhds; - } + *bmp = MyAllocBitMap(bmhd->w, bmhd->h, + np, MEMF_CHIP | MEMF_CLEAR); + if (!*bmp) + panic("Can't allocate bitmap for %s", filename); - if (prop = FindProp(iff, ID_BMAP, ID_CMAP)) { - cmap = prop->sp_Data; - for (j = 0; j < (1L << bmhd->nPlanes) * 3; j += 3) { -#if 0 - /* Some day we will want to use the larger palette - * resolution available under v39 and later. i.e. - * 32 instead of 12 bits of color. Ususally this - * just means shifting the color left by 16-20 bits - * depending on what intensity looks best. Experience - * says that the higher values are better intensities. - * - * For now though we won't do this. The color table - * structure is incompatible with earlier versions of - * intuition. We would have to do some funny things - * to make 3*AMII_MAXCOLORS longs work like 3*AMII_MAXCOLORS - * UWORD's at run time... A union would help, but... - */ - if( IntuitionBase->LibNode.lib_Version >= 39 ) - { - /* 8 bits of color, so shift to left end. */ - amiv_init_map[ j+0 ] = cmap[j+0]<<24; - amiv_init_map[ j+1 ] = cmap[j+1]<<24; - amiv_init_map[ j+2 ] = cmap[j+2]<<24; - } - else -#endif - { -/* We can only use 4 bits of the 8 that are stored in the - * cmap, so mask them and then shift them into position - * for the UWORD value to store. - */ -#ifndef TESTING - amii_initmap[j / 3] = amiv_init_map[j / 3] = - ((cmap[j + 0] >> 4) << 8) | ((cmap[j + 1] >> 4) << 4) - | (cmap[j + 2] >> 4); -#endif - } - } - } else { - FreeImageFiles(filenames, iffimg); - CloseIFF(iff); - Close(iff->iff_Stream); - FreeIFF(iff); - *errstrp = "No CMAP CHUNK in file"; - return bmhds; - } + for (j = 0; j < np; j++) + ReadChunkBytes(iff, (*bmp)->Planes[j], + RASSIZE(bmhd->w, bmhd->h)); - if (prop = FindProp(iff, ID_BMAP, ID_PDAT)) { - struct PDAT *pp; - - pp = (struct PDAT *) prop->sp_Data; - pictdata = *pp; - } else { - FreeImageFiles(filenames, iffimg); - CloseIFF(iff); - Close(iff->iff_Stream); - FreeIFF(iff); - *errstrp = "No PDAT CHUNK in file"; - return bmhds; - } - - iffimg[i] = MyAllocBitMap(bmhd->w, bmhd->h, - pictdata.nplanes + amii_extraplanes, - MEMF_CHIP | MEMF_CLEAR); - if (iffimg[i] == NULL) { - char *buf = malloc(80); - FreeImageFiles(filenames, iffimg); - sprintf(buf, "Can't allocate bitmap for image %d\n", i); - *errstrp = buf; - return bmhds; - } - for (j = 0; j < pictdata.nplanes + amii_extraplanes; ++j) { - ReadChunkBytes(iff, iffimg[i]->Planes[j], - RASSIZE(bmhd->w, bmhd->h)); - } - bmhds = *bmhd; - CloseIFF(iff); - Close(iff->iff_Stream); - FreeIFF(iff); - } + bmhds = *bmhd; + CloseIFF(iff); + Close(iff->iff_Stream); + FreeIFF(iff); CloseLibrary(IFFParseBase); - tile = MyAllocBitMap(pictdata.xsize, pictdata.ysize, - pictdata.nplanes + amii_extraplanes, - MEMF_CHIP | MEMF_CLEAR); - if (tile == NULL) { - FreeImageFiles(filenames, iffimg); - *errstrp = "Can't allocate tile bitmap for scaling"; + return bmhds; +} + +void +FreeImageFile(struct BitMap **bmp) +{ + if (*bmp) { + MyFreeBitMap(*bmp); + *bmp = NULL; } - return (bmhds); +} + +BitMapHeader +ReadTileImageFiles(void) +{ + BitMapHeader bmhds; + + bmhds = ReadImageFile(tilefile, &tileimg); + + tile = MyAllocBitMap(pictdata.xsize, pictdata.ysize, + pictdata.nplanes + amii_extraplanes, + MEMF_CHIP | MEMF_CLEAR); + if (!tile) + panic("Can't allocate tile temp bitmap"); + + return bmhds; } struct MyBitMap { @@ -401,8 +331,7 @@ MyFreeBitMap(struct BitMap *bmp) #ifdef TESTING void -panic(s, a1, a2, a3, a4) -char *s; +panic(char *s, long a1, long a2, long a3, long a4) { printf(s, a1, a2, a3, a4); putchar('\n'); @@ -420,24 +349,10 @@ alloc(unsigned int x) #endif void -FreeTileImageFiles() +FreeTileImageFiles(void) { - FreeImageFiles(tileimages, ifftimg); -} - -void -FreeImageFiles(char **filenames, struct BitMap **img) -{ - register int i; - - for (i = 0; filenames[i]; ++i) { - if (img[i]) - MyFreeBitMap(img[i]); - } - - /* REALLY ugly hack alert! */ - if (tile && img == ifftimg) - MyFreeBitMap(tile); + FreeImageFile(&tileimg); + FreeImageFile(&tile); } #ifndef TESTING @@ -456,8 +371,7 @@ struct amiv_glyph_node amiv_g_nodes[NUMBER_GLYPH_NODES]; static char amiv_glyph_buffer[GLYPH_BUFFER_SIZE]; void -flush_glyph_buffer(vw) -struct Window *vw; +flush_glyph_buffer(struct Window *vw) { if (WINVERS_AMIV) amiv_flush_glyph_buffer(vw); @@ -469,8 +383,7 @@ struct Window *vw; * Routine to flush whatever is buffered */ void -amiv_flush_glyph_buffer(vw) -struct Window *vw; +amiv_flush_glyph_buffer(struct Window *vw) { #if !defined(DISPMAP) || defined(OPT_DISPMAP) int xsize, ysize, x, y; @@ -481,7 +394,7 @@ struct Window *vw; struct BitMap *imgbm = 0, *bm = 0; int i, k; int scaling_needed; - register struct RastPort *rp = vw->RPort; + struct RastPort *rp = vw->RPort; #endif /* If nothing is buffered, return before we do anything */ @@ -577,7 +490,7 @@ struct Window *vw; /* Go ahead and start dumping the stuff */ for (i = 0; i < glyph_node_index; ++i) { /* Do it */ - register int offx, offy, j; + int offx, offy, j; struct BitMap *nodebm = amiv_g_nodes[i].bitmap; /* Get the unclipped coordinates */ @@ -596,10 +509,10 @@ struct Window *vw; * this code is generalized to handle any size tile * image... */ - memcpy(tile->Planes[j] + ((k * pictdata.ysize) / 8), + memcpy(tile->Planes[j] + k * tile->BytesPerRow, nodebm->Planes[j] + offx + offy + (nodebm->BytesPerRow * k), - pictdata.ysize / 8); + pictdata.xsize / 8); } } @@ -648,41 +561,30 @@ struct Window *vw; * Glyph buffering routine. Called instead of WindowPuts(). */ void -amiv_lprint_glyph(window, color_index, glyph) -winid window; -int color_index, glyph; +amiv_lprint_glyph(winid window, int color_index, int glyph) { int base; struct amii_WinDesc *cw; struct Window *w; int curx; int cury; - int tbl, icon; - register int xoff, yoff; + int icon; + int xoff, yoff; - /* Get the real icon index */ - if (glyph != NO_GLYPH) - icon = GlyphToIcon(glyph); + /* Skip NO_GLYPH — nothing to draw */ + if (glyph == NO_GLYPH) + return; + + icon = GlyphToIcon(glyph); if ((cw = amii_wins[window]) == (struct amii_WinDesc *) NULL) panic("bad winid in amiv_lprint_glyph: %d", window); w = cw->win; - if (glyph != NO_GLYPH && glyph < 10000) { - /* decide on which image has the needed picture */ - if (icon <= MAXMONTILE) { - tbl = TBLMONTILE; - base = 0; - } else if (icon <= MAXOBJTILE) { - tbl = TBLOBJTILE; - base = MAXMONTILE + 1; - } else if (icon <= MAXOTHTILE) { - tbl = TBLOTHTILE; - base = MAXOBJTILE + 1; - } else - panic("Bad icon #%d, glyph #%d, only %d icons known\n", icon, - glyph, MAXOTHTILE); + if (glyph < 10000) { + if (icon >= pictdata.npics) + icon = 0; /* fallback for out-of-range */ /* Get the relative offset in the page */ @@ -748,11 +650,11 @@ int color_index, glyph; amiv_g_nodes[glyph_node_index].odstx = cw->curx; amiv_g_nodes[glyph_node_index].srcx = xoff; amiv_g_nodes[glyph_node_index].srcy = yoff; - amiv_g_nodes[glyph_node_index].bitmap = ifftimg[tbl]; + amiv_g_nodes[glyph_node_index].bitmap = tileimg; ++glyph_node_index; } else { /* Do it */ - register int j, k, x, y, apen; + int j, k, x, y, apen; struct RastPort *rp = w->RPort; x = rp->cp_x - pictdata.xsize - 3; #ifdef OPT_DISPMAP @@ -770,17 +672,17 @@ int color_index, glyph; y = rp->cp_y - pictdata.ysize + 1; if (glyph != NO_GLYPH) { - struct BitMap *bm = ifftimg[tbl]; + struct BitMap *bm = tileimg; /* 8 bits per byte */ xoff /= 8; yoff *= bm->BytesPerRow; for (j = 0; j < pictdata.nplanes; ++j) { for (k = 0; k < pictdata.ysize; ++k) { - memcpy(tile->Planes[j] + ((k * pictdata.ysize) / 8), + memcpy(tile->Planes[j] + k * tile->BytesPerRow, bm->Planes[j] + xoff + yoff + (bm->BytesPerRow * k), - pictdata.ysize / 8); + pictdata.xsize / 8); } } @@ -824,8 +726,7 @@ static int usecolor; */ void -amiv_start_glyphout(window) -winid window; +amiv_start_glyphout(winid window) { struct amii_WinDesc *cw; struct Window *w; @@ -860,8 +761,7 @@ winid window; * General cleanup routine -- flushes and restores cursor */ void -amii_end_glyphout(window) -winid window; +amii_end_glyphout(winid window) { struct amii_WinDesc *cw; struct Window *w; @@ -891,7 +791,7 @@ winid window; Move(w->RPort, xsave, ysave); } -static maze_type = COL_MAZE_BRICK; +static int maze_type = COL_MAZE_BRICK; void SetMazeType(MazeType t) @@ -987,11 +887,10 @@ int backg[AMII_MAXCOLORS] = { * Routine to simply flush whatever is buffered */ void -amii_flush_glyph_buffer(w) -struct Window *w; +amii_flush_glyph_buffer(struct Window *w) { short i, x, y; - register struct RastPort *rp = w->RPort; + struct RastPort *rp = w->RPort; /* If nothing is buffered, return before we do anything */ if (glyph_node_index == 0) @@ -1013,6 +912,10 @@ struct Window *w; + rp->TxBaseline + 1; x = amii_g_nodes[i].x * rp->TxWidth + w->BorderLeft; + /* Skip if pixel coordinates are outside window */ + if (x < 0 || y < 0 || y >= w->Height || x >= w->Width) + continue; + /* Move pens to correct location */ Move(rp, (long) x, (long) y); @@ -1029,9 +932,7 @@ struct Window *w; glyph_node_index = glyph_buffer_index = 0; } void -amiga_print_glyph(window, color_index, glyph) -winid window; -int color_index, glyph; +amiga_print_glyph(winid window, int color_index, int glyph) { if (WINVERS_AMIV) amiv_lprint_glyph(window, color_index, glyph); @@ -1043,9 +944,7 @@ int color_index, glyph; * Glyph buffering routine. Called instead of WindowPuts(). */ void -amii_lprint_glyph(window, color_index, glyph) -winid window; -int color_index, glyph; +amii_lprint_glyph(winid window, int color_index, int glyph) { int fg_color, bg_color; struct amii_WinDesc *cw; @@ -1120,8 +1019,7 @@ static int usecolor; */ void -amii_start_glyphout(window) -winid window; +amii_start_glyphout(winid window) { struct amii_WinDesc *cw; struct Window *w; @@ -1195,7 +1093,7 @@ amii_end_glyphout(window) #ifdef OPT_DISPMAP /* don't use dispmap unless x & y are 8,16,24,32,48 and equal */ void -dispmap_sanity() +dispmap_sanity(void) { if (mxsize != mysize || dispmap_sanity1(mxsize) || dispmap_sanity1(mysize)) { @@ -1203,11 +1101,10 @@ dispmap_sanity() } } int -dispmap_sanity1(x) -int x; +dispmap_sanity1(int x) { static unsigned char valid[] = { 8, 16, 24, 32, 48, 0 }; - return !!strchr(valid, x); + return !strchr((char *)valid, x); } #endif /* OPT_DISPMAP */ #endif /* TESTING */ diff --git a/sys/amiga/windefs.h b/sys/amiga/windefs.h index 9e59da82f..cb6cbd3db 100644 --- a/sys/amiga/windefs.h +++ b/sys/amiga/windefs.h @@ -112,7 +112,7 @@ CLIPPING must be defined for the AMIGA version #endif #define WINVERS_AMII (strcmp("amii", windowprocs.name) == 0) -#define WINVERS_AMIV (strcmp("amitile", windowprocs.name) == 0) +#define WINVERS_AMIV (strcmp("amiv", windowprocs.name) == 0) #define WINVERS_AMIT (strcmp("amitty", windowprocs.name) == 0) /* cw->data[x] contains 2 characters worth of special information. These @@ -196,3 +196,4 @@ struct PDAT #undef MAXCOLORS #define MAXCOLORS 256 + diff --git a/sys/amiga/winfuncs.c b/sys/amiga/winfuncs.c index 9907ce1c6..7d451a665 100644 --- a/sys/amiga/winfuncs.c +++ b/sys/amiga/winfuncs.c @@ -14,7 +14,6 @@ #endif #include "patchlevel.h" -#include "date.h" extern struct TagItem scrntags[]; #ifndef CROSS_TO_AMIGA @@ -40,6 +39,11 @@ int xclipbord = 4, yclipbord = 2; #endif int mxsize, mysize; + +/* Track the last level we centered the clipping viewport on, so that + both amii_clear_nhwindow (pre-docrt centering) and amii_cliparound + (scroll/pan during play) can detect level changes. */ +static d_level clip_saved_level = { 127, 127 }; /* XXX */ struct Rectangle amii_oldover; struct Rectangle amii_oldmsg; @@ -58,8 +62,7 @@ int amii_otherBPen; long amii_libvers = LIBRARY_FONT_VERSION; void -ami_wininit_data(dir) -int dir; +ami_wininit_data(int dir) { extern unsigned short amii_init_map[AMII_MAXCOLORS]; extern unsigned short amiv_init_map[AMII_MAXCOLORS]; @@ -150,12 +153,13 @@ struct TagItem wintags[] = { }; #endif -void amii_destroy_nhwindow(win) /* just hide */ -register winid win; +void +amii_destroy_nhwindow(winid win) /* just hide */ { int i; int type; - register struct amii_WinDesc *cw; + struct amii_WinDesc *cw; + if (win == WIN_ERR || (cw = amii_wins[win]) == NULL) { panic(winpanicstr, win, "destroy_nhwindow"); @@ -260,26 +264,37 @@ PPC_LayerFillHook(void) struct RastPort *rp = (struct RastPort *) REG_A2; struct FillParams *fp = (struct FillParams *) REG_A1; #else +/* Assembly trampoline: Intuition calls LayerFillHook with arguments + in registers a0 (hook), a1 (fillparams), a2 (rastport). + Push them onto the stack and call the C implementation. */ +__asm( +" .globl _LayerFillHook\n" +"_LayerFillHook:\n" +" move.l a1,-(sp)\n" +" move.l a2,-(sp)\n" +" move.l a0,-(sp)\n" +" jsr _LayerFillHook_impl\n" +" lea 12(sp),sp\n" +" rts\n" +); void -LayerFillHook(void) +LayerFillHook_impl(struct Hook *hk, struct RastPort *rp, + struct FillParams *fp) { - register struct Hook *hk asm("a0"); - register struct RastPort *rp asm("a2"); - register struct FillParams *fp asm("a1"); #endif #else void #ifndef _DCC __interrupt #endif - __saveds __asm LayerFillHook(register __a0 struct Hook *hk, - register __a2 struct RastPort *rp, - register __a1 struct FillParams *fp) + __saveds __asm LayerFillHook(__a0 struct Hook *hk, + __a2 struct RastPort *rp, + __a1 struct FillParams *fp) { #endif - register long x, y, xmax, ymax; - register int apen; + long x, y, xmax, ymax; + int apen; struct RastPort rptmp; memcpy(&rptmp, rp, sizeof(struct RastPort)); @@ -321,15 +336,17 @@ void } #endif -amii_create_nhwindow(type) register int type; +winid +amii_create_nhwindow(int type) { - register struct Window *w = NULL; - register struct NewWindow *nw = NULL; - register struct amii_WinDesc *wd = NULL; + struct Window *w = NULL; + struct NewWindow *nw = NULL; + struct amii_WinDesc *wd = NULL; struct Window *mapwin = NULL, *stwin = NULL, *msgwin = NULL; - register int newid; + int newid; int maph, stath, scrfontysize; + scrfontysize = HackScreen->Font->ta_YSize; /* @@ -563,11 +580,7 @@ amii_create_nhwindow(type) register int type; case NHW_OVER: case NHW_MAP: if (wd) { -#ifdef __GNUC__ fillhook.h_Entry = (void *) &LayerFillHook; -#else - fillhook.h_Entry = (ULONG (*) ()) LayerFillHook; -#endif fillhook.h_Data = (void *) type; fillhook.h_SubEntry = 0; wd->hook = alloc(sizeof(fillhook)); @@ -665,6 +678,10 @@ amii_create_nhwindow(type) register int type; / w->RPort->TxHeight; wd->cols = (w->Width - w->BorderLeft - w->BorderRight - 2) / w->RPort->TxWidth; + /* Map window uses y+2 offset in amii_print_glyph, so cury + values go up to ROWNO+2. Ensure rows accommodates this. */ + if (type == NHW_MAP && wd->rows < ROWNO + 3) + wd->rows = ROWNO + 3; } /* Okay, now do the individual type initialization */ @@ -736,7 +753,9 @@ amii_create_nhwindow(type) register int type; */ wd->data = (char **) alloc(3 * sizeof(char *)); wd->data[0] = (char *) alloc(wd->cols + 10); + wd->data[0][0] = '\0'; wd->data[1] = (char *) alloc(wd->cols + 10); + wd->data[1][0] = '\0'; wd->data[2] = NULL; break; @@ -771,7 +790,7 @@ amii_create_nhwindow(type) register int type; SetMenuStrip(w, MenuStrip); /* Make our requesters come to our screen */ { - register struct Process *myProcess = + struct Process *myProcess = (struct Process *) FindTask(NULL); pr_WindowPtr = (struct Window *) (myProcess->pr_WindowPtr); myProcess->pr_WindowPtr = (APTR) w; @@ -787,17 +806,7 @@ amii_create_nhwindow(type) register int type; Abort(AG_OpenDev | AO_ConsoleDev); } - ConsoleDevice = -#ifndef CROSS_TO_AMIGA - (struct Library *) -#else - (struct Device * -# ifdef __CONSTLIBBASEDECL__ - __CONSTLIBBASEDECL__ -# endif /* __CONSTLIBBASEDECL__ */ - ) -#endif - ConsoleIO.io_Device; + ConsoleDevice = (struct Device *) ConsoleIO.io_Device; KbdBuffered = 0; #ifdef HACKFONT @@ -831,12 +840,23 @@ PPC_SM_Filter(void) ULONG modeID = (ULONG) REG_A1; struct ScreenModeRequester *smr = (struct ScreenModeRequester *) REG_A2; #else +/* Assembly trampoline for SM_Filter hook callback. + SM_Filter is the asm entry point; SM_Filter_impl is the C body. */ +extern void SM_Filter(void); +__asm( +" .globl _SM_Filter\n" +"_SM_Filter:\n" +" move.l a2,-(sp)\n" +" move.l a1,-(sp)\n" +" move.l a0,-(sp)\n" +" jsr _SM_Filter_impl\n" +" lea 12(sp),sp\n" +" rts\n" +); int -SM_Filter(void) +SM_Filter_impl(struct Hook *hk, ULONG modeID, + struct ScreenModeRequester *smr) { - register struct Hook *hk asm("a0"); - register ULONG modeID asm("a1"); - register struct ScreenModeRequester *smr asm("a2"); #endif #else int @@ -844,8 +864,8 @@ int __interrupt #endif __saveds __asm SM_Filter( - register __a0 struct Hook *hk, register __a1 ULONG modeID, - register __a2 struct ScreenModeRequester *smr) + __a0 struct Hook *hk, __a1 ULONG modeID, + __a2 struct ScreenModeRequester *smr) { #endif struct DimensionInfo dims; @@ -869,14 +889,13 @@ int /* Initialize the windowing environment */ void -amii_init_nhwindows(argcp, argv) -int *argcp; -char **argv; +amii_init_nhwindows(int *argcp, char **argv) { int i; struct Screen *wbscr; int forcenobig = 0; + if (HackScreen) panic("init_nhwindows() called twice", 0); @@ -1192,9 +1211,16 @@ char **argv; } #endif - if (WINVERS_AMIV) + if (WINVERS_AMIV) { + extern char *tilefile; + if (amii_numcolors >= 32) { + tilefile = "NetHack:tiles/tiles32.iff"; + amii_numcolors = 32; + } else { + tilefile = "NetHack:tiles/tiles16.iff"; + } amii_bmhd = ReadTileImageFiles(); - else + } else memcpy(amii_initmap, amii_init_map, sizeof(amii_initmap)); memcpy(sysflags.amii_curmap, amii_initmap, sizeof(sysflags.amii_curmap)); @@ -1361,11 +1387,11 @@ amii_setdrawpens(struct Window *w, int type) /* Clear the indicated window */ void -amii_clear_nhwindow(win) -register winid win; +amii_clear_nhwindow(winid win) { - register struct amii_WinDesc *cw; - register struct Window *w; + struct amii_WinDesc *cw; + struct Window *w; + if (reclip == 2) return; @@ -1394,6 +1420,40 @@ register winid win; amii_setfillpens(w, cw->type); SetDrMd(w->RPort, JAM2); +#ifdef CLIPPING + /* When clearing the map for a full redraw (docrt/cls), center the + clipping viewport on the player BEFORE the map is redrawn. + Without this, the first docrt() after a level change or new game + draws with stale clip coordinates (typically 0,0), leaving the + player off-screen until their first move triggers amii_cliparound. */ + if (cw->type == NHW_MAP && clipping && u.ux + && !on_level(&u.uz, &clip_saved_level)) { + int COx, LIx; + struct RastPort *rp = w->RPort; + + if (Is_rogue_level(&u.uz)) { + COx = (w->Width - w->BorderLeft - w->BorderRight) / rp->TxWidth; + LIx = (w->Height - w->BorderTop - w->BorderBottom) / rp->TxHeight; + } else { + COx = CO; + LIx = LI; + } + clipx = max(0, (int) u.ux - COx / 2); + clipxmax = clipx + COx; + if (clipxmax > COLNO) { + clipxmax = COLNO; + clipx = clipxmax - COx; + } + clipy = max(0, (int) u.uy - LIx / 2); + clipymax = clipy + LIx; + if (clipymax > ROWNO) { + clipymax = ROWNO; + clipy = clipymax - LIx; + } + clip_saved_level = u.uz; + } +#endif + if (cw->type == NHW_MENU || cw->type == NHW_TEXT) { RectFill(w->RPort, w->BorderLeft, w->BorderTop, w->Width - w->BorderRight - 1, @@ -1418,11 +1478,10 @@ register winid win; /* Dismiss the window from the screen */ void -dismiss_nhwindow(win) -register winid win; +dismiss_nhwindow(winid win) { - register struct Window *w; - register struct amii_WinDesc *cw; + struct Window *w; + struct amii_WinDesc *cw; if (win == WIN_ERR || (cw = amii_wins[win]) == NULL) { panic(winpanicstr, win, "dismiss_nhwindow"); @@ -1458,9 +1517,9 @@ register winid win; } void -amii_exit_nhwindows(str) -const char *str; +amii_exit_nhwindows(const char *str) { + /* Seems strange to have to do this... but we need the BASE window * left behind... */ @@ -1475,15 +1534,14 @@ const char *str; } void -amii_display_nhwindow(win, blocking) -winid win; -boolean blocking; +amii_display_nhwindow(winid win, boolean blocking) { menu_item *mip; int cnt; static int lastwin = -1; struct amii_WinDesc *cw; + if (!Initialized) return; lastwin = win; @@ -1513,13 +1571,12 @@ boolean blocking; } void -amii_curs(window, x, y) -winid window; -register int x, y; +amii_curs(winid window, int x, int y) { - register struct amii_WinDesc *cw; - register struct Window *w; - register struct RastPort *rp; + struct amii_WinDesc *cw; + struct Window *w; + struct RastPort *rp; + if (window == WIN_ERR || (cw = amii_wins[window]) == NULL) panic(winpanicstr, window, "curs"); @@ -1540,8 +1597,14 @@ register int x, y; cw->curx = x; cw->cury = y; + /* Silently skip rendering for out-of-bounds coordinates */ + if (cw->rows > 0 && y >= cw->rows) + return; + if (cw->cols > 0 && x >= cw->cols) + return; + #ifdef DEBUG - if (x < 0 || y < 0 || y >= cw->rows || x >= cw->cols) { + if (0 && (x < 0 || y < 0 || y >= cw->rows || x >= cw->cols)) { char *s = "[unknown type]"; switch (cw->type) { case NHW_MESSAGE: @@ -1663,12 +1726,10 @@ printf("pos: (%d,%d)->(%d,%d)\n",x,y,qqx,qqy); } void -amii_set_text_font(name, size) -char *name; -int size; +amii_set_text_font(char *name, int size) { - register int i; - register struct amii_WinDesc *cw; + int i; + struct amii_WinDesc *cw; int osize = TextsFont13.ta_YSize; static char nname[100]; @@ -1709,11 +1770,10 @@ int size; } void -kill_nhwindows(all) -register int all; +kill_nhwindows(int all) { - register int i; - register struct amii_WinDesc *cw; + int i; + struct amii_WinDesc *cw; /* Foreach open window in all of amii_wins[], CloseShWindow, free memory */ @@ -1726,12 +1786,10 @@ register int all; } void -amii_cl_end(cw, curs_pos) -register struct amii_WinDesc *cw; -register int curs_pos; +amii_cl_end(struct amii_WinDesc *cw, int curs_pos) { - register struct Window *w = cw->win; - register int oy, ox; + struct Window *w = cw->win; + int oy, ox; if (!w) panic("NULL window pointer in amii_cl_end()"); @@ -1745,12 +1803,11 @@ register int curs_pos; } void -cursor_off(window) -winid window; +cursor_off(winid window) { - register struct amii_WinDesc *cw; - register struct Window *w; - register struct RastPort *rp; + struct amii_WinDesc *cw; + struct Window *w; + struct RastPort *rp; int curx, cury; int x, y; long dmode; @@ -1805,13 +1862,12 @@ winid window; } void -cursor_on(window) -winid window; +cursor_on(winid window) { int x, y; - register struct amii_WinDesc *cw; - register struct Window *w; - register struct RastPort *rp; + struct amii_WinDesc *cw; + struct Window *w; + struct RastPort *rp; unsigned char ch; long dmode; short apen, bpen; @@ -1858,10 +1914,13 @@ winid window; if (WINVERS_AMIV && cw->type == NHW_MAP) { cursor_common(rp, x, y); } else { - Move(rp, x, y); - ch = CURSOR_CHAR; - Text(rp, &ch, 1); - Move(rp, x, y); + if (w && x >= 0 && y >= 0 + && x < w->Width && y < w->Height) { + Move(rp, x, y); + ch = CURSOR_CHAR; + Text(rp, &ch, 1); + Move(rp, x, y); + } } SetDrMd(rp, dmode); @@ -1870,9 +1929,7 @@ winid window; } static void -cursor_common(rp, x, y) -struct RastPort *rp; -int x, y; +cursor_common(struct RastPort *rp, int x, int y) { int x1, x2, y1, y2; @@ -1895,29 +1952,27 @@ int x, y; } void -amii_suspend_nhwindows(str) -const char *str; +amii_suspend_nhwindows(const char *str) { if (HackScreen) ScreenToBack(HackScreen); } void -amii_resume_nhwindows() +amii_resume_nhwindows(void) { if (HackScreen) ScreenToFront(HackScreen); } void -amii_bell() +amii_bell(void) { DisplayBeep(NULL); } void -removetopl(cnt) -int cnt; +removetopl(int cnt) { struct amii_WinDesc *cw = amii_wins[WIN_MESSAGE]; /* NB - this is sufficient for @@ -1935,7 +1990,7 @@ int cnt; #ifdef PORT_HELP void -port_help() +port_help(void) { display_file(PORT_HELP, 1); } @@ -1951,17 +2006,20 @@ port_help() */ void -amii_print_glyph(win, x, y, glyph, bkglyph) -winid win; -coordxy x, y; -int glyph, bkglyph; +amii_print_glyph(winid win, coordxy x, coordxy y, + const glyph_info *glyphinfo, + const glyph_info *bkglyphinfo UNUSED) { struct amii_WinDesc *cw; uchar ch; + int glyph; int color, och; extern const int zapcolors[]; unsigned special; + + glyph = glyphinfo->glyph; + /* In order for the overview window to work, we can not clip here */ if (!WINVERS_AMIV) { #ifdef CLIPPING @@ -1993,14 +2051,8 @@ if(u.uz.dlevel != x){ } else /* AMII, or Rogue level in either version */ { /* map glyph to character and color */ -#if 0 - (void) mapglyph(glyph, &och, &color, &special, x, y, 0); - ch = (uchar) och; -#else - glyph_info gi; - map_glyphinfo(0, 0, glyph, 0, &gi); - ch = gi.ttychar; -#endif + ch = glyphinfo->ttychar; + color = glyphinfo->gm.sym.color; if (WINVERS_AMIV) { /* implies Rogue level here */ amii_curs(win, x, y); amiga_print_glyph(win, NO_COLOR, ch + 10000); @@ -2021,11 +2073,11 @@ if(u.uz.dlevel != x){ /* Make sure the user sees a text string when no windowing is available */ void -amii_raw_print(s) -register const char *s; +amii_raw_print(const char *s) { int argc = 0; + if (!s) return; if (amiIDisplay) @@ -2057,11 +2109,11 @@ register const char *s; */ void -amii_raw_print_bold(s) -register const char *s; +amii_raw_print_bold(const char *s) { int argc = 0; + if (!s) return; @@ -2092,9 +2144,10 @@ register const char *s; /* Rebuild/update the inventory if the window is up. */ void -amii_update_inventory() +amii_update_inventory(int arg UNUSED) { - register struct amii_WinDesc *cw; + struct amii_WinDesc *cw; + if (WIN_INVEN != WIN_ERR && (cw = amii_wins[WIN_INVEN]) && cw->type == NHW_MENU && cw->win) { @@ -2102,10 +2155,17 @@ amii_update_inventory() } } +/* Stub for ctrl_nhwindow - no special handling needed for Amiga */ +win_request_info * +amii_ctrl_nhwindow(winid window UNUSED, int request UNUSED, win_request_info *wri UNUSED) +{ + return (win_request_info *) 0; +} + /* Humm, doesn't really do anything useful */ void -amii_mark_synch() +amii_mark_synch(void) { if (!amiIDisplay) fflush(stderr); @@ -2116,7 +2176,7 @@ amii_mark_synch() * ask for a key to be pressed. */ void -amii_wait_synch() +amii_wait_synch(void) { if (!amiIDisplay || amiIDisplay->rawprint) { if (amiIDisplay) @@ -2130,7 +2190,7 @@ amii_wait_synch() } void -amii_setclipped() +amii_setclipped(void) { #ifdef CLIPPING clipping = TRUE; @@ -2141,12 +2201,35 @@ amii_setclipped() #endif } +/* Redraw a rectangular region of the map via newsym(). Used after + ScrollRaster() shifts existing pixels — only the exposed strip needs + redrawing. Does NOT flush; the caller flushes after all regions. */ +static void +redraw_map_region(int x1, int y1, int x2, int y2) +{ + int x, y; + + if (!u.ux) + return; + if (x1 < 1) x1 = 1; + if (y1 < 0) y1 = 0; + if (x2 >= COLNO) x2 = COLNO - 1; + if (y2 >= ROWNO) y2 = ROWNO - 1; + for (y = y1; y <= y2; y++) + for (x = x1; x <= x2; x++) { + /* Invalidate the glyph buffer so newsym() redraws even if + * the glyph hasn't changed — the pixels were shifted by + * ScrollRaster and no longer match the buffer. */ + gg.gbuf[y][x].glyphinfo.glyph = NO_GLYPH; + newsym(x, y); + } +} + /* XXX still to do: suppress scrolling if we violate the boundary but the * edge of the map is already displayed */ void -amii_cliparound(x, y) -register int x, y; +amii_cliparound(int x, int y) { #ifdef CLIPPING int oldx = clipx, oldy = clipy; @@ -2155,8 +2238,9 @@ register int x, y; #define SCROLLCNT 1 /* Get there in 3 moves... */ int scrollcnt = SCROLLCNT; /* ...or 1 if we changed level */ - if (!clipping) /* And 1 in anycase, cleaner, simpler, quicker */ + if (!clipping) { /* And 1 in anycase, cleaner, simpler, quicker */ return; + } if (Is_rogue_level(&u.uz)) { struct Window *w = amii_wins[WIN_MAP]->win; @@ -2173,16 +2257,22 @@ register int x, y; * reasonablely large window extra motion is avoided; for * the rogue level hopefully this means no motion at all. */ - { - static d_level saved_level = { 127, 127 }; /* XXX */ - - if (!on_level(&u.uz, &saved_level)) { - scrollcnt = 1; /* jump with blanking */ - clipx = clipy = 0; - clipxmax = COx; - clipymax = LIx; - saved_level = u.uz; /* save as new current level */ + if (!on_level(&u.uz, &clip_saved_level)) { + scrollcnt = 1; /* jump with blanking */ + /* Center viewport on the player for the new level. */ + clipx = max(0, x - COx / 2); + clipxmax = clipx + COx; + if (clipxmax > COLNO) { + clipxmax = COLNO; + clipx = clipxmax - COx; } + clipy = max(0, y - LIx / 2); + clipymax = clipy + LIx; + if (clipymax > ROWNO) { + clipymax = ROWNO; + clipy = clipymax - LIx; + } + clip_saved_level = u.uz; /* save as new current level */ } if (x <= clipx + xclipbord) { @@ -2204,13 +2294,12 @@ register int x, y; reclip = 1; if (clipx != oldx || clipy != oldy || clipxmax != oldxmax || clipymax != oldymax) { -#ifndef NOSCROLLRASTER struct Window *w = amii_wins[WIN_MAP]->win; struct RastPort *rp = w->RPort; - int xdelta, ydelta, xmod, ymod, i; - int incx, incy, mincx, mincy; - int savex, savey, savexmax, saveymax; - int scrx, scry; + int dx = clipx - oldx; + int dy = clipy - oldy; + int scrx, scry; /* tile pixel dimensions */ + int halfW, halfH; /* half viewport in tiles */ if (Is_rogue_level(&u.uz)) { scrx = rp->TxWidth; @@ -2220,129 +2309,63 @@ register int x, y; scry = mysize; } - /* Ask that the glyph routines not draw the overview window */ - reclip = 2; - cursor_off(WIN_MAP); + halfW = COx / 2; + halfH = LIx / 2; - /* Compute how far we are moving in terms of tiles */ - mincx = clipx - oldx; - mincy = clipy - oldy; + /* Erase the map cursor before shifting pixels, otherwise the + old cursor position leaves a COMPLEMENT artifact on screen. */ + if (WIN_MAP != WIN_ERR) + cursor_off(WIN_MAP); - /* How many tiles to get there in SCROLLCNT moves */ - incx = (clipx - oldx) / scrollcnt; - incy = (clipy - oldy) / scrollcnt; - - /* If less than SCROLLCNT tiles, then move by 1 tile if moving at all - */ - if (incx == 0) - incx = (mincx != 0); - if (incy == 0) - incy = (mincy != 0); - - /* Get count of pixels to move each iteration and final pixel count */ - xdelta = ((clipx - oldx) * scrx) / scrollcnt; - xmod = ((clipx - oldx) * scrx) % scrollcnt; - ydelta = ((clipy - oldy) * scry) / scrollcnt; - ymod = ((clipy - oldy) * scry) % scrollcnt; - - /* Preserve the final move location */ - savex = clipx; - savey = clipy; - saveymax = clipymax; - savexmax = clipxmax; - -/* - * Set clipping rectangle to be just the region that will be exposed so - * that drawing will be faster - */ -#if 0 /* Doesn't seem to work quite the way it should */ - /* In some cases hero is 'centered' offscreen */ - if( xdelta < 0 ) - { - clipx = oldx; - clipxmax = clipx + incx; - } - else if( xdelta > 0 ) - { - clipxmax = oldxmax; - clipx = clipxmax - incx; - } - else - { - clipx = oldx; - clipxmax = oldxmax; - } - - if( ydelta < 0 ) - { - clipy = oldy; - clipymax = clipy + incy; - } - else if( ydelta > 0 ) - { - clipymax = oldymax; - clipy = clipymax - incy; - } - else - { - clipy = oldy; - clipymax = oldymax; - } -#endif - /* Now, in scrollcnt moves, move the picture toward the final view */ - for (i = 0; i < scrollcnt; ++i) { -#ifdef DISPMAP - if (i == scrollcnt - 1 && (xmod != 0 || ymod != 0) - && (xdelta != 0 || ydelta != 0)) { - incx += (clipx - oldx) % scrollcnt; - incy += (clipy - oldy) % scrollcnt; - xdelta += xmod; - ydelta += ymod; + /* Large jumps (teleport, level change): just redraw everything. */ + if (abs(dx) > halfW || abs(dy) > halfH + || scrollcnt != SCROLLCNT) { + { + int savedAPen = rp->FgPen; + int savedDrMd = rp->DrawMode; + SetAPen(rp, amii_otherBPen); + SetDrMd(rp, JAM1); + RectFill(rp, w->BorderLeft, w->BorderTop, + w->Width - w->BorderRight - 1, + w->Height - w->BorderBottom - 1); + SetAPen(rp, savedAPen); + SetDrMd(rp, savedDrMd); } -#endif - /* Scroll the raster if we are scrolling */ - if (xdelta != 0 || ydelta != 0) { - ScrollRaster(rp, xdelta, ydelta, w->BorderLeft, w->BorderTop, - w->Width - w->BorderRight - 1, - w->Height - w->BorderBottom - 1); + redraw_map(FALSE); + } else { + /* Flush pending glyphs — they reference old clip coords */ + flush_glyph_buffer(w); - if (mincx == 0) - incx = 0; - else - mincx -= incx; + /* Hardware-blit the viewport by the scroll delta */ + ScrollRaster(rp, dx * scrx, dy * scry, + w->BorderLeft, w->BorderTop, + w->Width - w->BorderRight - 1, + w->Height - w->BorderBottom - 1); - clipx += incx; - clipxmax += incx; + /* Redraw only the exposed strip(s) */ + if (dx > 0) + redraw_map_region(clipxmax - dx, clipy, + clipxmax, clipymax - 1); + else if (dx < 0) + redraw_map_region(clipx, clipy, + clipx - dx, clipymax - 1); - if (mincy == 0) - incy = 0; - else - mincy -= incy; + if (dy > 0) + redraw_map_region(clipx, clipymax - dy, + clipxmax, clipymax - 1); + else if (dy < 0) + redraw_map_region(clipx, clipy, + clipxmax, clipy - dy - 1); - clipy += incy; - clipymax += incy; - - /* Draw the exposed portion */ - redraw_map(); - flush_glyph_buffer(amii_wins[WIN_MAP]->win); - } + flush_glyph_buffer(w); } - - clipx = savex; - clipy = savey; - clipymax = saveymax; - clipxmax = savexmax; -#endif - redraw_map(); - flush_glyph_buffer(amii_wins[WIN_MAP]->win); } reclip = 0; #endif } void -flushIDCMP(port) -struct MsgPort *port; +flushIDCMP(struct MsgPort *port) { struct Message *msg; while (msg = GetMsg(port)) diff --git a/sys/amiga/winkey.c b/sys/amiga/winkey.c index 9534c0c6d..f2686bfde 100644 --- a/sys/amiga/winkey.c +++ b/sys/amiga/winkey.c @@ -12,12 +12,14 @@ #include "winproto.h" #endif -amii_nh_poskey(x, y, mod) int *x, *y, *mod; +int +amii_nh_poskey(coordxy *x, coordxy *y, int *mod) { struct amii_WinDesc *cw; WETYPE type; struct RastPort *rp; struct Window *w; + /* No entry log for nh_poskey -- too noisy (called constantly) */ if (cw = amii_wins[WIN_MESSAGE]) { cw->wflags &= ~FLMAP_SKIP; @@ -59,10 +61,11 @@ amii_nh_poskey(x, y, mod) int *x, *y, *mod; } int -amii_nhgetch() +amii_nhgetch(void) { int ch; struct amii_WinDesc *cw = amii_wins[WIN_MESSAGE]; + /* No entry log for nhgetch -- too noisy (called constantly) */ if (WIN_MAP != WIN_ERR && amii_wins[WIN_MAP]) { cursor_on(WIN_MAP); @@ -75,15 +78,15 @@ amii_nhgetch() } void -amii_get_nh_event() +amii_get_nh_event(void) { /* nothing now - later I have no idea. Is this just a Mac hook? */ } void -amii_getret() +amii_getret(void) { - register int c; + int c; raw_print(""); raw_print("Press Return..."); diff --git a/sys/amiga/winproto.h b/sys/amiga/winproto.h index efa1e9b58..d855e50fe 100644 --- a/sys/amiga/winproto.h +++ b/sys/amiga/winproto.h @@ -8,10 +8,9 @@ void EditClipping(void); void DrawCol(struct Window *w, int idx, UWORD *colors); void DispCol(struct Window *w, int idx, UWORD *colors); void amii_change_color(int, long, int); -char *amii_get_color_string(); +char *amii_get_color_string(void); void amii_getlin(const char *prompt, char *bufp); void getlind(const char *prompt, char *bufp, const char *dflt); -char *amii_get_color_string(void); int filecopy(char *from, char *to); char *basename(char *str); char *dirname(char *str); @@ -33,15 +32,15 @@ void amii_scrollmsg(register struct Window *w, register struct amii_WinDesc *cw); /* winkey.c */ -int amii_nh_poskey(int *x, int *y, int *mod); +int amii_nh_poskey(coordxy *x, coordxy *y, int *mod); int amii_nhgetch(void); void amii_get_nh_event(void); void amii_getret(void); /* winmenu.c */ void amii_start_menu(winid window, unsigned long); -void amii_add_menu(winid, int, const anything *, CHAR_P, CHAR_P, int, - const char *, unsigned int); +void amii_add_menu(winid, const glyph_info *, const anything *, CHAR_P, CHAR_P, + int, int, const char *, unsigned int); void amii_end_menu(winid, const char *); int amii_select_menu(winid, int, menu_item **); int DoMenuScroll(int win, int blocking, int how, menu_item **); @@ -55,7 +54,6 @@ struct Window *OpenShWindow(struct NewWindow *nw); void CloseShWindow(struct Window *win); int ConvertKey(struct IntuiMessage *message); int kbhit(void); -int kbhit(void); int amikbhit(void); int WindowGetchar(void); WETYPE WindowGetevent(void); @@ -101,19 +99,19 @@ void amii_resume_nhwindows(void); void amii_bell(void); void removetopl(int cnt); void port_help(void); -void amii_print_glyph(winid win, coordxy x, coordxy y, int glyph, int bkglyph); +void amii_print_glyph(winid win, coordxy x, coordxy y, const glyph_info *glyphinfo, const glyph_info *bkglyphinfo); void amii_raw_print(const char *s); void amii_raw_print_bold(const char *s); -void amii_update_inventory(void); +void amii_update_inventory(int); void amii_mark_synch(void); void amii_wait_synch(void); void amii_setclipped(void); void amii_cliparound(int x, int y); void amii_set_text_font(char *font, int size); -BitMapHeader ReadImageFiles(char **, struct BitMap **, char **); +BitMapHeader ReadImageFile(const char *, struct BitMap **); +void FreeImageFile(struct BitMap **); BitMapHeader ReadTileImageFiles(void); -void FreeImageFiles(char **, struct BitMap **); -void FreeTileImageFiles(); +void FreeTileImageFiles(void); /* winami.c */ #ifdef SHAREDLIB @@ -124,12 +122,10 @@ void amii_askname(void); void amii_player_selection(void); void RandomWindow(char *name); int amii_get_ext_cmd(void); -char amii_yn_function(const char *prompt, const char *resp, char def); char amii_yn_function(const char *query, const char *resp, char def); void amii_display_file(const char *fn, boolean complain); void SetBorder(struct Gadget *gd); -void *malloc(register unsigned size); -void free(void *q); +/* malloc/free provided by stdlib.h */ #ifdef SHAREDLIB /* amilib.c */ @@ -141,6 +137,8 @@ void setup_librefs(WinamiBASE *base); void Abort(long rc); #endif +win_request_info *amii_ctrl_nhwindow(winid, int, win_request_info *); + /* amirip.c */ void amii_outrip(winid tmpwin, int how, time_t when); @@ -151,4 +149,3 @@ int GlyphToIcon(int glyph); void dispmap_sanity(void); int dispmap_sanity1(int); #endif -void FreeTileImageFiles(void); diff --git a/sys/amiga/winreq.c b/sys/amiga/winreq.c index af4c303de..910adeb86 100644 --- a/sys/amiga/winreq.c +++ b/sys/amiga/winreq.c @@ -67,16 +67,16 @@ struct NewWindow StrWindow = { void ClearCol(struct Window *w); void -EditColor() +EditColor(void) { - extern char configfile[]; + const char *configfile = get_configfile(); int i, done = 0, okay = 0; long code, qual, class; - register struct Gadget *gd, *dgad; - register struct Window *nw; - register struct IntuiMessage *imsg; - register struct PropInfo *pip; - register struct Screen *scrn; + struct Gadget *gd, *dgad; + struct Window *nw; + struct IntuiMessage *imsg; + struct PropInfo *pip; + struct Screen *scrn; long aidx; int msx, msy; int curcol = 0, drag = 0; @@ -128,11 +128,7 @@ EditColor() #ifdef INTUI_NEW_LOOK Col_NewWindowStructure1.Extension = wintags; Col_NewWindowStructure1.Flags |= WFLG_NW_EXTENDED; -#ifdef __GNUC__ fillhook.h_Entry = (void *) &LayerFillHook; -#else - fillhook.h_Entry = (ULONG (*) ()) LayerFillHook; -#endif fillhook.h_Data = (void *) -2; fillhook.h_SubEntry = 0; #endif @@ -248,7 +244,7 @@ EditColor() for (i = 0; i < amii_numcolors; ++i) { fprintf(nfp, "%03x", colors[i]); if ((i + 1) < amii_numcolors) - putc(',', nfp); + putc('/', nfp); } putc('\n', nfp); } @@ -356,11 +352,11 @@ EditClipping(void) char buf[40]; int done = 0, okay = 0; long code, qual, class; - register struct Gadget *gd, *dgad; - register struct Window *nw; - register struct IntuiMessage *imsg; - register struct PropInfo *pip; - register struct Screen *scrn; + struct Gadget *gd, *dgad; + struct Window *nw; + struct IntuiMessage *imsg; + struct PropInfo *pip; + struct Screen *scrn; long aidx; int lmxsize = mxsize, lmysize = mysize; int lxclipbord = xclipbord, lyclipbord = yclipbord; @@ -388,11 +384,7 @@ EditClipping(void) #ifdef INTUI_NEW_LOOK ClipNewWindowStructure1.Extension = wintags; ClipNewWindowStructure1.Flags |= WFLG_NW_EXTENDED; -#ifdef __GNUC__ fillhook.h_Entry = (void *) &LayerFillHook; -#else - fillhook.h_Entry = (ULONG (*) ()) LayerFillHook; -#endif fillhook.h_Data = (void *) -2; fillhook.h_SubEntry = 0; #endif @@ -546,8 +538,7 @@ EditClipping(void) } char * -dirname(str) -char *str; +dirname(char *str) { char *t, c; static char dir[300]; @@ -555,9 +546,9 @@ char *str; t = strrchr(str, '/'); if (!t) t = strrchr(str, ':'); - if (!t) - t = str; - else { + if (!t) { + dir[0] = '\0'; + } else { c = *t; *t = 0; strcpy(dir, str); @@ -567,8 +558,7 @@ char *str; } char * -basename(str) -char *str; +basename(char *str) { char *t; @@ -582,7 +572,8 @@ char *str; return (t); } -filecopy(from, to) char *from, *to; +int +filecopy(char *from, char *to) { char *buf; int i = 0; @@ -606,7 +597,7 @@ filecopy(from, to) char *from, *to; } /* The colornames, and the default values for the pens */ -static struct COLDEF { +struct COLDEF { char *name, *defval; }; struct COLDEF amii_colnames[AMII_MAXCOLORS] = { @@ -657,10 +648,7 @@ ClearCol(struct Window *w) } void -DrawCol(w, idx, colors) -struct Window *w; -int idx; -UWORD *colors; +DrawCol(struct Window *w, int idx, UWORD *colors) { int bxorx, bxory, bxxlen, bxylen; int i, incx, incy, r, g, b; @@ -742,10 +730,7 @@ UWORD *colors; } void -DispCol(w, idx, colors) -struct Window *w; -int idx; -UWORD *colors; +DispCol(struct Window *w, int idx, UWORD *colors) { char buf[50]; char *colname, *defval; @@ -824,26 +809,21 @@ amii_setpens(int count) /* Generate a requester for a string value. */ void -amii_getlin(prompt, bufp) -const char *prompt; -char *bufp; +amii_getlin(const char *prompt, char *bufp) { getlind(prompt, bufp, 0); } /* and with default */ void -getlind(prompt, bufp, dflt) -const char *prompt; -char *bufp; -const char *dflt; +getlind(const char *prompt, char *bufp, const char *dflt) { #ifndef TOPL_GETLINE - register struct Window *cwin; - register struct IntuiMessage *imsg; - register long class, code, qual; - register int aredone = 0; - register struct Gadget *gd; + struct Window *cwin; + struct IntuiMessage *imsg; + long class, code, qual; + int aredone = 0; + struct Gadget *gd; static int once; *StrString = 0; @@ -872,11 +852,7 @@ const char *dflt; #ifdef INTUI_NEW_LOOK StrWindow.Extension = wintags; StrWindow.Flags |= WFLG_NW_EXTENDED; -#ifdef __GNUC__ fillhook.h_Entry = (void *) &LayerFillHook; -#else - fillhook.h_Entry = (ULONG (*) ()) LayerFillHook; -#endif fillhook.h_Data = (void *) -2; fillhook.h_SubEntry = 0; #endif @@ -1006,9 +982,7 @@ const char *dflt; } void -amii_change_color(pen, val, rev) -int pen, rev; -long val; +amii_change_color(int pen, long val, int rev) { if (rev) sysflags.amii_curmap[pen] = ~val; @@ -1020,12 +994,13 @@ long val; } char * -amii_get_color_string() +amii_get_color_string(void) { int i; char s[10]; static char buf[BUFSZ]; + *buf = 0; for (i = 0; i < min(32, amii_numcolors); ++i) { sprintf(s, "%s%03lx", i ? "/" : "", (long) sysflags.amii_curmap[i]); diff --git a/sys/amiga/winstr.c b/sys/amiga/winstr.c index ea7d65343..a3801fff1 100644 --- a/sys/amiga/winstr.c +++ b/sys/amiga/winstr.c @@ -14,10 +14,7 @@ /* Put a string into the indicated window using the indicated attribute */ void -amii_putstr(window, attr, str) -winid window; -int attr; -const char *str; +amii_putstr(winid window, int attr, const char *str) { int fudge; int len; @@ -27,6 +24,7 @@ const char *str; int i, j, n0, bottom, totalvis, wheight; static int wrapping = 0; + /* Always try to avoid a panic when there is no window */ if (window == WIN_ERR) { window = WIN_BASE; @@ -101,13 +99,13 @@ const char *str; memcpy(cw->data, &cw->data[1], (iflags.msg_history - 1) * sizeof(char *)); cw->data[iflags.msg_history - 1] = - (char *) alloc(strlen(gt.toplines) + 5); + (char *) alloc(strlen(gt.toplines) + SOFF + 4); strcpy(cw->data[i = iflags.msg_history - 1] + SOFF + (scrollmsg != 0), gt.toplines); } else { /* Otherwise, allocate a new one and copy the line in */ - cw->data[cw->maxrow] = (char *) alloc(strlen(gt.toplines) + 5); + cw->data[cw->maxrow] = (char *) alloc(strlen(gt.toplines) + SOFF + 4); strcpy(cw->data[i = cw->maxrow++] + SOFF + (scrollmsg != 0), gt.toplines); } @@ -196,7 +194,10 @@ const char *str; /* Display when beam at top to avoid flicker... */ WaitTOF(); - Text(w->RPort, (char *) str, strlen((char *) str)); + { int slen = strlen((char *) str); + if (slen > cw->cols) slen = cw->cols; + Text(w->RPort, (char *) str, slen); + } if (cw->cols > strlen(str)) TextSpaces(w->RPort, cw->cols - strlen(str)); @@ -286,9 +287,7 @@ const char *str; } void -amii_scrollmsg(w, cw) -register struct Window *w; -register struct amii_WinDesc *cw; +amii_scrollmsg(struct Window *w, struct amii_WinDesc *cw) { int bottom, wheight; @@ -311,8 +310,7 @@ register struct amii_WinDesc *cw; } int -amii_msgborder(w) -struct Window *w; +amii_msgborder(struct Window *w) { register int bottom; @@ -325,8 +323,7 @@ struct Window *w; } void -outmore(cw) -register struct amii_WinDesc *cw; +outmore(struct amii_WinDesc *cw) { struct Window *w = cw->win; @@ -359,11 +356,7 @@ register struct amii_WinDesc *cw; } void -outsubstr(cw, str, len, fudge) -register struct amii_WinDesc *cw; -char *str; -int len; -int fudge; +outsubstr(struct amii_WinDesc *cw, char *str, int len, int fudge) { struct Window *w = cw->win; @@ -387,10 +380,7 @@ int fudge; /* Put a graphics character onto the screen */ void -amii_putsym(st, i, y, c) -winid st; -int i, y; -CHAR_P c; +amii_putsym(winid st, int i, int y, CHAR_P c) { amii_curs(st, i, y); Text(amii_wins[st]->win->RPort, &c, 1); @@ -399,8 +389,7 @@ CHAR_P c; /* Add to the last line in the message window */ void -amii_addtopl(s) -const char *s; +amii_addtopl(const char *s) { register struct amii_WinDesc *cw = amii_wins[WIN_MESSAGE]; @@ -413,9 +402,7 @@ const char *s; } void -TextSpaces(rp, nr) -struct RastPort *rp; -int nr; +TextSpaces(struct RastPort *rp, int nr) { if (nr < 1) return; @@ -429,7 +416,7 @@ int nr; } void -amii_remember_topl() +amii_remember_topl(void) { /* ignore for now. I think this will be done automatically by * the code writing to the message window, but I could be wrong. @@ -437,12 +424,13 @@ amii_remember_topl() } int -amii_doprev_message() +amii_doprev_message(void) { struct amii_WinDesc *cw; struct Window *w; char *str; + if (WIN_MESSAGE == WIN_ERR || (cw = amii_wins[WIN_MESSAGE]) == NULL || (w = cw->win) == NULL) { panic(winpanicstr, WIN_MESSAGE, "doprev_message"); diff --git a/sys/share/pcmain.c b/sys/share/pcmain.c index 8850e2d82..fdfc599c6 100644 --- a/sys/share/pcmain.c +++ b/sys/share/pcmain.c @@ -128,6 +128,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ choose_windows(DEFAULT_WINDOW_SYS); + #if !defined(AMIGA) && !defined(GNUDOS) /* Save current directory and make sure it gets restored when * the game is exited. @@ -147,6 +148,10 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ if (dir == (char *) 0) dir = exepath(argv[0]); #endif +#if defined(AMIGA) && defined(HACKDIR) + if (dir == (char *) 0) + dir = HACKDIR; +#endif #ifdef _MSC_VER if (IsDebuggerPresent()) { static char exepath[_MAX_PATH]; diff --git a/sys/unix/hints/include/cross-post.370 b/sys/unix/hints/include/cross-post.370 index 01a998572..9d7b3a313 100644 --- a/sys/unix/hints/include/cross-post.370 +++ b/sys/unix/hints/include/cross-post.370 @@ -229,6 +229,76 @@ mipspkg: dodata $(GAMEBIN) $(TARGETPFX)recover @echo MIPS package zip file $(TARGETPFX)nh370mips.zip endif # CROSS_TO_MIPS + +ifdef CROSS_TO_AMIGA +$(TARGETPFX)amidos.o : ../sys/amiga/amidos.c $(HACK_H) +$(TARGETPFX)amigst.o : ../sys/amiga/amigst.c $(HACK_H) +$(TARGETPFX)amirip.o : ../sys/amiga/amirip.c $(HACK_H) +$(TARGETPFX)amistack.o : ../sys/amiga/amistack.c $(HACK_H) +$(TARGETPFX)amitty.o : ../sys/amiga/amitty.c $(HACK_H) +$(TARGETPFX)amiwind.o : ../sys/amiga/amiwind.c \ + ../sys/amiga/amimenu.c $(HACK_H) +$(TARGETPFX)winami.o : ../sys/amiga/winami.c $(HACK_H) +$(TARGETPFX)winchar.o : ../sys/amiga/winchar.c tile.c $(HACK_H) +$(TARGETPFX)winfuncs.o : ../sys/amiga/winfuncs.c $(HACK_H) +$(TARGETPFX)winkey.o : ../sys/amiga/winkey.c $(HACK_H) +$(TARGETPFX)winamenu.o : ../sys/amiga/winamenu.c $(HACK_H) +$(TARGETPFX)winreq.o : ../sys/amiga/winreq.c \ + ../sys/amiga/colorwin.c \ + ../sys/amiga/clipwin.c $(HACK_H) +$(TARGETPFX)winstr.o : ../sys/amiga/winstr.c $(HACK_H) +$(GAMEBIN) : $(HOBJ) $(LUACROSSLIB) + $(TARGET_LINK) $(TARGET_LFLAGS) -o $(GAMEBIN) \ + $(HOBJ) $(WINLIB) $(TARGET_LIBS) +# +# Host-side IFF tile conversion tools (run on Linux, produce Amiga IFF files) +# +AMISRC = ../sys/amiga + +$(TARGETPFX)xpm2iff_host: $(AMISRC)/xpm2iff_host.c + $(CC) $(CFLAGS) -o $@ $< +$(TARGETPFX)tomb.iff: $(AMISRC)/grave16.xpm $(TARGETPFX)xpm2iff_host + $(TARGETPFX)xpm2iff_host $(AMISRC)/grave16.xpm $@ + +$(TARGETPFX)bmp2iff_host: $(AMISRC)/bmp2iff_host.c + $(CC) $(CFLAGS) -o $@ $< +$(TARGETPFX)tiles16.iff: ../dat/nhtiles.bmp $(TARGETPFX)bmp2iff_host + $(TARGETPFX)bmp2iff_host -planes 4 ../dat/nhtiles.bmp $@ +$(TARGETPFX)tiles32.iff: ../dat/nhtiles.bmp $(TARGETPFX)bmp2iff_host + $(TARGETPFX)bmp2iff_host -planes 5 ../dat/nhtiles.bmp $@ + +AMITILES = $(TARGETPFX)tiles16.iff $(TARGETPFX)tiles32.iff $(TARGETPFX)tomb.iff + +.PHONY: amigapkg amitiles +amitiles: $(AMITILES) + +amigapkg: $(AMITILES) + mkdir -p $(TARGETPFX)pkg/tiles $(TARGETPFX)pkg/hack + cp $(GAMEBIN) $(TARGETPFX)pkg/nethack + cp ../dat/nhdat $(TARGETPFX)pkg/nhdat + cp ../dat/license $(TARGETPFX)pkg/license + cp ../dat/symbols $(TARGETPFX)pkg/symbols + cp $(TARGETPFX)tiles16.iff $(TARGETPFX)pkg/tiles/tiles16.iff + cp $(TARGETPFX)tiles32.iff $(TARGETPFX)pkg/tiles/tiles32.iff + cp $(TARGETPFX)tomb.iff $(TARGETPFX)pkg/tomb.iff + cp ../sys/msdos/sysconf $(TARGETPFX)pkg/sysconf + cp ../doc/nethack.txt $(TARGETPFX)pkg/nethack.txt + ( cd $(TARGETPFX)pkg && uudecode ../../../sys/amiga/amifont8.uu && mv 8 hack/8 ) + ( cd $(TARGETPFX)pkg && uudecode ../../../sys/amiga/amifont.uu ) + cp $(AMISRC)/nethack.cnf $(TARGETPFX)pkg/nethack.cnf + -( cd $(TARGETPFX)pkg && test -f ../../../sys/amiga/dflticon.uu && \ + uudecode ../../../sys/amiga/dflticon.uu ) + -( cd $(TARGETPFX)pkg && test -f ../../../sys/amiga/NHinfo.uu && \ + uudecode ../../../sys/amiga/NHinfo.uu ) + -( cd $(TARGETPFX)pkg && test -f ../../../sys/amiga/NewGame.uu && \ + uudecode ../../../sys/amiga/NewGame.uu ) + -( cd $(TARGETPFX)pkg && test -f ../../../sys/amiga/HackWB.uu && \ + uudecode ../../../sys/amiga/HackWB.uu ) + touch $(TARGETPFX)pkg/record + ( cd $(TARGETPFX)pkg && zip -9r ../NH370AMI.ZIP * ) + @echo amiga package zip file $(TARGETPFX)NH370AMI.ZIP +endif # CROSS_TO_AMIGA + ifdef CROSS_SHARED # shared file dependencies $(TARGETPFX)pcmain.o : ../sys/share/pcmain.c $(HACK_H) diff --git a/sys/unix/hints/include/cross-pre1.370 b/sys/unix/hints/include/cross-pre1.370 index 203c06048..867886d9b 100644 --- a/sys/unix/hints/include/cross-pre1.370 +++ b/sys/unix/hints/include/cross-pre1.370 @@ -38,6 +38,16 @@ override TARGETPFX = $(TARGETDIR)/ override TARGET_LIBS= endif +ifdef CROSS_TO_AMIGA +CROSS=1 +BUILD_TARGET_LUA=1 +CROSS_SHARED=1 +override TARGET = amiga +override TARGETDIR=../targets/$(TARGET) +override TARGETPFX = $(TARGETDIR)/ +override TARGET_LIBS= +endif + ifdef CROSS override PREGAME= override BUILDMORE= diff --git a/sys/unix/hints/include/cross-pre2.370 b/sys/unix/hints/include/cross-pre2.370 index d0cdf513a..eca47550f 100644 --- a/sys/unix/hints/include/cross-pre2.370 +++ b/sys/unix/hints/include/cross-pre2.370 @@ -460,6 +460,96 @@ NCURSES_PLATFORM=MIPS endif # CROSS_TO_MIPS #================================================================= +#================================================================= + +ifdef CROSS_TO_AMIGA +#===============-================================================= +# AmigaOS m68k cross-compile recipe +#===============-================================================= +# Uses an Amiga M68K cross-compiler on linux or macOS. +# +# Cross-compiler: https://franke.ms/git/bebbo/amiga-gcc +# Install to /opt/amiga, then: +# make fetch-lua +# sys/unix/setup.sh sys/unix/hints/linux.370 +# make CROSS_TO_AMIGA=1 all +# make CROSS_TO_AMIGA=1 package +#================================================================= + +CFLAGS += -DCROSSCOMPILE + +# +# Override the build tools and some obj files to +# reflect the amiga-gcc cross-compiler. +# +TOOLTOP = /opt/amiga/bin +TOOLARCH = -m68000 +override REGEXOBJ = $(TARGETPFX)posixregex.o +AMIREGEXOBJ = $(TARGETPFX)regcomp.o $(TARGETPFX)regexec.o \ + $(TARGETPFX)regerror.o $(TARGETPFX)regfree.o +override TARGET_CC = $(TOOLTOP)/m68k-amigaos-gcc +override TARGET_CXX = $(TOOLTOP)/m68k-amigaos-c++ +override TARGET_AR = $(TOOLTOP)/m68k-amigaos-ar +override TARGET_STUBEDIT= +override TARGET_CFLAGS = -c -O2 -noixemul $(TOOLARCH) \ + -include sys/types.h \ + -I../include \ + -I../sys/amiga -I../win/share \ + $(LUAINCL) -DAMIGA -DNOTTYGRAPHICS -DNO_TERMS -DNO_SIGNAL \ + -DTILES_IN_GLYPHMAP $(PDCURSESDEF) \ + -DCROSSCOMPILE -DCROSSCOMPILE_TARGET -DCROSS_TO_AMIGA \ + -DAMIGA_VERSION_STRING=\""VER: NetHack 3.7.0"\" +override TARGET_CXXFLAGS = $(TARGET_CFLAGS) +LUA_TARGET_CFLAGS = $(TARGET_CFLAGS) -DLUA_32BITS=1 +ifeq "$(REGEXOBJ)" "$(TARGETPFX)cppregex.o" +override TARGET_LINK = $(TARGET_CXX) +else +override TARGET_LINK = $(TARGET_CC) +endif +override TARGET_LFLAGS= $(TOOLARCH) -noixemul -Wl,--allow-multiple-definition +override TARGET_LIBS += $(LIBLM) +VARDATND += nhtiles.bmp +override SYSSRC = ../sys/amiga/amidos.c ../sys/amiga/amigst.c \ + ../sys/amiga/amimenu.c ../sys/amiga/amirip.c \ + ../sys/amiga/amistack.c ../sys/amiga/amitty.c \ + ../sys/amiga/amiwind.c ../sys/amiga/clipwin.c \ + ../sys/amiga/colorwin.c \ + ../sys/amiga/winami.c ../sys/amiga/winchar.c \ + ../sys/amiga/winfuncs.c ../sys/amiga/winkey.c \ + ../sys/amiga/winamenu.c ../sys/amiga/winreq.c \ + ../sys/amiga/winstr.c ../sys/share/pcmain.c \ + ../win/share/bmptiles.c ../win/share/giftiles.c \ + ../win/share/tileset.c +override SYSOBJ = $(TARGETPFX)amidos.o $(TARGETPFX)amigst.o \ + $(TARGETPFX)amirip.o $(TARGETPFX)amistack.o \ + $(TARGETPFX)amitty.o $(TARGETPFX)amiwind.o \ + $(TARGETPFX)winami.o $(TARGETPFX)winchar.o \ + $(TARGETPFX)winfuncs.o $(TARGETPFX)winkey.o \ + $(TARGETPFX)winamenu.o $(TARGETPFX)winreq.o \ + $(TARGETPFX)winstr.o $(TARGETPFX)pcmain.o \ + $(TARGETPFX)bmptiles.o $(TARGETPFX)giftiles.o \ + $(TARGETPFX)tileset.o \ + $(AMIREGEXOBJ) +override WINLIB= +override LUALIB= +override LUALIBS= +override TOPLUALIB= +override DLLIB= +override WINOBJ= +override GAMEBIN = $(TARGETPFX)nethack +override PACKAGE = amigapkg +override PREGAME += mkdir -p $(TARGETDIR) ; +override CLEANMORE += rm -r $(TARGETDIR) ; +# +# Rule for files in sys/amiga +$(TARGETPFX)%.o : ../sys/amiga/%.c + $(TARGET_CC) $(TARGET_CFLAGS) -o$@ $< +# Rule for BSD regex in sys/amiga/regex +$(TARGETPFX)%.o : ../sys/amiga/regex/%.c + $(TARGET_CC) $(TARGET_CFLAGS) -I../sys/amiga/regex -o$@ $< +endif # CROSS_TO_AMIGA +#================================================================= + ifdef WANT_WIN_CURSES ifdef BUILD_PDCURSES # Rules for PDCurses files From cee390482c17112fd4bab0796840fd89eea780ca Mon Sep 17 00:00:00 2001 From: Ingo Paschke Date: Mon, 23 Mar 2026 20:48:28 +0100 Subject: [PATCH 341/442] Add BSD POSIX regex for Amiga Add Henry Spencer's BSD regex implementation (from ixemul) to provide POSIX regular expressions for the Amiga port, enabling menu coloring and config file pattern matching. Copyright (c) 1992 Henry Spencer, The Regents of the University of California. BSD license. --- sys/amiga/regex/cclass.h | 72 ++ sys/amiga/regex/cname.h | 143 +++ sys/amiga/regex/engine.h | 1093 +++++++++++++++++++++++ sys/amiga/regex/regcomp.c | 1704 ++++++++++++++++++++++++++++++++++++ sys/amiga/regex/regerror.c | 186 ++++ sys/amiga/regex/regex2.h | 175 ++++ sys/amiga/regex/regexec.c | 187 ++++ sys/amiga/regex/regfree.c | 86 ++ sys/amiga/regex/utils.h | 59 ++ 9 files changed, 3705 insertions(+) create mode 100755 sys/amiga/regex/cclass.h create mode 100755 sys/amiga/regex/cname.h create mode 100755 sys/amiga/regex/engine.h create mode 100755 sys/amiga/regex/regcomp.c create mode 100755 sys/amiga/regex/regerror.c create mode 100755 sys/amiga/regex/regex2.h create mode 100755 sys/amiga/regex/regexec.c create mode 100755 sys/amiga/regex/regfree.c create mode 100755 sys/amiga/regex/utils.h diff --git a/sys/amiga/regex/cclass.h b/sys/amiga/regex/cclass.h new file mode 100755 index 000000000..52cd47b8f --- /dev/null +++ b/sys/amiga/regex/cclass.h @@ -0,0 +1,72 @@ +/* $NetBSD: cclass.h,v 1.3 1995/02/27 13:28:29 cgd Exp $ */ + +/*- + * Copyright (c) 1992, 1993, 1994 Henry Spencer. + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Henry Spencer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)cclass.h 8.3 (Berkeley) 3/20/94 + */ + +/* character-class table */ +static struct cclass { + char *name; + char *chars; + char *multis; +} cclasses[] = { + "alnum", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\ +0123456789", "", + "alpha", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", + "", + "blank", " \t", "", + "cntrl", "\007\b\t\n\v\f\r\1\2\3\4\5\6\16\17\20\21\22\23\24\ +\25\26\27\30\31\32\33\34\35\36\37\177", "", + "digit", "0123456789", "", + "graph", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\ +0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + "", + "lower", "abcdefghijklmnopqrstuvwxyz", + "", + "print", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\ +0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ ", + "", + "punct", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + "", + "space", "\t\n\v\f\r ", "", + "upper", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", + "", + "xdigit", "0123456789ABCDEFabcdef", + "", + NULL, 0, "" +}; diff --git a/sys/amiga/regex/cname.h b/sys/amiga/regex/cname.h new file mode 100755 index 000000000..50479db08 --- /dev/null +++ b/sys/amiga/regex/cname.h @@ -0,0 +1,143 @@ +/* $NetBSD: cname.h,v 1.3 1995/02/27 13:28:33 cgd Exp $ */ + +/*- + * Copyright (c) 1992, 1993, 1994 Henry Spencer. + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Henry Spencer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)cname.h 8.3 (Berkeley) 3/20/94 + */ + +/* character-name table */ +static struct cname { + char *name; + char code; +} cnames[] = { + "NUL", '\0', + "SOH", '\001', + "STX", '\002', + "ETX", '\003', + "EOT", '\004', + "ENQ", '\005', + "ACK", '\006', + "BEL", '\007', + "alert", '\007', + "BS", '\010', + "backspace", '\b', + "HT", '\011', + "tab", '\t', + "LF", '\012', + "newline", '\n', + "VT", '\013', + "vertical-tab", '\v', + "FF", '\014', + "form-feed", '\f', + "CR", '\015', + "carriage-return", '\r', + "SO", '\016', + "SI", '\017', + "DLE", '\020', + "DC1", '\021', + "DC2", '\022', + "DC3", '\023', + "DC4", '\024', + "NAK", '\025', + "SYN", '\026', + "ETB", '\027', + "CAN", '\030', + "EM", '\031', + "SUB", '\032', + "ESC", '\033', + "IS4", '\034', + "FS", '\034', + "IS3", '\035', + "GS", '\035', + "IS2", '\036', + "RS", '\036', + "IS1", '\037', + "US", '\037', + "space", ' ', + "exclamation-mark", '!', + "quotation-mark", '"', + "number-sign", '#', + "dollar-sign", '$', + "percent-sign", '%', + "ampersand", '&', + "apostrophe", '\'', + "left-parenthesis", '(', + "right-parenthesis", ')', + "asterisk", '*', + "plus-sign", '+', + "comma", ',', + "hyphen", '-', + "hyphen-minus", '-', + "period", '.', + "full-stop", '.', + "slash", '/', + "solidus", '/', + "zero", '0', + "one", '1', + "two", '2', + "three", '3', + "four", '4', + "five", '5', + "six", '6', + "seven", '7', + "eight", '8', + "nine", '9', + "colon", ':', + "semicolon", ';', + "less-than-sign", '<', + "equals-sign", '=', + "greater-than-sign", '>', + "question-mark", '?', + "commercial-at", '@', + "left-square-bracket", '[', + "backslash", '\\', + "reverse-solidus", '\\', + "right-square-bracket", ']', + "circumflex", '^', + "circumflex-accent", '^', + "underscore", '_', + "low-line", '_', + "grave-accent", '`', + "left-brace", '{', + "left-curly-bracket", '{', + "vertical-line", '|', + "right-brace", '}', + "right-curly-bracket", '}', + "tilde", '~', + "DEL", '\177', + NULL, 0, +}; diff --git a/sys/amiga/regex/engine.h b/sys/amiga/regex/engine.h new file mode 100755 index 000000000..93f6842ac --- /dev/null +++ b/sys/amiga/regex/engine.h @@ -0,0 +1,1093 @@ +/* $NetBSD: engine.c,v 1.5 1995/02/27 13:28:39 cgd Exp $ */ + +/*- + * Copyright (c) 1992, 1993, 1994 Henry Spencer. + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Henry Spencer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)engine.c 8.5 (Berkeley) 3/20/94 + */ + +/* + * The matching engine and friends. This file is #included by regexec.c + * after suitable #defines of a variety of macros used herein, so that + * different state representations can be used without duplicating masses + * of code. + */ + +#ifdef SNAMES +#define matcher smatcher +#define fast sfast +#define slow sslow +#define dissect sdissect +#define backref sbackref +#define step sstep +#define print sprint +#define at sat +#define match smat +#endif +#ifdef LNAMES +#define matcher lmatcher +#define fast lfast +#define slow lslow +#define dissect ldissect +#define backref lbackref +#define step lstep +#define print lprint +#define at lat +#define match lmat +#endif + +/* another structure passed up and down to avoid zillions of parameters */ +struct match { + struct re_guts *g; + int eflags; + regmatch_t *pmatch; /* [nsub+1] (0 element unused) */ + char *offp; /* offsets work from here */ + char *beginp; /* start of string -- virtual NUL precedes */ + char *endp; /* end of string -- virtual NUL here */ + char *coldp; /* can be no match starting before here */ + char **lastpos; /* [nplus+1] */ + STATEVARS; + states st; /* current states */ + states fresh; /* states for a fresh start */ + states tmp; /* temporary */ + states empty; /* empty set of states */ +}; + +/* ========= begin header generated by ./mkh ========= */ +#ifdef __cplusplus +extern "C" { +#endif + +/* === engine.c === */ +static int matcher __P((struct re_guts *g, char *string, size_t nmatch, regmatch_t pmatch[], int eflags)); +static char *dissect __P((struct match *m, char *start, char *stop, sopno startst, sopno stopst)); +static char *backref __P((struct match *m, char *start, char *stop, sopno startst, sopno stopst, sopno lev)); +static char *fast __P((struct match *m, char *start, char *stop, sopno startst, sopno stopst)); +static char *slow __P((struct match *m, char *start, char *stop, sopno startst, sopno stopst)); +static states step __P((struct re_guts *g, sopno start, sopno stop, states bef, int ch, states aft)); +#define BOL (OUT+1) +#define EOL (BOL+1) +#define BOLEOL (BOL+2) +#define NOTHING (BOL+3) +#define BOW (BOL+4) +#define EOW (BOL+5) +#define CODEMAX (BOL+5) /* highest code used */ +#define NONCHAR(c) ((c) > CHAR_MAX) +#define NNONCHAR (CODEMAX-CHAR_MAX) +#ifdef REDEBUG +static void print __P((struct match *m, char *caption, states st, int ch, FILE *d)); +#endif +#ifdef REDEBUG +static void at __P((struct match *m, char *title, char *start, char *stop, sopno startst, sopno stopst)); +#endif +#ifdef REDEBUG +static char *pchar __P((int ch)); +#endif + +#ifdef __cplusplus +} +#endif +/* ========= end header generated by ./mkh ========= */ + +#ifdef REDEBUG +#define SP(t, s, c) print(m, t, s, c, stdout) +#define AT(t, p1, p2, s1, s2) at(m, t, p1, p2, s1, s2) +#define NOTE(str) { if (m->eflags®_TRACE) printf("=%s\n", (str)); } +#else +#define SP(t, s, c) /* nothing */ +#define AT(t, p1, p2, s1, s2) /* nothing */ +#define NOTE(s) /* nothing */ +#endif + +/* + - matcher - the actual matching engine + == static int matcher(register struct re_guts *g, char *string, \ + == size_t nmatch, regmatch_t pmatch[], int eflags); + */ +static int /* 0 success, REG_NOMATCH failure */ +matcher(g, string, nmatch, pmatch, eflags) +register struct re_guts *g; +char *string; +size_t nmatch; +regmatch_t pmatch[]; +int eflags; +{ + register char *endp; + register int i; + struct match mv; + register struct match *m = &mv; + register char *dp; + const register sopno gf = g->firststate+1; /* +1 for OEND */ + const register sopno gl = g->laststate; + char *start; + char *stop; + + /* simplify the situation where possible */ + if (g->cflags®_NOSUB) + nmatch = 0; + if (eflags®_STARTEND) { + start = string + pmatch[0].rm_so; + stop = string + pmatch[0].rm_eo; + } else { + start = string; + stop = start + strlen(start); + } + if (stop < start) + return(REG_INVARG); + + /* prescreening; this does wonders for this rather slow code */ + if (g->must != NULL) { + for (dp = start; dp < stop; dp++) + if (*dp == g->must[0] && stop - dp >= g->mlen && + memcmp(dp, g->must, (size_t)g->mlen) == 0) + break; + if (dp == stop) /* we didn't find g->must */ + return(REG_NOMATCH); + } + + /* match struct setup */ + m->g = g; + m->eflags = eflags; + m->pmatch = NULL; + m->lastpos = NULL; + m->offp = string; + m->beginp = start; + m->endp = stop; + STATESETUP(m, 4); + SETUP(m->st); + SETUP(m->fresh); + SETUP(m->tmp); + SETUP(m->empty); + CLEAR(m->empty); + + /* this loop does only one repetition except for backrefs */ + for (;;) { + endp = fast(m, start, stop, gf, gl); + if (endp == NULL) { /* a miss */ + STATETEARDOWN(m); + return(REG_NOMATCH); + } + if (nmatch == 0 && !g->backrefs) + break; /* no further info needed */ + + /* where? */ + assert(m->coldp != NULL); + for (;;) { + NOTE("finding start"); + endp = slow(m, m->coldp, stop, gf, gl); + if (endp != NULL) + break; + assert(m->coldp < m->endp); + m->coldp++; + } + if (nmatch == 1 && !g->backrefs) + break; /* no further info needed */ + + /* oh my, he wants the subexpressions... */ + if (m->pmatch == NULL) + m->pmatch = (regmatch_t *)malloc((m->g->nsub + 1) * + sizeof(regmatch_t)); + if (m->pmatch == NULL) { + STATETEARDOWN(m); + return(REG_ESPACE); + } + for (i = 1; i <= m->g->nsub; i++) + m->pmatch[i].rm_so = m->pmatch[i].rm_eo = -1; + if (!g->backrefs && !(m->eflags®_BACKR)) { + NOTE("dissecting"); + dp = dissect(m, m->coldp, endp, gf, gl); + } else { + if (g->nplus > 0 && m->lastpos == NULL) + m->lastpos = (char **)malloc((g->nplus+1) * + sizeof(char *)); + if (g->nplus > 0 && m->lastpos == NULL) { + free(m->pmatch); + STATETEARDOWN(m); + return(REG_ESPACE); + } + NOTE("backref dissect"); + dp = backref(m, m->coldp, endp, gf, gl, (sopno)0); + } + if (dp != NULL) + break; + + /* uh-oh... we couldn't find a subexpression-level match */ + assert(g->backrefs); /* must be back references doing it */ + assert(g->nplus == 0 || m->lastpos != NULL); + for (;;) { + if (dp != NULL || endp <= m->coldp) + break; /* defeat */ + NOTE("backoff"); + endp = slow(m, m->coldp, endp-1, gf, gl); + if (endp == NULL) + break; /* defeat */ + /* try it on a shorter possibility */ +#ifndef NDEBUG + for (i = 1; i <= m->g->nsub; i++) { + assert(m->pmatch[i].rm_so == -1); + assert(m->pmatch[i].rm_eo == -1); + } +#endif + NOTE("backoff dissect"); + dp = backref(m, m->coldp, endp, gf, gl, (sopno)0); + } + assert(dp == NULL || dp == endp); + if (dp != NULL) /* found a shorter one */ + break; + + /* despite initial appearances, there is no match here */ + NOTE("false alarm"); + start = m->coldp + 1; /* recycle starting later */ + assert(start <= stop); + } + + /* fill in the details if requested */ + if (nmatch > 0) { + pmatch[0].rm_so = m->coldp - m->offp; + pmatch[0].rm_eo = endp - m->offp; + } + if (nmatch > 1) { + assert(m->pmatch != NULL); + for (i = 1; i < nmatch; i++) + if (i <= m->g->nsub) + pmatch[i] = m->pmatch[i]; + else { + pmatch[i].rm_so = -1; + pmatch[i].rm_eo = -1; + } + } + + if (m->pmatch != NULL) + free((char *)m->pmatch); + if (m->lastpos != NULL) + free((char *)m->lastpos); + STATETEARDOWN(m); + return(0); +} + +/* + - dissect - figure out what matched what, no back references + == static char *dissect(register struct match *m, char *start, \ + == char *stop, sopno startst, sopno stopst); + */ +static char * /* == stop (success) always */ +dissect(m, start, stop, startst, stopst) +register struct match *m; +char *start; +char *stop; +sopno startst; +sopno stopst; +{ + register int i; + register sopno ss; /* start sop of current subRE */ + register sopno es; /* end sop of current subRE */ + register char *sp; /* start of string matched by it */ + register char *stp; /* string matched by it cannot pass here */ + register char *rest; /* start of rest of string */ + register char *tail; /* string unmatched by rest of RE */ + register sopno ssub; /* start sop of subsubRE */ + register sopno esub; /* end sop of subsubRE */ + register char *ssp; /* start of string matched by subsubRE */ + register char *sep; /* end of string matched by subsubRE */ + register char *oldssp; /* previous ssp */ + register char *dp; + + AT("diss", start, stop, startst, stopst); + sp = start; + for (ss = startst; ss < stopst; ss = es) { + /* identify end of subRE */ + es = ss; + switch (OP(m->g->strip[es])) { + case OPLUS_: + case OQUEST_: + es += OPND(m->g->strip[es]); + break; + case OCH_: + while (OP(m->g->strip[es]) != O_CH) + es += OPND(m->g->strip[es]); + break; + } + es++; + + /* figure out what it matched */ + switch (OP(m->g->strip[ss])) { + case OEND: + assert(nope); + break; + case OCHAR: + sp++; + break; + case OBOL: + case OEOL: + case OBOW: + case OEOW: + break; + case OANY: + case OANYOF: + sp++; + break; + case OBACK_: + case O_BACK: + assert(nope); + break; + /* cases where length of match is hard to find */ + case OQUEST_: + stp = stop; + for (;;) { + /* how long could this one be? */ + rest = slow(m, sp, stp, ss, es); + assert(rest != NULL); /* it did match */ + /* could the rest match the rest? */ + tail = slow(m, rest, stop, es, stopst); + if (tail == stop) + break; /* yes! */ + /* no -- try a shorter match for this one */ + stp = rest - 1; + assert(stp >= sp); /* it did work */ + } + ssub = ss + 1; + esub = es - 1; + /* did innards match? */ + if (slow(m, sp, rest, ssub, esub) != NULL) { + dp = dissect(m, sp, rest, ssub, esub); + assert(dp == rest); + } else /* no */ + assert(sp == rest); + sp = rest; + break; + case OPLUS_: + stp = stop; + for (;;) { + /* how long could this one be? */ + rest = slow(m, sp, stp, ss, es); + assert(rest != NULL); /* it did match */ + /* could the rest match the rest? */ + tail = slow(m, rest, stop, es, stopst); + if (tail == stop) + break; /* yes! */ + /* no -- try a shorter match for this one */ + stp = rest - 1; + assert(stp >= sp); /* it did work */ + } + ssub = ss + 1; + esub = es - 1; + ssp = sp; + oldssp = ssp; + for (;;) { /* find last match of innards */ + sep = slow(m, ssp, rest, ssub, esub); + if (sep == NULL || sep == ssp) + break; /* failed or matched null */ + oldssp = ssp; /* on to next try */ + ssp = sep; + } + if (sep == NULL) { + /* last successful match */ + sep = ssp; + ssp = oldssp; + } + assert(sep == rest); /* must exhaust substring */ + assert(slow(m, ssp, sep, ssub, esub) == rest); + dp = dissect(m, ssp, sep, ssub, esub); + assert(dp == sep); + sp = rest; + break; + case OCH_: + stp = stop; + for (;;) { + /* how long could this one be? */ + rest = slow(m, sp, stp, ss, es); + assert(rest != NULL); /* it did match */ + /* could the rest match the rest? */ + tail = slow(m, rest, stop, es, stopst); + if (tail == stop) + break; /* yes! */ + /* no -- try a shorter match for this one */ + stp = rest - 1; + assert(stp >= sp); /* it did work */ + } + ssub = ss + 1; + esub = ss + OPND(m->g->strip[ss]) - 1; + assert(OP(m->g->strip[esub]) == OOR1); + for (;;) { /* find first matching branch */ + if (slow(m, sp, rest, ssub, esub) == rest) + break; /* it matched all of it */ + /* that one missed, try next one */ + assert(OP(m->g->strip[esub]) == OOR1); + esub++; + assert(OP(m->g->strip[esub]) == OOR2); + ssub = esub + 1; + esub += OPND(m->g->strip[esub]); + if (OP(m->g->strip[esub]) == OOR2) + esub--; + else + assert(OP(m->g->strip[esub]) == O_CH); + } + dp = dissect(m, sp, rest, ssub, esub); + assert(dp == rest); + sp = rest; + break; + case O_PLUS: + case O_QUEST: + case OOR1: + case OOR2: + case O_CH: + assert(nope); + break; + case OLPAREN: + i = OPND(m->g->strip[ss]); + assert(0 < i && i <= m->g->nsub); + m->pmatch[i].rm_so = sp - m->offp; + break; + case ORPAREN: + i = OPND(m->g->strip[ss]); + assert(0 < i && i <= m->g->nsub); + m->pmatch[i].rm_eo = sp - m->offp; + break; + default: /* uh oh */ + assert(nope); + break; + } + } + + assert(sp == stop); + return(sp); +} + +/* + - backref - figure out what matched what, figuring in back references + == static char *backref(register struct match *m, char *start, \ + == char *stop, sopno startst, sopno stopst, sopno lev); + */ +static char * /* == stop (success) or NULL (failure) */ +backref(m, start, stop, startst, stopst, lev) +register struct match *m; +char *start; +char *stop; +sopno startst; +sopno stopst; +sopno lev; /* PLUS nesting level */ +{ + register int i; + register sopno ss; /* start sop of current subRE */ + register char *sp; /* start of string matched by it */ + register sopno ssub; /* start sop of subsubRE */ + register sopno esub; /* end sop of subsubRE */ + register char *ssp; /* start of string matched by subsubRE */ + register char *dp; + register size_t len; + register int hard; + register sop s; + register regoff_t offsave; + register cset *cs; + + AT("back", start, stop, startst, stopst); + sp = start; + + /* get as far as we can with easy stuff */ + hard = 0; + for (ss = startst; !hard && ss < stopst; ss++) + switch (OP(s = m->g->strip[ss])) { + case OCHAR: + if (sp == stop || *sp++ != (char)OPND(s)) + return(NULL); + break; + case OANY: + if (sp == stop) + return(NULL); + sp++; + break; + case OANYOF: + cs = &m->g->sets[OPND(s)]; + if (sp == stop || !CHIN(cs, *sp++)) + return(NULL); + break; + case OBOL: + if ( (sp == m->beginp && !(m->eflags®_NOTBOL)) || + (sp < m->endp && *(sp-1) == '\n' && + (m->g->cflags®_NEWLINE)) ) + { /* yes */ } + else + return(NULL); + break; + case OEOL: + if ( (sp == m->endp && !(m->eflags®_NOTEOL)) || + (sp < m->endp && *sp == '\n' && + (m->g->cflags®_NEWLINE)) ) + { /* yes */ } + else + return(NULL); + break; + case OBOW: + if (( (sp == m->beginp && !(m->eflags®_NOTBOL)) || + (sp < m->endp && *(sp-1) == '\n' && + (m->g->cflags®_NEWLINE)) || + (sp > m->beginp && + !ISWORD(*(sp-1))) ) && + (sp < m->endp && ISWORD(*sp)) ) + { /* yes */ } + else + return(NULL); + break; + case OEOW: + if (( (sp == m->endp && !(m->eflags®_NOTEOL)) || + (sp < m->endp && *sp == '\n' && + (m->g->cflags®_NEWLINE)) || + (sp < m->endp && !ISWORD(*sp)) ) && + (sp > m->beginp && ISWORD(*(sp-1))) ) + { /* yes */ } + else + return(NULL); + break; + case O_QUEST: + break; + case OOR1: /* matches null but needs to skip */ + ss++; + s = m->g->strip[ss]; + do { + assert(OP(s) == OOR2); + ss += OPND(s); + } while (OP(s = m->g->strip[ss]) != O_CH); + /* note that the ss++ gets us past the O_CH */ + break; + default: /* have to make a choice */ + hard = 1; + break; + } + if (!hard) { /* that was it! */ + if (sp != stop) + return(NULL); + return(sp); + } + ss--; /* adjust for the for's final increment */ + + /* the hard stuff */ + AT("hard", sp, stop, ss, stopst); + s = m->g->strip[ss]; + switch (OP(s)) { + case OBACK_: /* the vilest depths */ + i = OPND(s); + assert(0 < i && i <= m->g->nsub); + if (m->pmatch[i].rm_eo == -1) + return(NULL); + assert(m->pmatch[i].rm_so != -1); + len = m->pmatch[i].rm_eo - m->pmatch[i].rm_so; + assert(stop - m->beginp >= len); + if (sp > stop - len) + return(NULL); /* not enough left to match */ + ssp = m->offp + m->pmatch[i].rm_so; + if (memcmp(sp, ssp, len) != 0) + return(NULL); + while (m->g->strip[ss] != SOP(O_BACK, i)) + ss++; + return(backref(m, sp+len, stop, ss+1, stopst, lev)); + break; + case OQUEST_: /* to null or not */ + dp = backref(m, sp, stop, ss+1, stopst, lev); + if (dp != NULL) + return(dp); /* not */ + return(backref(m, sp, stop, ss+OPND(s)+1, stopst, lev)); + break; + case OPLUS_: + assert(m->lastpos != NULL); + assert(lev+1 <= m->g->nplus); + m->lastpos[lev+1] = sp; + return(backref(m, sp, stop, ss+1, stopst, lev+1)); + break; + case O_PLUS: + if (sp == m->lastpos[lev]) /* last pass matched null */ + return(backref(m, sp, stop, ss+1, stopst, lev-1)); + /* try another pass */ + m->lastpos[lev] = sp; + dp = backref(m, sp, stop, ss-OPND(s)+1, stopst, lev); + if (dp == NULL) + return(backref(m, sp, stop, ss+1, stopst, lev-1)); + else + return(dp); + break; + case OCH_: /* find the right one, if any */ + ssub = ss + 1; + esub = ss + OPND(s) - 1; + assert(OP(m->g->strip[esub]) == OOR1); + for (;;) { /* find first matching branch */ + dp = backref(m, sp, stop, ssub, esub, lev); + if (dp != NULL) + return(dp); + /* that one missed, try next one */ + if (OP(m->g->strip[esub]) == O_CH) + return(NULL); /* there is none */ + esub++; + assert(OP(m->g->strip[esub]) == OOR2); + ssub = esub + 1; + esub += OPND(m->g->strip[esub]); + if (OP(m->g->strip[esub]) == OOR2) + esub--; + else + assert(OP(m->g->strip[esub]) == O_CH); + } + break; + case OLPAREN: /* must undo assignment if rest fails */ + i = OPND(s); + assert(0 < i && i <= m->g->nsub); + offsave = m->pmatch[i].rm_so; + m->pmatch[i].rm_so = sp - m->offp; + dp = backref(m, sp, stop, ss+1, stopst, lev); + if (dp != NULL) + return(dp); + m->pmatch[i].rm_so = offsave; + return(NULL); + break; + case ORPAREN: /* must undo assignment if rest fails */ + i = OPND(s); + assert(0 < i && i <= m->g->nsub); + offsave = m->pmatch[i].rm_eo; + m->pmatch[i].rm_eo = sp - m->offp; + dp = backref(m, sp, stop, ss+1, stopst, lev); + if (dp != NULL) + return(dp); + m->pmatch[i].rm_eo = offsave; + return(NULL); + break; + default: /* uh oh */ + assert(nope); + break; + } + + /* "can't happen" */ + assert(nope); + /* NOTREACHED */ +} + +/* + - fast - step through the string at top speed + == static char *fast(register struct match *m, char *start, \ + == char *stop, sopno startst, sopno stopst); + */ +static char * /* where tentative match ended, or NULL */ +fast(m, start, stop, startst, stopst) +register struct match *m; +char *start; +char *stop; +sopno startst; +sopno stopst; +{ + register states st = m->st; + register states fresh = m->fresh; + register states tmp = m->tmp; + register char *p = start; + register int c = (start == m->beginp) ? OUT : *(start-1); + register int lastc; /* previous c */ + register int flagch; + register int i; + register char *coldp; /* last p after which no match was underway */ + + CLEAR(st); + SET1(st, startst); + st = step(m->g, startst, stopst, st, NOTHING, st); + ASSIGN(fresh, st); + SP("start", st, *p); + coldp = NULL; + for (;;) { + /* next character */ + lastc = c; + c = (p == m->endp) ? OUT : *p; + if (EQ(st, fresh)) + coldp = p; + + /* is there an EOL and/or BOL between lastc and c? */ + flagch = '\0'; + i = 0; + if ( (lastc == '\n' && m->g->cflags®_NEWLINE) || + (lastc == OUT && !(m->eflags®_NOTBOL)) ) { + flagch = BOL; + i = m->g->nbol; + } + if ( (c == '\n' && m->g->cflags®_NEWLINE) || + (c == OUT && !(m->eflags®_NOTEOL)) ) { + flagch = (flagch == BOL) ? BOLEOL : EOL; + i += m->g->neol; + } + if (i != 0) { + for (; i > 0; i--) + st = step(m->g, startst, stopst, st, flagch, st); + SP("boleol", st, c); + } + + /* how about a word boundary? */ + if ( (flagch == BOL || (lastc != OUT && !ISWORD(lastc))) && + (c != OUT && ISWORD(c)) ) { + flagch = BOW; + } + if ( (lastc != OUT && ISWORD(lastc)) && + (flagch == EOL || (c != OUT && !ISWORD(c))) ) { + flagch = EOW; + } + if (flagch == BOW || flagch == EOW) { + st = step(m->g, startst, stopst, st, flagch, st); + SP("boweow", st, c); + } + + /* are we done? */ + if (ISSET(st, stopst) || p == stop) + break; /* NOTE BREAK OUT */ + + /* no, we must deal with this character */ + ASSIGN(tmp, st); + ASSIGN(st, fresh); + assert(c != OUT); + st = step(m->g, startst, stopst, tmp, c, st); + SP("aft", st, c); + assert(EQ(step(m->g, startst, stopst, st, NOTHING, st), st)); + p++; + } + + assert(coldp != NULL); + m->coldp = coldp; + if (ISSET(st, stopst)) + return(p+1); + else + return(NULL); +} + +/* + - slow - step through the string more deliberately + == static char *slow(register struct match *m, char *start, \ + == char *stop, sopno startst, sopno stopst); + */ +static char * /* where it ended */ +slow(m, start, stop, startst, stopst) +register struct match *m; +char *start; +char *stop; +sopno startst; +sopno stopst; +{ + register states st = m->st; + register states empty = m->empty; + register states tmp = m->tmp; + register char *p = start; + register int c = (start == m->beginp) ? OUT : *(start-1); + register int lastc; /* previous c */ + register int flagch; + register int i; + register char *matchp; /* last p at which a match ended */ + + AT("slow", start, stop, startst, stopst); + CLEAR(st); + SET1(st, startst); + SP("sstart", st, *p); + st = step(m->g, startst, stopst, st, NOTHING, st); + matchp = NULL; + for (;;) { + /* next character */ + lastc = c; + c = (p == m->endp) ? OUT : *p; + + /* is there an EOL and/or BOL between lastc and c? */ + flagch = '\0'; + i = 0; + if ( (lastc == '\n' && m->g->cflags®_NEWLINE) || + (lastc == OUT && !(m->eflags®_NOTBOL)) ) { + flagch = BOL; + i = m->g->nbol; + } + if ( (c == '\n' && m->g->cflags®_NEWLINE) || + (c == OUT && !(m->eflags®_NOTEOL)) ) { + flagch = (flagch == BOL) ? BOLEOL : EOL; + i += m->g->neol; + } + if (i != 0) { + for (; i > 0; i--) + st = step(m->g, startst, stopst, st, flagch, st); + SP("sboleol", st, c); + } + + /* how about a word boundary? */ + if ( (flagch == BOL || (lastc != OUT && !ISWORD(lastc))) && + (c != OUT && ISWORD(c)) ) { + flagch = BOW; + } + if ( (lastc != OUT && ISWORD(lastc)) && + (flagch == EOL || (c != OUT && !ISWORD(c))) ) { + flagch = EOW; + } + if (flagch == BOW || flagch == EOW) { + st = step(m->g, startst, stopst, st, flagch, st); + SP("sboweow", st, c); + } + + /* are we done? */ + if (ISSET(st, stopst)) + matchp = p; + if (EQ(st, empty) || p == stop) + break; /* NOTE BREAK OUT */ + + /* no, we must deal with this character */ + ASSIGN(tmp, st); + ASSIGN(st, empty); + assert(c != OUT); + st = step(m->g, startst, stopst, tmp, c, st); + SP("saft", st, c); + assert(EQ(step(m->g, startst, stopst, st, NOTHING, st), st)); + p++; + } + + return(matchp); +} + + +/* + - step - map set of states reachable before char to set reachable after + == static states step(register struct re_guts *g, sopno start, sopno stop, \ + == register states bef, int ch, register states aft); + == #define BOL (OUT+1) + == #define EOL (BOL+1) + == #define BOLEOL (BOL+2) + == #define NOTHING (BOL+3) + == #define BOW (BOL+4) + == #define EOW (BOL+5) + == #define CODEMAX (BOL+5) // highest code used + == #define NONCHAR(c) ((c) > CHAR_MAX) + == #define NNONCHAR (CODEMAX-CHAR_MAX) + */ +static states +step(g, start, stop, bef, ch, aft) +register struct re_guts *g; +sopno start; /* start state within strip */ +sopno stop; /* state after stop state within strip */ +register states bef; /* states reachable before */ +int ch; /* character or NONCHAR code */ +register states aft; /* states already known reachable after */ +{ + register cset *cs; + register sop s; + register sopno pc; + register onestate here; /* note, macros know this name */ + register sopno look; + register int i; + + for (pc = start, INIT(here, pc); pc != stop; pc++, INC(here)) { + s = g->strip[pc]; + switch (OP(s)) { + case OEND: + assert(pc == stop-1); + break; + case OCHAR: + /* only characters can match */ + assert(!NONCHAR(ch) || ch != (char)OPND(s)); + if (ch == (char)OPND(s)) + FWD(aft, bef, 1); + break; + case OBOL: + if (ch == BOL || ch == BOLEOL) + FWD(aft, bef, 1); + break; + case OEOL: + if (ch == EOL || ch == BOLEOL) + FWD(aft, bef, 1); + break; + case OBOW: + if (ch == BOW) + FWD(aft, bef, 1); + break; + case OEOW: + if (ch == EOW) + FWD(aft, bef, 1); + break; + case OANY: + if (!NONCHAR(ch)) + FWD(aft, bef, 1); + break; + case OANYOF: + cs = &g->sets[OPND(s)]; + if (!NONCHAR(ch) && CHIN(cs, ch)) + FWD(aft, bef, 1); + break; + case OBACK_: /* ignored here */ + case O_BACK: + FWD(aft, aft, 1); + break; + case OPLUS_: /* forward, this is just an empty */ + FWD(aft, aft, 1); + break; + case O_PLUS: /* both forward and back */ + FWD(aft, aft, 1); + i = ISSETBACK(aft, OPND(s)); + BACK(aft, aft, OPND(s)); + if (!i && ISSETBACK(aft, OPND(s))) { + /* oho, must reconsider loop body */ + pc -= OPND(s) + 1; + INIT(here, pc); + } + break; + case OQUEST_: /* two branches, both forward */ + FWD(aft, aft, 1); + FWD(aft, aft, OPND(s)); + break; + case O_QUEST: /* just an empty */ + FWD(aft, aft, 1); + break; + case OLPAREN: /* not significant here */ + case ORPAREN: + FWD(aft, aft, 1); + break; + case OCH_: /* mark the first two branches */ + FWD(aft, aft, 1); + assert(OP(g->strip[pc+OPND(s)]) == OOR2); + FWD(aft, aft, OPND(s)); + break; + case OOR1: /* done a branch, find the O_CH */ + if (ISSTATEIN(aft, here)) { + for (look = 1; + OP(s = g->strip[pc+look]) != O_CH; + look += OPND(s)) + assert(OP(s) == OOR2); + FWD(aft, aft, look); + } + break; + case OOR2: /* propagate OCH_'s marking */ + FWD(aft, aft, 1); + if (OP(g->strip[pc+OPND(s)]) != O_CH) { + assert(OP(g->strip[pc+OPND(s)]) == OOR2); + FWD(aft, aft, OPND(s)); + } + break; + case O_CH: /* just empty */ + FWD(aft, aft, 1); + break; + default: /* ooooops... */ + assert(nope); + break; + } + } + + return(aft); +} + +#ifdef REDEBUG +/* + - print - print a set of states + == #ifdef REDEBUG + == static void print(struct match *m, char *caption, states st, \ + == int ch, FILE *d); + == #endif + */ +static void +print(m, caption, st, ch, d) +struct match *m; +char *caption; +states st; +int ch; +FILE *d; +{ + register struct re_guts *g = m->g; + register int i; + register int first = 1; + + if (!(m->eflags®_TRACE)) + return; + + fprintf(d, "%s", caption); + if (ch != '\0') + fprintf(d, " %s", pchar(ch)); + for (i = 0; i < g->nstates; i++) + if (ISSET(st, i)) { + fprintf(d, "%s%d", (first) ? "\t" : ", ", i); + first = 0; + } + fprintf(d, "\n"); +} + +/* + - at - print current situation + == #ifdef REDEBUG + == static void at(struct match *m, char *title, char *start, char *stop, \ + == sopno startst, sopno stopst); + == #endif + */ +static void +at(m, title, start, stop, startst, stopst) +struct match *m; +char *title; +char *start; +char *stop; +sopno startst; +sopno stopst; +{ + if (!(m->eflags®_TRACE)) + return; + + printf("%s %s-", title, pchar(*start)); + printf("%s ", pchar(*stop)); + printf("%ld-%ld\n", (long)startst, (long)stopst); +} + +#ifndef PCHARDONE +#define PCHARDONE /* never again */ +/* + - pchar - make a character printable + == #ifdef REDEBUG + == static char *pchar(int ch); + == #endif + * + * Is this identical to regchar() over in debug.c? Well, yes. But a + * duplicate here avoids having a debugging-capable regexec.o tied to + * a matching debug.o, and this is convenient. It all disappears in + * the non-debug compilation anyway, so it doesn't matter much. + */ +static char * /* -> representation */ +pchar(ch) +int ch; +{ + static char pbuf[10]; + + if (isprint(ch) || ch == ' ') + sprintf(pbuf, "%c", ch); + else + sprintf(pbuf, "\\%o", ch); + return(pbuf); +} +#endif +#endif + +#undef matcher +#undef fast +#undef slow +#undef dissect +#undef backref +#undef step +#undef print +#undef at +#undef match diff --git a/sys/amiga/regex/regcomp.c b/sys/amiga/regex/regcomp.c new file mode 100755 index 000000000..9b64aa4b2 --- /dev/null +++ b/sys/amiga/regex/regcomp.c @@ -0,0 +1,1704 @@ +/* $NetBSD: regcomp.c,v 1.6 1995/02/27 13:29:01 cgd Exp $ */ + +/*- + * Copyright (c) 1992, 1993, 1994 Henry Spencer. + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Henry Spencer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)regcomp.c 8.5 (Berkeley) 3/20/94 + */ + +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)regcomp.c 8.5 (Berkeley) 3/20/94"; +#else +static char rcsid[] = "$NetBSD: regcomp.c,v 1.6 1995/02/27 13:29:01 cgd Exp $"; +#endif +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "regex2.h" + +#include "cclass.h" +#include "cname.h" + +/* + * parse structure, passed up and down to avoid global variables and + * other clumsinesses + */ +struct parse { + char *next; /* next character in RE */ + char *end; /* end of string (-> NUL normally) */ + int error; /* has an error been seen? */ + sop *strip; /* malloced strip */ + sopno ssize; /* malloced strip size (allocated) */ + sopno slen; /* malloced strip length (used) */ + int ncsalloc; /* number of csets allocated */ + struct re_guts *g; +# define NPAREN 10 /* we need to remember () 1-9 for back refs */ + sopno pbegin[NPAREN]; /* -> ( ([0] unused) */ + sopno pend[NPAREN]; /* -> ) ([0] unused) */ +}; + +/* ========= begin header generated by ./mkh ========= */ +#ifdef __cplusplus +extern "C" { +#endif + +/* === regcomp.c === */ +static void p_ere __P((struct parse *p, int stop)); +static void p_ere_exp __P((struct parse *p)); +static void p_str __P((struct parse *p)); +static void p_bre __P((struct parse *p, int end1, int end2)); +static int p_simp_re __P((struct parse *p, int starordinary)); +static int p_count __P((struct parse *p)); +static void p_bracket __P((struct parse *p)); +static void p_b_term __P((struct parse *p, cset *cs)); +static void p_b_cclass __P((struct parse *p, cset *cs)); +static void p_b_eclass __P((struct parse *p, cset *cs)); +static char p_b_symbol __P((struct parse *p)); +static char p_b_coll_elem __P((struct parse *p, int endc)); +static char othercase __P((int ch)); +static void bothcases __P((struct parse *p, int ch)); +static void ordinary __P((struct parse *p, int ch)); +static void nonnewline __P((struct parse *p)); +static void repeat __P((struct parse *p, sopno start, int from, int to)); +static int seterr __P((struct parse *p, int e)); +static cset *allocset __P((struct parse *p)); +static void freeset __P((struct parse *p, cset *cs)); +static int freezeset __P((struct parse *p, cset *cs)); +static int firstch __P((struct parse *p, cset *cs)); +static int nch __P((struct parse *p, cset *cs)); +static void mcadd __P((struct parse *p, cset *cs, char *cp)); +static void mcsub __P((cset *cs, char *cp)); +static int mcin __P((cset *cs, char *cp)); +static char *mcfind __P((cset *cs, char *cp)); +static void mcinvert __P((struct parse *p, cset *cs)); +static void mccase __P((struct parse *p, cset *cs)); +static int isinsets __P((struct re_guts *g, int c)); +static int samesets __P((struct re_guts *g, int c1, int c2)); +static void categorize __P((struct parse *p, struct re_guts *g)); +static sopno dupl __P((struct parse *p, sopno start, sopno finish)); +static void doemit __P((struct parse *p, sop op, size_t opnd)); +static void doinsert __P((struct parse *p, sop op, size_t opnd, sopno pos)); +static void dofwd __P((struct parse *p, sopno pos, sop value)); +static void enlarge __P((struct parse *p, sopno size)); +static void stripsnug __P((struct parse *p, struct re_guts *g)); +static void findmust __P((struct parse *p, struct re_guts *g)); +static sopno pluscount __P((struct parse *p, struct re_guts *g)); + +#ifdef __cplusplus +} +#endif +/* ========= end header generated by ./mkh ========= */ + +static char nuls[10]; /* place to point scanner in event of error */ + +/* + * macros for use with parse structure + * BEWARE: these know that the parse structure is named `p' !!! + */ +#define PEEK() (*p->next) +#define PEEK2() (*(p->next+1)) +#define MORE() (p->next < p->end) +#define MORE2() (p->next+1 < p->end) +#define SEE(c) (MORE() && PEEK() == (c)) +#define SEETWO(a, b) (MORE() && MORE2() && PEEK() == (a) && PEEK2() == (b)) +#define EAT(c) ((SEE(c)) ? (NEXT(), 1) : 0) +#define EATTWO(a, b) ((SEETWO(a, b)) ? (NEXT2(), 1) : 0) +#define NEXT() (p->next++) +#define NEXT2() (p->next += 2) +#define NEXTn(n) (p->next += (n)) +#define GETNEXT() (*p->next++) +#define SETERROR(e) seterr(p, (e)) +#define REQUIRE(co, e) ((co) || SETERROR(e)) +#define MUSTSEE(c, e) (REQUIRE(MORE() && PEEK() == (c), e)) +#define MUSTEAT(c, e) (REQUIRE(MORE() && GETNEXT() == (c), e)) +#define MUSTNOTSEE(c, e) (REQUIRE(!MORE() || PEEK() != (c), e)) +#define EMIT(op, sopnd) doemit(p, (sop)(op), (size_t)(sopnd)) +#define INSERT(op, pos) doinsert(p, (sop)(op), HERE()-(pos)+1, pos) +#define AHEAD(pos) dofwd(p, pos, HERE()-(pos)) +#define ASTERN(sop, pos) EMIT(sop, HERE()-pos) +#define HERE() (p->slen) +#define THERE() (p->slen - 1) +#define THERETHERE() (p->slen - 2) +#define DROP(n) (p->slen -= (n)) + +#ifndef NDEBUG +static int never = 0; /* for use in asserts; shuts lint up */ +#else +#define never 0 /* some s have bugs too */ +#endif + +/* + - regcomp - interface for parser and compilation + = extern int regcomp(regex_t *, const char *, int); + = #define REG_BASIC 0000 + = #define REG_EXTENDED 0001 + = #define REG_ICASE 0002 + = #define REG_NOSUB 0004 + = #define REG_NEWLINE 0010 + = #define REG_NOSPEC 0020 + = #define REG_PEND 0040 + = #define REG_DUMP 0200 + */ +int /* 0 success, otherwise REG_something */ +regcomp(preg, pattern, cflags) +regex_t *preg; +const char *pattern; +int cflags; +{ + struct parse pa; + register struct re_guts *g; + register struct parse *p = &pa; + register int i; + register size_t len; +#ifdef REDEBUG +# define GOODFLAGS(f) (f) +#else +# define GOODFLAGS(f) ((f)&~REG_DUMP) +#endif + + cflags = GOODFLAGS(cflags); + if ((cflags®_EXTENDED) && (cflags®_NOSPEC)) + return(REG_INVARG); + + if (cflags®_PEND) { + if (preg->re_endp < pattern) + return(REG_INVARG); + len = preg->re_endp - pattern; + } else + len = strlen((char *)pattern); + + /* do the mallocs early so failure handling is easy */ + g = (struct re_guts *)malloc(sizeof(struct re_guts) + + (NC-1)*sizeof(cat_t)); + if (g == NULL) + return(REG_ESPACE); + p->ssize = len/(size_t)2*(size_t)3 + (size_t)1; /* ugh */ + p->strip = (sop *)malloc(p->ssize * sizeof(sop)); + p->slen = 0; + if (p->strip == NULL) { + free((char *)g); + return(REG_ESPACE); + } + + /* set things up */ + p->g = g; + p->next = (char *)pattern; /* convenience; we do not modify it */ + p->end = p->next + len; + p->error = 0; + p->ncsalloc = 0; + for (i = 0; i < NPAREN; i++) { + p->pbegin[i] = 0; + p->pend[i] = 0; + } + g->csetsize = NC; + g->sets = NULL; + g->setbits = NULL; + g->ncsets = 0; + g->cflags = cflags; + g->iflags = 0; + g->nbol = 0; + g->neol = 0; + g->must = NULL; + g->mlen = 0; + g->nsub = 0; + g->ncategories = 1; /* category 0 is "everything else" */ + g->categories = &g->catspace[-(CHAR_MIN)]; + (void) memset((char *)g->catspace, 0, NC*sizeof(cat_t)); + g->backrefs = 0; + + /* do it */ + EMIT(OEND, 0); + g->firststate = THERE(); + if (cflags®_EXTENDED) + p_ere(p, OUT); + else if (cflags®_NOSPEC) + p_str(p); + else + p_bre(p, OUT, OUT); + EMIT(OEND, 0); + g->laststate = THERE(); + + /* tidy up loose ends and fill things in */ + categorize(p, g); + stripsnug(p, g); + findmust(p, g); + g->nplus = pluscount(p, g); + g->magic = MAGIC2; + preg->re_nsub = g->nsub; + preg->re_g = g; + preg->re_magic = MAGIC1; +#ifndef REDEBUG + /* not debugging, so can't rely on the assert() in regexec() */ + if (g->iflags&BAD) + SETERROR(REG_ASSERT); +#endif + + /* win or lose, we're done */ + if (p->error != 0) /* lose */ + regfree(preg); + return(p->error); +} + +/* + - p_ere - ERE parser top level, concatenation and alternation + == static void p_ere(register struct parse *p, int stop); + */ +static void +p_ere(p, stop) +register struct parse *p; +int stop; /* character this ERE should end at */ +{ + register char c; + register sopno prevback; + register sopno prevfwd; + register sopno conc; + register int first = 1; /* is this the first alternative? */ + + for (;;) { + /* do a bunch of concatenated expressions */ + conc = HERE(); + while (MORE() && (c = PEEK()) != '|' && c != stop) + p_ere_exp(p); + REQUIRE(HERE() != conc, REG_EMPTY); /* require nonempty */ + + if (!EAT('|')) + break; /* NOTE BREAK OUT */ + + if (first) { + INSERT(OCH_, conc); /* offset is wrong */ + prevfwd = conc; + prevback = conc; + first = 0; + } + ASTERN(OOR1, prevback); + prevback = THERE(); + AHEAD(prevfwd); /* fix previous offset */ + prevfwd = HERE(); + EMIT(OOR2, 0); /* offset is very wrong */ + } + + if (!first) { /* tail-end fixups */ + AHEAD(prevfwd); + ASTERN(O_CH, prevback); + } + + assert(!MORE() || SEE(stop)); +} + +/* + - p_ere_exp - parse one subERE, an atom possibly followed by a repetition op + == static void p_ere_exp(register struct parse *p); + */ +static void +p_ere_exp(p) +register struct parse *p; +{ + register char c; + register sopno pos; + register int count; + register int count2; + register sopno subno; + int wascaret = 0; + + assert(MORE()); /* caller should have ensured this */ + c = GETNEXT(); + + pos = HERE(); + switch (c) { + case '(': + REQUIRE(MORE(), REG_EPAREN); + p->g->nsub++; + subno = p->g->nsub; + if (subno < NPAREN) + p->pbegin[subno] = HERE(); + EMIT(OLPAREN, subno); + if (!SEE(')')) + p_ere(p, ')'); + if (subno < NPAREN) { + p->pend[subno] = HERE(); + assert(p->pend[subno] != 0); + } + EMIT(ORPAREN, subno); + MUSTEAT(')', REG_EPAREN); + break; +#ifndef POSIX_MISTAKE + case ')': /* happens only if no current unmatched ( */ + /* + * You may ask, why the ifndef? Because I didn't notice + * this until slightly too late for 1003.2, and none of the + * other 1003.2 regular-expression reviewers noticed it at + * all. So an unmatched ) is legal POSIX, at least until + * we can get it fixed. + */ + SETERROR(REG_EPAREN); + break; +#endif + case '^': + EMIT(OBOL, 0); + p->g->iflags |= USEBOL; + p->g->nbol++; + wascaret = 1; + break; + case '$': + EMIT(OEOL, 0); + p->g->iflags |= USEEOL; + p->g->neol++; + break; + case '|': + SETERROR(REG_EMPTY); + break; + case '*': + case '+': + case '?': + SETERROR(REG_BADRPT); + break; + case '.': + if (p->g->cflags®_NEWLINE) + nonnewline(p); + else + EMIT(OANY, 0); + break; + case '[': + p_bracket(p); + break; + case '\\': + REQUIRE(MORE(), REG_EESCAPE); + c = GETNEXT(); + ordinary(p, c); + break; + case '{': /* okay as ordinary except if digit follows */ + REQUIRE(!MORE() || !isdigit(PEEK()), REG_BADRPT); + /* FALLTHROUGH */ + default: + ordinary(p, c); + break; + } + + if (!MORE()) + return; + c = PEEK(); + /* we call { a repetition if followed by a digit */ + if (!( c == '*' || c == '+' || c == '?' || + (c == '{' && MORE2() && isdigit(PEEK2())) )) + return; /* no repetition, we're done */ + NEXT(); + + REQUIRE(!wascaret, REG_BADRPT); + switch (c) { + case '*': /* implemented as +? */ + /* this case does not require the (y|) trick, noKLUDGE */ + INSERT(OPLUS_, pos); + ASTERN(O_PLUS, pos); + INSERT(OQUEST_, pos); + ASTERN(O_QUEST, pos); + break; + case '+': + INSERT(OPLUS_, pos); + ASTERN(O_PLUS, pos); + break; + case '?': + /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */ + INSERT(OCH_, pos); /* offset slightly wrong */ + ASTERN(OOR1, pos); /* this one's right */ + AHEAD(pos); /* fix the OCH_ */ + EMIT(OOR2, 0); /* offset very wrong... */ + AHEAD(THERE()); /* ...so fix it */ + ASTERN(O_CH, THERETHERE()); + break; + case '{': + count = p_count(p); + if (EAT(',')) { + if (isdigit(PEEK())) { + count2 = p_count(p); + REQUIRE(count <= count2, REG_BADBR); + } else /* single number with comma */ + count2 = INFINITY; + } else /* just a single number */ + count2 = count; + repeat(p, pos, count, count2); + if (!EAT('}')) { /* error heuristics */ + while (MORE() && PEEK() != '}') + NEXT(); + REQUIRE(MORE(), REG_EBRACE); + SETERROR(REG_BADBR); + } + break; + } + + if (!MORE()) + return; + c = PEEK(); + if (!( c == '*' || c == '+' || c == '?' || + (c == '{' && MORE2() && isdigit(PEEK2())) ) ) + return; + SETERROR(REG_BADRPT); +} + +/* + - p_str - string (no metacharacters) "parser" + == static void p_str(register struct parse *p); + */ +static void +p_str(p) +register struct parse *p; +{ + REQUIRE(MORE(), REG_EMPTY); + while (MORE()) + ordinary(p, GETNEXT()); +} + +/* + - p_bre - BRE parser top level, anchoring and concatenation + == static void p_bre(register struct parse *p, register int end1, \ + == register int end2); + * Giving end1 as OUT essentially eliminates the end1/end2 check. + * + * This implementation is a bit of a kludge, in that a trailing $ is first + * taken as an ordinary character and then revised to be an anchor. The + * only undesirable side effect is that '$' gets included as a character + * category in such cases. This is fairly harmless; not worth fixing. + * The amount of lookahead needed to avoid this kludge is excessive. + */ +static void +p_bre(p, end1, end2) +register struct parse *p; +register int end1; /* first terminating character */ +register int end2; /* second terminating character */ +{ + register sopno start = HERE(); + register int first = 1; /* first subexpression? */ + register int wasdollar = 0; + + if (EAT('^')) { + EMIT(OBOL, 0); + p->g->iflags |= USEBOL; + p->g->nbol++; + } + while (MORE() && !SEETWO(end1, end2)) { + wasdollar = p_simp_re(p, first); + first = 0; + } + if (wasdollar) { /* oops, that was a trailing anchor */ + DROP(1); + EMIT(OEOL, 0); + p->g->iflags |= USEEOL; + p->g->neol++; + } + + REQUIRE(HERE() != start, REG_EMPTY); /* require nonempty */ +} + +/* + - p_simp_re - parse a simple RE, an atom possibly followed by a repetition + == static int p_simp_re(register struct parse *p, int starordinary); + */ +static int /* was the simple RE an unbackslashed $? */ +p_simp_re(p, starordinary) +register struct parse *p; +int starordinary; /* is a leading * an ordinary character? */ +{ + register int c; + register int count; + register int count2; + register sopno pos; + register int i; + register sopno subno; +# define BACKSL (1<g->cflags®_NEWLINE) + nonnewline(p); + else + EMIT(OANY, 0); + break; + case '[': + p_bracket(p); + break; + case BACKSL|'{': + SETERROR(REG_BADRPT); + break; + case BACKSL|'(': + p->g->nsub++; + subno = p->g->nsub; + if (subno < NPAREN) + p->pbegin[subno] = HERE(); + EMIT(OLPAREN, subno); + /* the MORE here is an error heuristic */ + if (MORE() && !SEETWO('\\', ')')) + p_bre(p, '\\', ')'); + if (subno < NPAREN) { + p->pend[subno] = HERE(); + assert(p->pend[subno] != 0); + } + EMIT(ORPAREN, subno); + REQUIRE(EATTWO('\\', ')'), REG_EPAREN); + break; + case BACKSL|')': /* should not get here -- must be user */ + case BACKSL|'}': + SETERROR(REG_EPAREN); + break; + case BACKSL|'1': + case BACKSL|'2': + case BACKSL|'3': + case BACKSL|'4': + case BACKSL|'5': + case BACKSL|'6': + case BACKSL|'7': + case BACKSL|'8': + case BACKSL|'9': + i = (c&~BACKSL) - '0'; + assert(i < NPAREN); + if (p->pend[i] != 0) { + assert(i <= p->g->nsub); + EMIT(OBACK_, i); + assert(p->pbegin[i] != 0); + assert(OP(p->strip[p->pbegin[i]]) == OLPAREN); + assert(OP(p->strip[p->pend[i]]) == ORPAREN); + (void) dupl(p, p->pbegin[i]+1, p->pend[i]); + EMIT(O_BACK, i); + } else + SETERROR(REG_ESUBREG); + p->g->backrefs = 1; + break; + case '*': + REQUIRE(starordinary, REG_BADRPT); + /* FALLTHROUGH */ + default: + ordinary(p, c &~ BACKSL); + break; + } + + if (EAT('*')) { /* implemented as +? */ + /* this case does not require the (y|) trick, noKLUDGE */ + INSERT(OPLUS_, pos); + ASTERN(O_PLUS, pos); + INSERT(OQUEST_, pos); + ASTERN(O_QUEST, pos); + } else if (EATTWO('\\', '{')) { + count = p_count(p); + if (EAT(',')) { + if (MORE() && isdigit(PEEK())) { + count2 = p_count(p); + REQUIRE(count <= count2, REG_BADBR); + } else /* single number with comma */ + count2 = INFINITY; + } else /* just a single number */ + count2 = count; + repeat(p, pos, count, count2); + if (!EATTWO('\\', '}')) { /* error heuristics */ + while (MORE() && !SEETWO('\\', '}')) + NEXT(); + REQUIRE(MORE(), REG_EBRACE); + SETERROR(REG_BADBR); + } + } else if (c == (unsigned char)'$') /* $ (but not \$) ends it */ + return(1); + + return(0); +} + +/* + - p_count - parse a repetition count + == static int p_count(register struct parse *p); + */ +static int /* the value */ +p_count(p) +register struct parse *p; +{ + register int count = 0; + register int ndigits = 0; + + while (MORE() && isdigit(PEEK()) && count <= DUPMAX) { + count = count*10 + (GETNEXT() - '0'); + ndigits++; + } + + REQUIRE(ndigits > 0 && count <= DUPMAX, REG_BADBR); + return(count); +} + +/* + - p_bracket - parse a bracketed character list + == static void p_bracket(register struct parse *p); + * + * Note a significant property of this code: if the allocset() did SETERROR, + * no set operations are done. + */ +static void +p_bracket(p) +register struct parse *p; +{ + register char c; + register cset *cs = allocset(p); + register int invert = 0; + + /* Dept of Truly Sickening Special-Case Kludges */ + if (p->next + 5 < p->end && strncmp(p->next, "[:<:]]", 6) == 0) { + EMIT(OBOW, 0); + NEXTn(6); + return; + } + if (p->next + 5 < p->end && strncmp(p->next, "[:>:]]", 6) == 0) { + EMIT(OEOW, 0); + NEXTn(6); + return; + } + + if (EAT('^')) + invert++; /* make note to invert set at end */ + if (EAT(']')) + CHadd(cs, ']'); + else if (EAT('-')) + CHadd(cs, '-'); + while (MORE() && PEEK() != ']' && !SEETWO('-', ']')) + p_b_term(p, cs); + if (EAT('-')) + CHadd(cs, '-'); + MUSTEAT(']', REG_EBRACK); + + if (p->error != 0) /* don't mess things up further */ + return; + + if (p->g->cflags®_ICASE) { + register int i; + register int ci; + + for (i = p->g->csetsize - 1; i >= 0; i--) + if (CHIN(cs, i) && isalpha(i)) { + ci = othercase(i); + if (ci != i) + CHadd(cs, ci); + } + if (cs->multis != NULL) + mccase(p, cs); + } + if (invert) { + register int i; + + for (i = p->g->csetsize - 1; i >= 0; i--) + if (CHIN(cs, i)) + CHsub(cs, i); + else + CHadd(cs, i); + if (p->g->cflags®_NEWLINE) + CHsub(cs, '\n'); + if (cs->multis != NULL) + mcinvert(p, cs); + } + + assert(cs->multis == NULL); /* xxx */ + + if (nch(p, cs) == 1) { /* optimize singleton sets */ + ordinary(p, firstch(p, cs)); + freeset(p, cs); + } else + EMIT(OANYOF, freezeset(p, cs)); +} + +/* + - p_b_term - parse one term of a bracketed character list + == static void p_b_term(register struct parse *p, register cset *cs); + */ +static void +p_b_term(p, cs) +register struct parse *p; +register cset *cs; +{ + register char c; + register char start, finish; + register int i; + + /* classify what we've got */ + switch ((MORE()) ? PEEK() : '\0') { + case '[': + c = (MORE2()) ? PEEK2() : '\0'; + break; + case '-': + SETERROR(REG_ERANGE); + return; /* NOTE RETURN */ + break; + default: + c = '\0'; + break; + } + + switch (c) { + case ':': /* character class */ + NEXT2(); + REQUIRE(MORE(), REG_EBRACK); + c = PEEK(); + REQUIRE(c != '-' && c != ']', REG_ECTYPE); + p_b_cclass(p, cs); + REQUIRE(MORE(), REG_EBRACK); + REQUIRE(EATTWO(':', ']'), REG_ECTYPE); + break; + case '=': /* equivalence class */ + NEXT2(); + REQUIRE(MORE(), REG_EBRACK); + c = PEEK(); + REQUIRE(c != '-' && c != ']', REG_ECOLLATE); + p_b_eclass(p, cs); + REQUIRE(MORE(), REG_EBRACK); + REQUIRE(EATTWO('=', ']'), REG_ECOLLATE); + break; + default: /* symbol, ordinary character, or range */ +/* xxx revision needed for multichar stuff */ + start = p_b_symbol(p); + if (SEE('-') && MORE2() && PEEK2() != ']') { + /* range */ + NEXT(); + if (EAT('-')) + finish = '-'; + else + finish = p_b_symbol(p); + } else + finish = start; +/* xxx what about signed chars here... */ + REQUIRE(start <= finish, REG_ERANGE); + for (i = start; i <= finish; i++) + CHadd(cs, i); + break; + } +} + +/* + - p_b_cclass - parse a character-class name and deal with it + == static void p_b_cclass(register struct parse *p, register cset *cs); + */ +static void +p_b_cclass(p, cs) +register struct parse *p; +register cset *cs; +{ + register char *sp = p->next; + register struct cclass *cp; + register size_t len; + register char *u; + register char c; + + while (MORE() && isalpha(PEEK())) + NEXT(); + len = p->next - sp; + for (cp = cclasses; cp->name != NULL; cp++) + if (strncmp(cp->name, sp, len) == 0 && cp->name[len] == '\0') + break; + if (cp->name == NULL) { + /* oops, didn't find it */ + SETERROR(REG_ECTYPE); + return; + } + + u = cp->chars; + while ((c = *u++) != '\0') + CHadd(cs, c); + for (u = cp->multis; *u != '\0'; u += strlen(u) + 1) + MCadd(p, cs, u); +} + +/* + - p_b_eclass - parse an equivalence-class name and deal with it + == static void p_b_eclass(register struct parse *p, register cset *cs); + * + * This implementation is incomplete. xxx + */ +static void +p_b_eclass(p, cs) +register struct parse *p; +register cset *cs; +{ + register char c; + + c = p_b_coll_elem(p, '='); + CHadd(cs, c); +} + +/* + - p_b_symbol - parse a character or [..]ed multicharacter collating symbol + == static char p_b_symbol(register struct parse *p); + */ +static char /* value of symbol */ +p_b_symbol(p) +register struct parse *p; +{ + register char value; + + REQUIRE(MORE(), REG_EBRACK); + if (!EATTWO('[', '.')) + return(GETNEXT()); + + /* collating symbol */ + value = p_b_coll_elem(p, '.'); + REQUIRE(EATTWO('.', ']'), REG_ECOLLATE); + return(value); +} + +/* + - p_b_coll_elem - parse a collating-element name and look it up + == static char p_b_coll_elem(register struct parse *p, int endc); + */ +static char /* value of collating element */ +p_b_coll_elem(p, endc) +register struct parse *p; +int endc; /* name ended by endc,']' */ +{ + register char *sp = p->next; + register struct cname *cp; + register int len; + register char c; + + while (MORE() && !SEETWO(endc, ']')) + NEXT(); + if (!MORE()) { + SETERROR(REG_EBRACK); + return(0); + } + len = p->next - sp; + for (cp = cnames; cp->name != NULL; cp++) + if (strncmp(cp->name, sp, len) == 0 && cp->name[len] == '\0') + return(cp->code); /* known name */ + if (len == 1) + return(*sp); /* single character */ + SETERROR(REG_ECOLLATE); /* neither */ + return(0); +} + +/* + - othercase - return the case counterpart of an alphabetic + == static char othercase(int ch); + */ +static char /* if no counterpart, return ch */ +othercase(ch) +int ch; +{ + assert(isalpha(ch)); + if (isupper(ch)) + return(tolower(ch)); + else if (islower(ch)) + return(toupper(ch)); + else /* peculiar, but could happen */ + return(ch); +} + +/* + - bothcases - emit a dualcase version of a two-case character + == static void bothcases(register struct parse *p, int ch); + * + * Boy, is this implementation ever a kludge... + */ +static void +bothcases(p, ch) +register struct parse *p; +int ch; +{ + register char *oldnext = p->next; + register char *oldend = p->end; + char bracket[3]; + + assert(othercase(ch) != ch); /* p_bracket() would recurse */ + p->next = bracket; + p->end = bracket+2; + bracket[0] = ch; + bracket[1] = ']'; + bracket[2] = '\0'; + p_bracket(p); + assert(p->next == bracket+2); + p->next = oldnext; + p->end = oldend; +} + +/* + - ordinary - emit an ordinary character + == static void ordinary(register struct parse *p, register int ch); + */ +static void +ordinary(p, ch) +register struct parse *p; +register int ch; +{ + register cat_t *cap = p->g->categories; + + if ((p->g->cflags®_ICASE) && isalpha(ch) && othercase(ch) != ch) + bothcases(p, ch); + else { + EMIT(OCHAR, (unsigned char)ch); + if (cap[ch] == 0) + cap[ch] = p->g->ncategories++; + } +} + +/* + - nonnewline - emit REG_NEWLINE version of OANY + == static void nonnewline(register struct parse *p); + * + * Boy, is this implementation ever a kludge... + */ +static void +nonnewline(p) +register struct parse *p; +{ + register char *oldnext = p->next; + register char *oldend = p->end; + char bracket[4]; + + p->next = bracket; + p->end = bracket+3; + bracket[0] = '^'; + bracket[1] = '\n'; + bracket[2] = ']'; + bracket[3] = '\0'; + p_bracket(p); + assert(p->next == bracket+3); + p->next = oldnext; + p->end = oldend; +} + +/* + - repeat - generate code for a bounded repetition, recursively if needed + == static void repeat(register struct parse *p, sopno start, int from, int to); + */ +static void +repeat(p, start, from, to) +register struct parse *p; +sopno start; /* operand from here to end of strip */ +int from; /* repeated from this number */ +int to; /* to this number of times (maybe INFINITY) */ +{ + register sopno finish = HERE(); +# define N 2 +# define INF 3 +# define REP(f, t) ((f)*8 + (t)) +# define MAP(n) (((n) <= 1) ? (n) : ((n) == INFINITY) ? INF : N) + register sopno copy; + + if (p->error != 0) /* head off possible runaway recursion */ + return; + + assert(from <= to); + + switch (REP(MAP(from), MAP(to))) { + case REP(0, 0): /* must be user doing this */ + DROP(finish-start); /* drop the operand */ + break; + case REP(0, 1): /* as x{1,1}? */ + case REP(0, N): /* as x{1,n}? */ + case REP(0, INF): /* as x{1,}? */ + /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */ + INSERT(OCH_, start); /* offset is wrong... */ + repeat(p, start+1, 1, to); + ASTERN(OOR1, start); + AHEAD(start); /* ... fix it */ + EMIT(OOR2, 0); + AHEAD(THERE()); + ASTERN(O_CH, THERETHERE()); + break; + case REP(1, 1): /* trivial case */ + /* done */ + break; + case REP(1, N): /* as x?x{1,n-1} */ + /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */ + INSERT(OCH_, start); + ASTERN(OOR1, start); + AHEAD(start); + EMIT(OOR2, 0); /* offset very wrong... */ + AHEAD(THERE()); /* ...so fix it */ + ASTERN(O_CH, THERETHERE()); + copy = dupl(p, start+1, finish+1); + assert(copy == finish+4); + repeat(p, copy, 1, to-1); + break; + case REP(1, INF): /* as x+ */ + INSERT(OPLUS_, start); + ASTERN(O_PLUS, start); + break; + case REP(N, N): /* as xx{m-1,n-1} */ + copy = dupl(p, start, finish); + repeat(p, copy, from-1, to-1); + break; + case REP(N, INF): /* as xx{n-1,INF} */ + copy = dupl(p, start, finish); + repeat(p, copy, from-1, to); + break; + default: /* "can't happen" */ + SETERROR(REG_ASSERT); /* just in case */ + break; + } +} + +/* + - seterr - set an error condition + == static int seterr(register struct parse *p, int e); + */ +static int /* useless but makes type checking happy */ +seterr(p, e) +register struct parse *p; +int e; +{ + if (p->error == 0) /* keep earliest error condition */ + p->error = e; + p->next = nuls; /* try to bring things to a halt */ + p->end = nuls; + return(0); /* make the return value well-defined */ +} + +/* + - allocset - allocate a set of characters for [] + == static cset *allocset(register struct parse *p); + */ +static cset * +allocset(p) +register struct parse *p; +{ + register int no = p->g->ncsets++; + register size_t nc; + register size_t nbytes; + register cset *cs; + register size_t css = (size_t)p->g->csetsize; + register int i; + + if (no >= p->ncsalloc) { /* need another column of space */ + p->ncsalloc += CHAR_BIT; + nc = p->ncsalloc; + assert(nc % CHAR_BIT == 0); + nbytes = nc / CHAR_BIT * css; + if (p->g->sets == NULL) + p->g->sets = (cset *)malloc(nc * sizeof(cset)); + else + p->g->sets = (cset *)realloc((char *)p->g->sets, + nc * sizeof(cset)); + if (p->g->setbits == NULL) + p->g->setbits = (uch *)malloc(nbytes); + else { + p->g->setbits = (uch *)realloc((char *)p->g->setbits, + nbytes); + /* xxx this isn't right if setbits is now NULL */ + for (i = 0; i < no; i++) + p->g->sets[i].ptr = p->g->setbits + css*(i/CHAR_BIT); + } + if (p->g->sets != NULL && p->g->setbits != NULL) + (void) memset((char *)p->g->setbits + (nbytes - css), + 0, css); + else { + no = 0; + SETERROR(REG_ESPACE); + /* caller's responsibility not to do set ops */ + } + } + + assert(p->g->sets != NULL); /* xxx */ + cs = &p->g->sets[no]; + cs->ptr = p->g->setbits + css*((no)/CHAR_BIT); + cs->mask = 1 << ((no) % CHAR_BIT); + cs->hash = 0; + cs->smultis = 0; + cs->multis = NULL; + + return(cs); +} + +/* + - freeset - free a now-unused set + == static void freeset(register struct parse *p, register cset *cs); + */ +static void +freeset(p, cs) +register struct parse *p; +register cset *cs; +{ + register int i; + register cset *top = &p->g->sets[p->g->ncsets]; + register size_t css = (size_t)p->g->csetsize; + + for (i = 0; i < css; i++) + CHsub(cs, i); + if (cs == top-1) /* recover only the easy case */ + p->g->ncsets--; +} + +/* + - freezeset - final processing on a set of characters + == static int freezeset(register struct parse *p, register cset *cs); + * + * The main task here is merging identical sets. This is usually a waste + * of time (although the hash code minimizes the overhead), but can win + * big if REG_ICASE is being used. REG_ICASE, by the way, is why the hash + * is done using addition rather than xor -- all ASCII [aA] sets xor to + * the same value! + */ +static int /* set number */ +freezeset(p, cs) +register struct parse *p; +register cset *cs; +{ + register uch h = cs->hash; + register int i; + register cset *top = &p->g->sets[p->g->ncsets]; + register cset *cs2; + register size_t css = (size_t)p->g->csetsize; + + /* look for an earlier one which is the same */ + for (cs2 = &p->g->sets[0]; cs2 < top; cs2++) + if (cs2->hash == h && cs2 != cs) { + /* maybe */ + for (i = 0; i < css; i++) + if (!!CHIN(cs2, i) != !!CHIN(cs, i)) + break; /* no */ + if (i == css) + break; /* yes */ + } + + if (cs2 < top) { /* found one */ + freeset(p, cs); + cs = cs2; + } + + return((int)(cs - p->g->sets)); +} + +/* + - firstch - return first character in a set (which must have at least one) + == static int firstch(register struct parse *p, register cset *cs); + */ +static int /* character; there is no "none" value */ +firstch(p, cs) +register struct parse *p; +register cset *cs; +{ + register int i; + register size_t css = (size_t)p->g->csetsize; + + for (i = 0; i < css; i++) + if (CHIN(cs, i)) + return((char)i); + assert(never); + return(0); /* arbitrary */ +} + +/* + - nch - number of characters in a set + == static int nch(register struct parse *p, register cset *cs); + */ +static int +nch(p, cs) +register struct parse *p; +register cset *cs; +{ + register int i; + register size_t css = (size_t)p->g->csetsize; + register int n = 0; + + for (i = 0; i < css; i++) + if (CHIN(cs, i)) + n++; + return(n); +} + +/* + - mcadd - add a collating element to a cset + == static void mcadd(register struct parse *p, register cset *cs, \ + == register char *cp); + */ +static void +mcadd(p, cs, cp) +register struct parse *p; +register cset *cs; +register char *cp; +{ + register size_t oldend = cs->smultis; + + cs->smultis += strlen(cp) + 1; + if (cs->multis == NULL) + cs->multis = malloc(cs->smultis); + else + cs->multis = realloc(cs->multis, cs->smultis); + if (cs->multis == NULL) { + SETERROR(REG_ESPACE); + return; + } + + (void) strcpy(cs->multis + oldend - 1, cp); + cs->multis[cs->smultis - 1] = '\0'; +} + +/* + - mcsub - subtract a collating element from a cset + == static void mcsub(register cset *cs, register char *cp); + */ +static void +mcsub(cs, cp) +register cset *cs; +register char *cp; +{ + register char *fp = mcfind(cs, cp); + register size_t len = strlen(fp); + + assert(fp != NULL); + (void) memmove(fp, fp + len + 1, + cs->smultis - (fp + len + 1 - cs->multis)); + cs->smultis -= len; + + if (cs->smultis == 0) { + free(cs->multis); + cs->multis = NULL; + return; + } + + cs->multis = realloc(cs->multis, cs->smultis); + assert(cs->multis != NULL); +} + +/* + - mcin - is a collating element in a cset? + == static int mcin(register cset *cs, register char *cp); + */ +static int +mcin(cs, cp) +register cset *cs; +register char *cp; +{ + return(mcfind(cs, cp) != NULL); +} + +/* + - mcfind - find a collating element in a cset + == static char *mcfind(register cset *cs, register char *cp); + */ +static char * +mcfind(cs, cp) +register cset *cs; +register char *cp; +{ + register char *p; + + if (cs->multis == NULL) + return(NULL); + for (p = cs->multis; *p != '\0'; p += strlen(p) + 1) + if (strcmp(cp, p) == 0) + return(p); + return(NULL); +} + +/* + - mcinvert - invert the list of collating elements in a cset + == static void mcinvert(register struct parse *p, register cset *cs); + * + * This would have to know the set of possibilities. Implementation + * is deferred. + */ +static void +mcinvert(p, cs) +register struct parse *p; +register cset *cs; +{ + assert(cs->multis == NULL); /* xxx */ +} + +/* + - mccase - add case counterparts of the list of collating elements in a cset + == static void mccase(register struct parse *p, register cset *cs); + * + * This would have to know the set of possibilities. Implementation + * is deferred. + */ +static void +mccase(p, cs) +register struct parse *p; +register cset *cs; +{ + assert(cs->multis == NULL); /* xxx */ +} + +/* + - isinsets - is this character in any sets? + == static int isinsets(register struct re_guts *g, int c); + */ +static int /* predicate */ +isinsets(g, c) +register struct re_guts *g; +int c; +{ + register uch *col; + register int i; + register int ncols = (g->ncsets+(CHAR_BIT-1)) / CHAR_BIT; + register unsigned uc = (unsigned char)c; + + for (i = 0, col = g->setbits; i < ncols; i++, col += g->csetsize) + if (col[uc] != 0) + return(1); + return(0); +} + +/* + - samesets - are these two characters in exactly the same sets? + == static int samesets(register struct re_guts *g, int c1, int c2); + */ +static int /* predicate */ +samesets(g, c1, c2) +register struct re_guts *g; +int c1; +int c2; +{ + register uch *col; + register int i; + register int ncols = (g->ncsets+(CHAR_BIT-1)) / CHAR_BIT; + register unsigned uc1 = (unsigned char)c1; + register unsigned uc2 = (unsigned char)c2; + + for (i = 0, col = g->setbits; i < ncols; i++, col += g->csetsize) + if (col[uc1] != col[uc2]) + return(0); + return(1); +} + +/* + - categorize - sort out character categories + == static void categorize(struct parse *p, register struct re_guts *g); + */ +static void +categorize(p, g) +struct parse *p; +register struct re_guts *g; +{ + register cat_t *cats = g->categories; + register int c; + register int c2; + register cat_t cat; + + /* avoid making error situations worse */ + if (p->error != 0) + return; + + for (c = CHAR_MIN; c <= CHAR_MAX; c++) + if (cats[c] == 0 && isinsets(g, c)) { + cat = g->ncategories++; + cats[c] = cat; + for (c2 = c+1; c2 <= CHAR_MAX; c2++) + if (cats[c2] == 0 && samesets(g, c, c2)) + cats[c2] = cat; + } +} + +/* + - dupl - emit a duplicate of a bunch of sops + == static sopno dupl(register struct parse *p, sopno start, sopno finish); + */ +static sopno /* start of duplicate */ +dupl(p, start, finish) +register struct parse *p; +sopno start; /* from here */ +sopno finish; /* to this less one */ +{ + register sopno ret = HERE(); + register sopno len = finish - start; + + assert(finish >= start); + if (len == 0) + return(ret); + enlarge(p, p->ssize + len); /* this many unexpected additions */ + assert(p->ssize >= p->slen + len); + (void) memcpy((char *)(p->strip + p->slen), + (char *)(p->strip + start), (size_t)len*sizeof(sop)); + p->slen += len; + return(ret); +} + +/* + - doemit - emit a strip operator + == static void doemit(register struct parse *p, sop op, size_t opnd); + * + * It might seem better to implement this as a macro with a function as + * hard-case backup, but it's just too big and messy unless there are + * some changes to the data structures. Maybe later. + */ +static void +doemit(p, op, opnd) +register struct parse *p; +sop op; +size_t opnd; +{ + /* avoid making error situations worse */ + if (p->error != 0) + return; + + /* deal with oversize operands ("can't happen", more or less) */ + assert(opnd < 1<slen >= p->ssize) + enlarge(p, (p->ssize+1) / 2 * 3); /* +50% */ + assert(p->slen < p->ssize); + + /* finally, it's all reduced to the easy case */ + p->strip[p->slen++] = SOP(op, opnd); +} + +/* + - doinsert - insert a sop into the strip + == static void doinsert(register struct parse *p, sop op, size_t opnd, sopno pos); + */ +static void +doinsert(p, op, opnd, pos) +register struct parse *p; +sop op; +size_t opnd; +sopno pos; +{ + register sopno sn; + register sop s; + register int i; + + /* avoid making error situations worse */ + if (p->error != 0) + return; + + sn = HERE(); + EMIT(op, opnd); /* do checks, ensure space */ + assert(HERE() == sn+1); + s = p->strip[sn]; + + /* adjust paren pointers */ + assert(pos > 0); + for (i = 1; i < NPAREN; i++) { + if (p->pbegin[i] >= pos) { + p->pbegin[i]++; + } + if (p->pend[i] >= pos) { + p->pend[i]++; + } + } + + memmove((char *)&p->strip[pos+1], (char *)&p->strip[pos], + (HERE()-pos-1)*sizeof(sop)); + p->strip[pos] = s; +} + +/* + - dofwd - complete a forward reference + == static void dofwd(register struct parse *p, sopno pos, sop value); + */ +static void +dofwd(p, pos, value) +register struct parse *p; +register sopno pos; +sop value; +{ + /* avoid making error situations worse */ + if (p->error != 0) + return; + + assert(value < 1<strip[pos] = OP(p->strip[pos]) | value; +} + +/* + - enlarge - enlarge the strip + == static void enlarge(register struct parse *p, sopno size); + */ +static void +enlarge(p, size) +register struct parse *p; +register sopno size; +{ + register sop *sp; + + if (p->ssize >= size) + return; + + sp = (sop *)realloc(p->strip, size*sizeof(sop)); + if (sp == NULL) { + SETERROR(REG_ESPACE); + return; + } + p->strip = sp; + p->ssize = size; +} + +/* + - stripsnug - compact the strip + == static void stripsnug(register struct parse *p, register struct re_guts *g); + */ +static void +stripsnug(p, g) +register struct parse *p; +register struct re_guts *g; +{ + g->nstates = p->slen; + g->strip = (sop *)realloc((char *)p->strip, p->slen * sizeof(sop)); + if (g->strip == NULL) { + SETERROR(REG_ESPACE); + g->strip = p->strip; + } +} + +/* + - findmust - fill in must and mlen with longest mandatory literal string + == static void findmust(register struct parse *p, register struct re_guts *g); + * + * This algorithm could do fancy things like analyzing the operands of | + * for common subsequences. Someday. This code is simple and finds most + * of the interesting cases. + * + * Note that must and mlen got initialized during setup. + */ +static void +findmust(p, g) +struct parse *p; +register struct re_guts *g; +{ + register sop *scan; + sop *start; + register sop *newstart; + register sopno newlen; + register sop s; + register char *cp; + register sopno i; + + /* avoid making error situations worse */ + if (p->error != 0) + return; + + /* find the longest OCHAR sequence in strip */ + newlen = 0; + scan = g->strip + 1; + do { + s = *scan++; + switch (OP(s)) { + case OCHAR: /* sequence member */ + if (newlen == 0) /* new sequence */ + newstart = scan - 1; + newlen++; + break; + case OPLUS_: /* things that don't break one */ + case OLPAREN: + case ORPAREN: + break; + case OQUEST_: /* things that must be skipped */ + case OCH_: + scan--; + do { + scan += OPND(s); + s = *scan; + /* assert() interferes w debug printouts */ + if (OP(s) != O_QUEST && OP(s) != O_CH && + OP(s) != OOR2) { + g->iflags |= BAD; + return; + } + } while (OP(s) != O_QUEST && OP(s) != O_CH); + /* fallthrough */ + default: /* things that break a sequence */ + if (newlen > g->mlen) { /* ends one */ + start = newstart; + g->mlen = newlen; + } + newlen = 0; + break; + } + } while (OP(s) != OEND); + + if (g->mlen == 0) /* there isn't one */ + return; + + /* turn it into a character string */ + g->must = malloc((size_t)g->mlen + 1); + if (g->must == NULL) { /* argh; just forget it */ + g->mlen = 0; + return; + } + cp = g->must; + scan = start; + for (i = g->mlen; i > 0; i--) { + while (OP(s = *scan++) != OCHAR) + continue; + assert(cp < g->must + g->mlen); + *cp++ = (char)OPND(s); + } + assert(cp == g->must + g->mlen); + *cp++ = '\0'; /* just on general principles */ +} + +/* + - pluscount - count + nesting + == static sopno pluscount(register struct parse *p, register struct re_guts *g); + */ +static sopno /* nesting depth */ +pluscount(p, g) +struct parse *p; +register struct re_guts *g; +{ + register sop *scan; + register sop s; + register sopno plusnest = 0; + register sopno maxnest = 0; + + if (p->error != 0) + return(0); /* there may not be an OEND */ + + scan = g->strip + 1; + do { + s = *scan++; + switch (OP(s)) { + case OPLUS_: + plusnest++; + break; + case O_PLUS: + if (plusnest > maxnest) + maxnest = plusnest; + plusnest--; + break; + } + } while (OP(s) != OEND); + if (plusnest != 0) + g->iflags |= BAD; + return(maxnest); +} diff --git a/sys/amiga/regex/regerror.c b/sys/amiga/regex/regerror.c new file mode 100755 index 000000000..54c1333d9 --- /dev/null +++ b/sys/amiga/regex/regerror.c @@ -0,0 +1,186 @@ +/* $NetBSD: regerror.c,v 1.4 1995/02/27 13:29:20 cgd Exp $ */ + +/*- + * Copyright (c) 1992, 1993, 1994 Henry Spencer. + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Henry Spencer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)regerror.c 8.4 (Berkeley) 3/20/94 + */ + +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)regerror.c 8.4 (Berkeley) 3/20/94"; +#else +static char rcsid[] = "$NetBSD: regerror.c,v 1.4 1995/02/27 13:29:20 cgd Exp $"; +#endif +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" + +/* ========= begin header generated by ./mkh ========= */ +#ifdef __cplusplus +extern "C" { +#endif + +/* === regerror.c === */ +static char *regatoi __P((const regex_t *preg, char *localbuf)); + +#ifdef __cplusplus +} +#endif +/* ========= end header generated by ./mkh ========= */ +/* + = #define REG_NOMATCH 1 + = #define REG_BADPAT 2 + = #define REG_ECOLLATE 3 + = #define REG_ECTYPE 4 + = #define REG_EESCAPE 5 + = #define REG_ESUBREG 6 + = #define REG_EBRACK 7 + = #define REG_EPAREN 8 + = #define REG_EBRACE 9 + = #define REG_BADBR 10 + = #define REG_ERANGE 11 + = #define REG_ESPACE 12 + = #define REG_BADRPT 13 + = #define REG_EMPTY 14 + = #define REG_ASSERT 15 + = #define REG_INVARG 16 + = #define REG_ATOI 255 // convert name to number (!) + = #define REG_ITOA 0400 // convert number to name (!) + */ +static struct rerr { + int code; + char *name; + char *explain; +} rerrs[] = { + REG_NOMATCH, "REG_NOMATCH", "regexec() failed to match", + REG_BADPAT, "REG_BADPAT", "invalid regular expression", + REG_ECOLLATE, "REG_ECOLLATE", "invalid collating element", + REG_ECTYPE, "REG_ECTYPE", "invalid character class", + REG_EESCAPE, "REG_EESCAPE", "trailing backslash (\\)", + REG_ESUBREG, "REG_ESUBREG", "invalid backreference number", + REG_EBRACK, "REG_EBRACK", "brackets ([ ]) not balanced", + REG_EPAREN, "REG_EPAREN", "parentheses not balanced", + REG_EBRACE, "REG_EBRACE", "braces not balanced", + REG_BADBR, "REG_BADBR", "invalid repetition count(s)", + REG_ERANGE, "REG_ERANGE", "invalid character range", + REG_ESPACE, "REG_ESPACE", "out of memory", + REG_BADRPT, "REG_BADRPT", "repetition-operator operand invalid", + REG_EMPTY, "REG_EMPTY", "empty (sub)expression", + REG_ASSERT, "REG_ASSERT", "\"can't happen\" -- you found a bug", + REG_INVARG, "REG_INVARG", "invalid argument to regex routine", + 0, "", "*** unknown regexp error code ***", +}; + +/* + - regerror - the interface to error numbers + = extern size_t regerror(int, const regex_t *, char *, size_t); + */ +/* ARGSUSED */ +size_t +regerror(errcode, preg, errbuf, errbuf_size) +int errcode; +const regex_t *preg; +char *errbuf; +size_t errbuf_size; +{ + register struct rerr *r; + register size_t len; + register int target = errcode &~ REG_ITOA; + register char *s; + char convbuf[50]; + + if (errcode == REG_ATOI) + s = regatoi(preg, convbuf); + else { + for (r = rerrs; r->code != 0; r++) + if (r->code == target) + break; + + if (errcode®_ITOA) { + if (r->code != 0) + (void) strcpy(convbuf, r->name); + else + sprintf(convbuf, "REG_0x%x", target); + assert(strlen(convbuf) < sizeof(convbuf)); + s = convbuf; + } else + s = r->explain; + } + + len = strlen(s) + 1; + if (errbuf_size > 0) { + if (errbuf_size > len) + (void) strcpy(errbuf, s); + else { + (void) strncpy(errbuf, s, errbuf_size-1); + errbuf[errbuf_size-1] = '\0'; + } + } + + return(len); +} + +/* + - regatoi - internal routine to implement REG_ATOI + == static char *regatoi(const regex_t *preg, char *localbuf); + */ +static char * +regatoi(preg, localbuf) +const regex_t *preg; +char *localbuf; +{ + register struct rerr *r; + register size_t siz; + register char *p; + + for (r = rerrs; r->code != 0; r++) + if (strcmp(r->name, preg->re_endp) == 0) + break; + if (r->code == 0) + return("0"); + + sprintf(localbuf, "%d", r->code); + return(localbuf); +} diff --git a/sys/amiga/regex/regex2.h b/sys/amiga/regex/regex2.h new file mode 100755 index 000000000..901a6393b --- /dev/null +++ b/sys/amiga/regex/regex2.h @@ -0,0 +1,175 @@ +/* $NetBSD: regex2.h,v 1.5 1995/02/27 13:29:40 cgd Exp $ */ + +/*- + * Copyright (c) 1992, 1993, 1994 Henry Spencer. + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Henry Spencer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)regex2.h 8.4 (Berkeley) 3/20/94 + */ + +/* + * First, the stuff that ends up in the outside-world include file + = typedef off_t regoff_t; + = typedef struct { + = int re_magic; + = size_t re_nsub; // number of parenthesized subexpressions + = const char *re_endp; // end pointer for REG_PEND + = struct re_guts *re_g; // none of your business :-) + = } regex_t; + = typedef struct { + = regoff_t rm_so; // start of match + = regoff_t rm_eo; // end of match + = } regmatch_t; + */ +/* + * internals of regex_t + */ +#define MAGIC1 ((('r'^0200)<<8) | 'e') + +/* + * The internal representation is a *strip*, a sequence of + * operators ending with an endmarker. (Some terminology etc. is a + * historical relic of earlier versions which used multiple strips.) + * Certain oddities in the representation are there to permit running + * the machinery backwards; in particular, any deviation from sequential + * flow must be marked at both its source and its destination. Some + * fine points: + * + * - OPLUS_ and O_PLUS are *inside* the loop they create. + * - OQUEST_ and O_QUEST are *outside* the bypass they create. + * - OCH_ and O_CH are *outside* the multi-way branch they create, while + * OOR1 and OOR2 are respectively the end and the beginning of one of + * the branches. Note that there is an implicit OOR2 following OCH_ + * and an implicit OOR1 preceding O_CH. + * + * In state representations, an operator's bit is on to signify a state + * immediately *preceding* "execution" of that operator. + */ +typedef unsigned long sop; /* strip operator */ +typedef long sopno; +#define OPRMASK 0xf8000000 +#define OPDMASK 0x07ffffff +#define OPSHIFT ((unsigned)27) +#define OP(n) ((n)&OPRMASK) +#define OPND(n) ((n)&OPDMASK) +#define SOP(op, opnd) ((op)|(opnd)) +/* operators meaning operand */ +/* (back, fwd are offsets) */ +#define OEND (1< uch [csetsize] */ + uch mask; /* bit within array */ + uch hash; /* hash code */ + size_t smultis; + char *multis; /* -> char[smulti] ab\0cd\0ef\0\0 */ +} cset; +/* note that CHadd and CHsub are unsafe, and CHIN doesn't yield 0/1 */ +#define CHadd(cs, c) ((cs)->ptr[(uch)(c)] |= (cs)->mask, (cs)->hash += (c)) +#define CHsub(cs, c) ((cs)->ptr[(uch)(c)] &= ~(cs)->mask, (cs)->hash -= (c)) +#define CHIN(cs, c) ((cs)->ptr[(uch)(c)] & (cs)->mask) +#define MCadd(p, cs, cp) mcadd(p, cs, cp) /* regcomp() internal fns */ +#define MCsub(p, cs, cp) mcsub(p, cs, cp) +#define MCin(p, cs, cp) mcin(p, cs, cp) + +/* stuff for character categories */ +typedef unsigned char cat_t; + +/* + * main compiled-expression structure + */ +struct re_guts { + int magic; +# define MAGIC2 ((('R'^0200)<<8)|'E') + sop *strip; /* malloced area for strip */ + int csetsize; /* number of bits in a cset vector */ + int ncsets; /* number of csets in use */ + cset *sets; /* -> cset [ncsets] */ + uch *setbits; /* -> uch[csetsize][ncsets/CHAR_BIT] */ + int cflags; /* copy of regcomp() cflags argument */ + sopno nstates; /* = number of sops */ + sopno firststate; /* the initial OEND (normally 0) */ + sopno laststate; /* the final OEND */ + int iflags; /* internal flags */ +# define USEBOL 01 /* used ^ */ +# define USEEOL 02 /* used $ */ +# define BAD 04 /* something wrong */ + int nbol; /* number of ^ used */ + int neol; /* number of $ used */ + int ncategories; /* how many character categories */ + cat_t *categories; /* ->catspace[-CHAR_MIN] */ + char *must; /* match must contain this string */ + int mlen; /* length of must */ + size_t nsub; /* copy of re_nsub */ + int backrefs; /* does it use back references? */ + sopno nplus; /* how deep does it nest +s? */ + /* catspace must be last */ + cat_t catspace[1]; /* actually [NC] */ +}; + +/* misc utilities */ +#define OUT (CHAR_MAX+1) /* a non-character value */ +#define ISWORD(c) (isalnum(c) || (c) == '_') diff --git a/sys/amiga/regex/regexec.c b/sys/amiga/regex/regexec.c new file mode 100755 index 000000000..3b8ef5341 --- /dev/null +++ b/sys/amiga/regex/regexec.c @@ -0,0 +1,187 @@ +/* $NetBSD: regexec.c,v 1.6 1995/02/27 13:29:48 cgd Exp $ */ + +/*- + * Copyright (c) 1992, 1993, 1994 Henry Spencer. + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Henry Spencer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)regexec.c 8.3 (Berkeley) 3/20/94 + */ + +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)regexec.c 8.3 (Berkeley) 3/20/94"; +#else +static char rcsid[] = "$NetBSD: regexec.c,v 1.6 1995/02/27 13:29:48 cgd Exp $"; +#endif +#endif /* LIBC_SCCS and not lint */ + +/* + * the outer shell of regexec() + * + * This file includes engine.h *twice*, after muchos fiddling with the + * macros that code uses. This lets the same code operate on two different + * representations for state sets. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "regex2.h" + +static int nope = 0; /* for use in asserts; shuts lint up */ + +/* macros for manipulating states, small version */ +#define states long +#define states1 states /* for later use in regexec() decision */ +#define CLEAR(v) ((v) = 0) +#define SET0(v, n) ((v) &= ~((unsigned long)1 << (n))) +#define SET1(v, n) ((v) |= (unsigned long)1 << (n)) +#define ISSET(v, n) (((v) & ((unsigned long)1 << (n))) != 0) +#define ASSIGN(d, s) ((d) = (s)) +#define EQ(a, b) ((a) == (b)) +#define STATEVARS long dummy /* dummy version */ +#define STATESETUP(m, n) /* nothing */ +#define STATETEARDOWN(m) /* nothing */ +#define SETUP(v) ((v) = 0) +#define onestate long +#define INIT(o, n) ((o) = (unsigned long)1 << (n)) +#define INC(o) ((o) = (unsigned long)(o) << 1) +#define ISSTATEIN(v, o) (((v) & (o)) != 0) +/* some abbreviations; note that some of these know variable names! */ +/* do "if I'm here, I can also be there" etc without branches */ +#define FWD(dst, src, n) ((dst) |= ((unsigned long)(src)&(here)) << (n)) +#define BACK(dst, src, n) ((dst) |= ((unsigned long)(src)&(here)) >> (n)) +#define ISSETBACK(v, n) (((v) & ((unsigned long)here >> (n))) != 0) +/* function names */ +#define SNAMES /* engine.h looks after details */ + +#include "engine.h" + +/* now undo things */ +#undef states +#undef CLEAR +#undef SET0 +#undef SET1 +#undef ISSET +#undef ASSIGN +#undef EQ +#undef STATEVARS +#undef STATESETUP +#undef STATETEARDOWN +#undef SETUP +#undef onestate +#undef INIT +#undef INC +#undef ISSTATEIN +#undef FWD +#undef BACK +#undef ISSETBACK +#undef SNAMES + +/* macros for manipulating states, large version */ +#define states char * +#define CLEAR(v) memset(v, 0, m->g->nstates) +#define SET0(v, n) ((v)[n] = 0) +#define SET1(v, n) ((v)[n] = 1) +#define ISSET(v, n) ((v)[n]) +#define ASSIGN(d, s) memcpy(d, s, m->g->nstates) +#define EQ(a, b) (memcmp(a, b, m->g->nstates) == 0) +#define STATEVARS long vn; char *space +#define STATESETUP(m, nv) { (m)->space = malloc((nv)*(m)->g->nstates); \ + if ((m)->space == NULL) return(REG_ESPACE); \ + (m)->vn = 0; } +#define STATETEARDOWN(m) { free((m)->space); } +#define SETUP(v) ((v) = &m->space[m->vn++ * m->g->nstates]) +#define onestate long +#define INIT(o, n) ((o) = (n)) +#define INC(o) ((o)++) +#define ISSTATEIN(v, o) ((v)[o]) +/* some abbreviations; note that some of these know variable names! */ +/* do "if I'm here, I can also be there" etc without branches */ +#define FWD(dst, src, n) ((dst)[here+(n)] |= (src)[here]) +#define BACK(dst, src, n) ((dst)[here-(n)] |= (src)[here]) +#define ISSETBACK(v, n) ((v)[here - (n)]) +/* function names */ +#define LNAMES /* flag */ + +#include "engine.h" + +/* + - regexec - interface for matching + = extern int regexec(const regex_t *, const char *, size_t, \ + = regmatch_t [], int); + = #define REG_NOTBOL 00001 + = #define REG_NOTEOL 00002 + = #define REG_STARTEND 00004 + = #define REG_TRACE 00400 // tracing of execution + = #define REG_LARGE 01000 // force large representation + = #define REG_BACKR 02000 // force use of backref code + * + * We put this here so we can exploit knowledge of the state representation + * when choosing which matcher to call. Also, by this point the matchers + * have been prototyped. + */ +int /* 0 success, REG_NOMATCH failure */ +regexec(preg, string, nmatch, pmatch, eflags) +const regex_t *preg; +const char *string; +size_t nmatch; +regmatch_t pmatch[]; +int eflags; +{ + register struct re_guts *g = preg->re_g; +#ifdef REDEBUG +# define GOODFLAGS(f) (f) +#else +# define GOODFLAGS(f) ((f)&(REG_NOTBOL|REG_NOTEOL|REG_STARTEND)) +#endif + + if (preg->re_magic != MAGIC1 || g->magic != MAGIC2) + return(REG_BADPAT); + assert(!(g->iflags&BAD)); + if (g->iflags&BAD) /* backstop for no-debug case */ + return(REG_BADPAT); + eflags = GOODFLAGS(eflags); + + if (g->nstates <= CHAR_BIT*sizeof(states1) && !(eflags®_LARGE)) + return(smatcher(g, (char *)string, nmatch, pmatch, eflags)); + else + return(lmatcher(g, (char *)string, nmatch, pmatch, eflags)); +} diff --git a/sys/amiga/regex/regfree.c b/sys/amiga/regex/regfree.c new file mode 100755 index 000000000..e01e30627 --- /dev/null +++ b/sys/amiga/regex/regfree.c @@ -0,0 +1,86 @@ +/* $NetBSD: regfree.c,v 1.4 1995/02/27 13:29:56 cgd Exp $ */ + +/*- + * Copyright (c) 1992, 1993, 1994 Henry Spencer. + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Henry Spencer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)regfree.c 8.3 (Berkeley) 3/20/94 + */ + +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)regfree.c 8.3 (Berkeley) 3/20/94"; +#else +static char rcsid[] = "$NetBSD: regfree.c,v 1.4 1995/02/27 13:29:56 cgd Exp $"; +#endif +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include +#include + +#include "utils.h" +#include "regex2.h" + +/* + - regfree - free everything + = extern void regfree(regex_t *); + */ +void +regfree(preg) +regex_t *preg; +{ + register struct re_guts *g; + + if (preg->re_magic != MAGIC1) /* oops */ + return; /* nice to complain, but hard */ + + g = preg->re_g; + if (g == NULL || g->magic != MAGIC2) /* oops again */ + return; + preg->re_magic = 0; /* mark it invalid */ + g->magic = 0; /* mark it invalid */ + + if (g->strip != NULL) + free((char *)g->strip); + if (g->sets != NULL) + free((char *)g->sets); + if (g->setbits != NULL) + free((char *)g->setbits); + if (g->must != NULL) + free(g->must); + free((char *)g); +} diff --git a/sys/amiga/regex/utils.h b/sys/amiga/regex/utils.h new file mode 100755 index 000000000..2f372ce29 --- /dev/null +++ b/sys/amiga/regex/utils.h @@ -0,0 +1,59 @@ +/* $NetBSD: utils.h,v 1.5 1995/02/27 13:29:59 cgd Exp $ */ + +/*- + * Copyright (c) 1992, 1993, 1994 Henry Spencer. + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Henry Spencer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)utils.h 8.3 (Berkeley) 3/20/94 + */ + +/* utility definitions */ +#define DUPMAX _POSIX2_RE_DUP_MAX /* xxx is this right? */ +#define INFINITY (DUPMAX + 1) +#define NC (CHAR_MAX - CHAR_MIN + 1) +typedef unsigned char uch; + +/* switch off assertions (if not already off) if no REDEBUG */ +#ifndef REDEBUG +#ifndef NDEBUG +#define NDEBUG /* no assertions please */ +#endif +#endif +#include + +/* for old systems with bcopy() but no memmove() */ +#ifdef USEBCOPY +#define memmove(d, s, c) bcopy(s, d, c) +#endif From cb0b11be112a2ddace1a56a65eb29f03bcc6d8bc Mon Sep 17 00:00:00 2001 From: Ingo Paschke Date: Mon, 23 Mar 2026 20:50:14 +0100 Subject: [PATCH 342/442] Add portable utilities for creating tiles, add AmigaFont to symbols - bmp2iff_host: convert nhtiles.bmp to Amiga IFF tile files. Uses the AMIV UI palette in pens 0-15, remaining pens filled with tile colors sorted by frequency. Usage: bmp2iff_host -planes N input.bmp output.iff - xpm2iff_host: convert XPM to IFF for tomb.iff (RIP screen). Adapted from xpm2iff.c, Copyright (c) 1995 Gregg Wonderly. - Auto-select tiles32.iff (5 planes) or tiles16.iff (4 planes) based on screen color depth at runtime. - Fix NO_GLYPH in amiv_lprint_glyph: return early to prevent blitting with uninitialised data (caused black spots). - Add AmigaFont symbol set to dat/symbols for AMII text mode. --- dat/symbols | 78 +++++ sys/amiga/README.amiga | 2 +- sys/amiga/bmp2iff_host.c | 480 ++++++++++++++++++++++++++ sys/amiga/xpm2iff_host.c | 346 +++++++++++++++++++ sys/unix/hints/include/cross-pre2.370 | 2 +- 5 files changed, 906 insertions(+), 2 deletions(-) create mode 100644 sys/amiga/bmp2iff_host.c create mode 100644 sys/amiga/xpm2iff_host.c diff --git a/dat/symbols b/dat/symbols index af6c04ade..f2bc486fa 100644 --- a/dat/symbols +++ b/dat/symbols @@ -895,4 +895,82 @@ start: Enhanced1 G_trwall_mines: U+251C/113-126-142 finish +start: AmigaFont + Description: Amiga hack.font line-drawing and effect characters + # Dungeon features + S_stone: \x20 + S_vwall: \xc0 + S_hwall: \xc1 + S_tlcorn: \xc2 + S_trcorn: \xc3 + S_blcorn: \xc4 + S_brcorn: \xc5 + S_crwall: \xc6 + S_tuwall: \xd8 + S_tdwall: \xd6 + S_tlwall: \xd7 + S_trwall: \xd5 + S_ndoor: \xd9 + S_vodoor: \x91 + S_hodoor: \x92 + S_vcdoor: \x93 + S_hcdoor: \x94 + S_bars: '#' + S_tree: '#' + S_room: '.' + S_darkroom: \x20 + S_corr: \xe5 + S_litcorr: \xe5 + S_upstair: '<' + S_dnstair: '>' + S_upladder: '<' + S_dnladder: '>' + S_altar: '_' + S_grave: \x5c + S_throne: '#' + S_sink: '{' + S_fountain: '}' + S_pool: '*' + S_ice: '}' + S_lava: '*' + S_vodbridge: '*' + S_hodbridge: '#' + S_vcdbridge: '#' + S_hcdbridge: '.' + S_air: '#' + S_cloud: '}' + # Traps: all default to '^' except web + S_web: '"' + # Effects + S_vbeam: \xf1 + S_hbeam: \xf0 + S_lslant: \xf2 + S_rslant: \xf3 + S_digbeam: '*' + S_flashbeam: '!' + S_boomleft: '{' + S_boomright: '}' + S_ss1: '@' + S_ss2: '&' + S_ss3: '*' + S_ss4: '#' + S_sw_tl: \xf4 + S_sw_tc: \xf5 + S_sw_tr: \xf6 + S_sw_ml: \xf7 + S_sw_mr: \xef + S_sw_bl: \xf8 + S_sw_bc: \xf9 + S_sw_br: \xfa + S_explode1: \xe6 + S_explode2: \xea + S_explode3: \xe7 + S_explode4: \xec + S_explode5: \xd4 + S_explode6: \xed + S_explode7: \xe8 + S_explode8: \xeb + S_explode9: \xe9 +finish + # symbols EOF diff --git a/sys/amiga/README.amiga b/sys/amiga/README.amiga index 165501014..8eda92c31 100644 --- a/sys/amiga/README.amiga +++ b/sys/amiga/README.amiga @@ -73,8 +73,8 @@ Cross-compilation from Linux using bebbo's m68k-amigaos-gcc toolchain # Install toolchain to /opt/amiga # Clone NetHack 3.7 source cd NetHack - make fetch-lua sys/unix/setup.sh sys/unix/hints/linux.370 + make fetch-lua make CROSS_TO_AMIGA=1 all make CROSS_TO_AMIGA=1 package diff --git a/sys/amiga/bmp2iff_host.c b/sys/amiga/bmp2iff_host.c new file mode 100644 index 000000000..154335297 --- /dev/null +++ b/sys/amiga/bmp2iff_host.c @@ -0,0 +1,480 @@ +/* + * bmp2iff_host.c - Convert a BMP to Amiga BMAP IFF. + * Copyright (c) 2026 by Ingo Paschke. + * NetHack may be freely redistributed. See license for details. + * + * IFF BMAP format matches sys/amiga/xpm2iff.c by Gregg Wonderly. + * + * Usage: bmp2iff_host -planes N input.bmp output.iff + * + * This is a HOST tool -- runs on the build machine. + */ + +#include +#include +#include +#include + +#define TILE_X 16 +#define TILE_Y 16 + +#pragma pack(push,1) +typedef struct { + uint16_t bfType; + uint32_t bfSize; + uint16_t bfReserved1, bfReserved2; + uint32_t bfOffBits; +} BMPFILEHEADER; + +typedef struct { + uint32_t biSize; + int32_t biWidth, biHeight; + uint16_t biPlanes, biBitCount; + uint32_t biCompression; + uint32_t biSizeImage; + int32_t biXPelsPerMeter, biYPelsPerMeter; + uint32_t biClrUsed, biClrImportant; +} BMPINFOHEADER; +#pragma pack(pop) + +typedef struct { + uint8_t r, g, b; +} RGB; + +/* --------------------------------------------------------- */ +/* Fixed 16-color AMIV UI palette */ +/* Must match amiv_init_map[] in winami.c */ +/* --------------------------------------------------------- */ + +static const RGB amiv_pal[16] = { + {0x00,0x00,0x00}, /* 0 black */ + {0xFF,0xFF,0xFF}, /* 1 white */ + {0x00,0xBB,0xFF}, /* 2 cyan */ + {0xFF,0x66,0x00}, /* 3 orange */ + {0x00,0x00,0xFF}, /* 4 blue */ + {0x00,0x99,0x00}, /* 5 green */ + {0x66,0x99,0xBB}, /* 6 grey */ + {0xFF,0x00,0x00}, /* 7 red */ + {0x66,0xFF,0x00}, /* 8 ltgreen */ + {0xFF,0xFF,0x00}, /* 9 yellow */ + {0xFF,0x00,0xFF}, /* 10 magenta */ + {0x99,0x44,0x00}, /* 11 brown */ + {0x44,0x66,0x66}, /* 12 greyblue */ + {0xCC,0x44,0x00}, /* 13 ltbrown */ + {0xDD,0xDD,0xBB}, /* 14 ltgrey */ + {0xFF,0xBB,0x99}, /* 15 peach */ +}; + +/* --------------------------------------------------------- */ +/* Colour helpers */ +/* --------------------------------------------------------- */ + +static int +coldist(const RGB *a, const RGB *b) +{ + int dr = a->r - b->r; + int dg = a->g - b->g; + int db = a->b - b->b; + return dr*dr + dg*dg + db*db; +} + +static int +nearest(const RGB *c, const RGB *pal, int n) +{ + int best = 0, bestd = 0x7fffffff, i; + for (i = 0; i < n; i++) { + int d = coldist(c, &pal[i]); + if (d < bestd) { bestd = d; best = i; } + } + return best; +} + +/* --------------------------------------------------------- */ +/* IFF output */ +/* --------------------------------------------------------- */ + +static FILE *iff_fp; + +static void +wr32(uint32_t v) +{ + uint8_t b[4] = { v>>24, v>>16, v>>8, v }; + fwrite(b, 1, 4, iff_fp); +} + +static void +wr_chunk(const char *id, const void *d, uint32_t len) +{ + fwrite(id, 1, 4, iff_fp); + wr32(len); + fwrite(d, 1, len, iff_fp); + if (len & 1) fputc(0, iff_fp); +} + +static void +iff_write(int nplanes, int ncolors, uint8_t *cmap, + int w, int h, uint8_t **planes, + int ntiles, int across, int down) +{ + long spos; + uint32_t plsz = (uint32_t)(w / 8) * h; + int i; + + fwrite("FORM", 1, 4, iff_fp); + spos = ftell(iff_fp); + wr32(0); + fwrite("BMAP", 1, 4, iff_fp); + + /* BMHD */ + { + uint8_t bm[20]; + memset(bm, 0, 20); + bm[0] = w >> 8; bm[1] = w; + bm[2] = h >> 8; bm[3] = h; + bm[8] = (uint8_t)nplanes; + bm[14] = 100; bm[15] = 100; + wr_chunk("BMHD", bm, 20); + } + + /* CAMG */ + { + uint8_t c[4] = {0,0,0x80,0x04}; + wr_chunk("CAMG", c, 4); + } + + /* CMAP */ + wr_chunk("CMAP", cmap, (uint32_t)ncolors * 3); + + /* PDAT */ + { + uint8_t pd[28], *p = pd; + uint32_t v[7]; + v[0] = nplanes; + v[1] = plsz; + v[2] = across; + v[3] = down; + v[4] = ntiles; + v[5] = TILE_X; + v[6] = TILE_Y; + for (i = 0; i < 7; i++) { + p[0] = (v[i]>>24); p[1] = (v[i]>>16); + p[2] = (v[i]>> 8); p[3] = v[i]; + p += 4; + } + wr_chunk("PDAT", pd, 28); + } + + /* PLNE */ + fwrite("PLNE", 1, 4, iff_fp); + wr32(plsz * nplanes); + for (i = 0; i < nplanes; i++) + fwrite(planes[i], 1, plsz, iff_fp); + + /* fix FORM size */ + { + long end = ftell(iff_fp); + uint32_t sz = (uint32_t)(end - spos - 4); + fseek(iff_fp, spos, SEEK_SET); + wr32(sz); + fseek(iff_fp, 0, SEEK_END); + } +} + +/* --------------------------------------------------------- */ +/* Pixel-to-bitplane conversion */ +/* --------------------------------------------------------- */ + +static void +to_planes(uint8_t *pix, int w, int h, + int np, uint8_t **pl) +{ + int rb = w / 8; + int x, y, p; + + for (p = 0; p < np; p++) + memset(pl[p], 0, rb * h); + + for (y = 0; y < h; y++) + for (x = 0; x < w; x++) { + uint8_t v = pix[y * w + x]; + int off = y * rb + x / 8; + int bit = 7 - (x & 7); + for (p = 0; p < np; p++) + if (v & (1 << p)) + pl[p][off] |= (1 << bit); + } +} + +/* --------------------------------------------------------- */ +/* Palette building */ +/* --------------------------------------------------------- */ + +/* + * Build an output palette of 'maxcol' entries: + * - slots 0-15: fixed AMIV UI colors + * - slots 16+: tile colors from the BMP + * + * Returns remap[0..nsrc-1] mapping BMP palette index + * to output palette index. + */ +static void +build_palette(const RGB *src, int nsrc, + const uint8_t *pix, int npix, + int maxcol, + RGB *out, int *remap) +{ + int freq[256] = {0}; + int order[256]; + int i, j, nfree, nuniq; + + /* count pixel frequency per BMP palette entry */ + for (i = 0; i < npix; i++) + freq[pix[i]]++; + + /* sort BMP colors by frequency (descending) */ + for (i = 0; i < nsrc; i++) order[i] = i; + for (i = 1; i < nsrc; i++) { + int k = order[i], kf = freq[k]; + j = i - 1; + while (j >= 0 && freq[order[j]] < kf) { + order[j+1] = order[j]; + j--; + } + order[j+1] = k; + } + + /* count unique colors actually used */ + nuniq = 0; + for (i = 0; i < nsrc; i++) + if (freq[i] > 0) nuniq++; + + /* first 16 slots are AMIV UI */ + for (i = 0; i < 16 && i < maxcol; i++) + out[i] = amiv_pal[i]; + for (i = 16; i < maxcol; i++) + memset(&out[i], 0, sizeof(RGB)); + + nfree = maxcol - 16; + if (nfree < 0) nfree = 0; + + /* + * Case 1: enough free slots for all unique colors. + * Assign each unique BMP color its own pen, exact + * AMIV matches share the UI pen. + */ + if (nuniq <= nfree) { + int next = 16; + for (i = 0; i < nsrc; i++) { + if (freq[i] == 0) { + remap[i] = 0; + continue; + } + /* exact match to an AMIV pen? */ + int best = nearest(&src[i], amiv_pal, 16); + if (coldist(&src[i], &amiv_pal[best]) == 0) { + remap[i] = best; + } else { + remap[i] = next; + out[next] = src[i]; + next++; + } + } + return; + } + + /* + * Case 2: more unique colors than free slots. + * - Direct/near AMIV matches use UI pens. + * - Remaining slots filled by most-frequent colors. + * - Leftovers mapped to nearest in final palette. + */ + { + int next = 16; + int assigned[256]; + memset(assigned, 0, sizeof(assigned)); + + /* pass 1: exact/near AMIV matches */ + for (i = 0; i < nsrc; i++) { + if (freq[i] == 0) { + remap[i] = 0; + assigned[i] = 1; + continue; + } + int best = nearest(&src[i], amiv_pal, 16); + int d = coldist(&src[i], &amiv_pal[best]); + if (d < 200) { /* near match threshold */ + remap[i] = best; + assigned[i] = 1; + } + } + + /* pass 2: fill free slots with most-frequent + * unassigned colors (order[] is freq-sorted) */ + for (i = 0; i < nsrc && next < maxcol; i++) { + int idx = order[i]; + if (assigned[idx] || freq[idx] == 0) + continue; + remap[idx] = next; + out[next] = src[idx]; + assigned[idx] = 1; + next++; + } + + /* pass 3: map remaining to nearest in palette */ + for (i = 0; i < nsrc; i++) { + if (!assigned[i]) + remap[i] = nearest(&src[i], out, next); + } + } +} + +/* --------------------------------------------------------- */ +/* Main */ +/* --------------------------------------------------------- */ + +int +main(int argc, char **argv) +{ + FILE *bmpfp; + BMPFILEHEADER fhdr; + BMPINFOHEADER ihdr; + RGB palette[256]; + int ncolors, img_w, img_h, rowstride; + uint8_t *bmpdata, *pixels; + int ntiles, across, down; + int nplanes, maxcol; + int i, y; + RGB outpal[256]; + int remap[256]; + uint8_t *remapped; + uint8_t *plane_data[8]; + uint8_t cmap_rgb[256 * 3]; + int planesize; + + /* parse args */ + if (argc != 5 + || strcmp(argv[1], "-planes") != 0) { + fprintf(stderr, + "Usage: %s -planes N input.bmp output.iff\n", + argv[0]); + return 1; + } + nplanes = atoi(argv[2]); + if (nplanes < 1 || nplanes > 8) { + fprintf(stderr, "planes must be 1-8\n"); + return 1; + } + maxcol = 1 << nplanes; + + /* read BMP */ + bmpfp = fopen(argv[3], "rb"); + if (!bmpfp) { perror(argv[3]); return 1; } + + if (fread(&fhdr, sizeof(fhdr), 1, bmpfp) != 1 + || fread(&ihdr, sizeof(ihdr), 1, bmpfp) != 1) { + fprintf(stderr, "Failed to read BMP header\n"); + return 1; + } + if (fhdr.bfType != 0x4D42) { + fprintf(stderr, "Not a BMP file\n"); + return 1; + } + if (ihdr.biBitCount != 8) { + fprintf(stderr, + "Expected 8-bit BMP, got %d-bit\n", + ihdr.biBitCount); + return 1; + } + + img_w = ihdr.biWidth; + img_h = abs(ihdr.biHeight); + ncolors = ihdr.biClrUsed ? ihdr.biClrUsed : 256; + if (ncolors > 256) ncolors = 256; + + /* read palette (BMP stores BGRx) */ + { + uint8_t raw[256][4]; + if (fread(raw, 4, ncolors, bmpfp) + != (size_t)ncolors) { + fprintf(stderr, "Failed to read palette\n"); + return 1; + } + for (i = 0; i < ncolors; i++) { + palette[i].r = raw[i][2]; + palette[i].g = raw[i][1]; + palette[i].b = raw[i][0]; + } + } + + /* read pixel data */ + rowstride = (img_w + 3) & ~3; + bmpdata = malloc(rowstride * img_h); + fseek(bmpfp, fhdr.bfOffBits, SEEK_SET); + if (fread(bmpdata, 1, rowstride * img_h, bmpfp) + != (size_t)(rowstride * img_h)) { + fprintf(stderr, "Failed to read pixel data\n"); + return 1; + } + fclose(bmpfp); + + /* flip bottom-up to top-down */ + pixels = malloc(img_w * img_h); + if (ihdr.biHeight > 0) { + for (y = 0; y < img_h; y++) + memcpy(pixels + y * img_w, + bmpdata + (img_h-1-y) * rowstride, + img_w); + } else { + for (y = 0; y < img_h; y++) + memcpy(pixels + y * img_w, + bmpdata + y * rowstride, img_w); + } + free(bmpdata); + + across = img_w / TILE_X; + down = img_h / TILE_Y; + ntiles = across * down; + + /* build palette and remap pixels */ + build_palette(palette, ncolors, + pixels, img_w * img_h, + maxcol, outpal, remap); + + remapped = malloc(img_w * img_h); + for (i = 0; i < img_w * img_h; i++) + remapped[i] = (uint8_t)remap[pixels[i]]; + + /* convert to bitplanes */ + planesize = (img_w / 8) * img_h; + for (i = 0; i < nplanes; i++) + plane_data[i] = calloc(1, planesize); + + to_planes(remapped, img_w, img_h, + nplanes, plane_data); + + /* build CMAP */ + for (i = 0; i < maxcol; i++) { + cmap_rgb[i*3+0] = outpal[i].r; + cmap_rgb[i*3+1] = outpal[i].g; + cmap_rgb[i*3+2] = outpal[i].b; + } + + /* write IFF */ + iff_fp = fopen(argv[4], "wb"); + if (!iff_fp) { perror(argv[4]); return 1; } + + iff_write(nplanes, maxcol, cmap_rgb, + img_w, img_h, plane_data, + ntiles, across, down); + fclose(iff_fp); + + printf("%s: %dx%d, %d colors (%d planes), " + "%d tiles\n", + argv[4], img_w, img_h, + maxcol, nplanes, ntiles); + + for (i = 0; i < nplanes; i++) free(plane_data[i]); + free(remapped); + free(pixels); + return 0; +} diff --git a/sys/amiga/xpm2iff_host.c b/sys/amiga/xpm2iff_host.c new file mode 100644 index 000000000..589d4f56f --- /dev/null +++ b/sys/amiga/xpm2iff_host.c @@ -0,0 +1,346 @@ +/* xpm2iff_host.c - host-side .xpm -> Amiga BMAP IFF converter. + * Copyright (c) 2026 by Ingo Paschke. + * NetHack may be freely redistributed. See license for details. + * + * Adapted from sys/amiga/xpm2iff.c, Copyright (c) 1995 by Gregg Wonderly. + * Rewritten for host-side cross-compilation using POSIX file I/O with + * explicit big-endian output instead of AmigaOS IFFParse library calls. + * + * Input: an XPM2 file, 1 char per pixel. + * Output: a BMAP IFF file readable by sys/amiga/winchar.c (tomb.iff). + */ + +#include +#include +#include +#include +#include + +/* ------------------------------------------------------------------ + * XPM screen descriptor and color translation table + * ------------------------------------------------------------------ */ + +static struct { + int Width; + int Height; + int Colors; + int BytesPerRow; +} XpmScreen; + +/* Translation table: indexed by the single XPM character code. + * slot = output palette index (0-based) + * flag = 1 if this entry is valid + * r,g,b = RGB components (0-255) */ +static struct { + unsigned char flag; + unsigned char r, g, b; + int slot; +} ttable[256]; + +/* ------------------------------------------------------------------ + * XPM parsing + * Adapted directly from sys/amiga/xpm2iff.c (already POSIX). + * ------------------------------------------------------------------ */ + +static FILE *xpmfh; + +#define XBUFSZ 2048 +static char xbuf[XBUFSZ]; + +/* Read the next quoted line from the XPM file. + * Returns a pointer to the content between the first pair of double-quotes, + * or NULL on EOF. Trailing ", and whitespace are stripped. */ +static char * +xpmgetline(void) +{ + char *bp; + do { + if (fgets(xbuf, XBUFSZ, xpmfh) == NULL) + return NULL; + } while (xbuf[0] != '"'); + + /* strip trailing <",> and whitespace */ + for (bp = xbuf; *bp; bp++) + ; + bp--; + while (isspace((unsigned char)*bp)) + bp--; + if (*bp == ',') + bp--; + if (*bp == '"') + bp--; + bp++; + *bp = '\0'; + + return &xbuf[1]; +} + +/* Open an XPM file and parse its header + color table. + * Populates XpmScreen and ttable[]. + * Returns 1 on success, 0 on failure. */ +static int +fopen_xpm_file(const char *fn) +{ + int temp; + char *xb; + + xpmfh = fopen(fn, "r"); + if (!xpmfh) + return 0; + + /* read dimensions header: "W H Colors 1" */ + xb = xpmgetline(); + if (!xb) + return 0; + if (sscanf(xb, "%d %d %d %d", + &XpmScreen.Width, &XpmScreen.Height, + &XpmScreen.Colors, &temp) != 4) + return 0; + if (temp != 1) { + fprintf(stderr, "xpm2iff_host: only 1 char/pixel XPM files supported\n"); + return 0; + } + + /* read color map: "%c c #rrggbb" */ + { + int ccount = 0; + while (ccount < XpmScreen.Colors) { + char idx; + int r, g, b; + xb = xpmgetline(); + if (!xb) + return 0; + if (sscanf(xb, "%c c #%2x%2x%2x", &idx, &r, &g, &b) != 4) { + fprintf(stderr, "xpm2iff_host: bad color entry: %s\n", xb); + return 0; + } + ttable[(unsigned char)idx].flag = 1; + ttable[(unsigned char)idx].r = (unsigned char)r; + ttable[(unsigned char)idx].g = (unsigned char)g; + ttable[(unsigned char)idx].b = (unsigned char)b; + ttable[(unsigned char)idx].slot = ccount; + ccount++; + } + } + return 1; +} + +/* ------------------------------------------------------------------ + * Bitplane packing + * ------------------------------------------------------------------ */ + +static char **planes; + +#define SETBIT(plane, plane_offset, col, value) \ + do { \ + if (value) \ + planes[plane][plane_offset + ((col) / 8)] \ + |= (char)(1 << (7 - ((col) & 7))); \ + } while (0) + +static void +conv_image(int nplanes) +{ + int row, col, planeno; + + for (row = 0; row < XpmScreen.Height; row++) { + char *xb = xpmgetline(); + int plane_offset; + if (!xb) + return; + plane_offset = row * XpmScreen.BytesPerRow; + for (col = 0; col < XpmScreen.Width; col++) { + int color = (unsigned char)xb[col]; + int slot; + if (!ttable[color].flag) { + fprintf(stderr, "xpm2iff_host: bad image data at row %d col %d\n", + row, col); + continue; + } + slot = ttable[color].slot; + for (planeno = 0; planeno < nplanes; planeno++) + SETBIT(planeno, plane_offset, col, slot & (1 << planeno)); + } + } +} + +/* ------------------------------------------------------------------ + * Big-endian IFF output helpers + * ------------------------------------------------------------------ */ + +static FILE *iff_out; + +static void +wr32(uint32_t v) +{ + fputc((v >> 24) & 0xff, iff_out); + fputc((v >> 16) & 0xff, iff_out); + fputc((v >> 8) & 0xff, iff_out); + fputc( v & 0xff, iff_out); +} + +static void +write_chunk(const char *id, const void *data, uint32_t size) +{ + fwrite(id, 1, 4, iff_out); + wr32(size); + fwrite(data, 1, size, iff_out); + if (size & 1) + fputc(0, iff_out); +} + +/* ------------------------------------------------------------------ + * main + * ------------------------------------------------------------------ */ + +int +main(int argc, char **argv) +{ + int i, nplanes, colors; + uint32_t pbytes, plne_size, form_size; + + if (argc != 3) { + fprintf(stderr, "Usage: %s source.xpm destination.iff\n", argv[0]); + return 1; + } + + if (!fopen_xpm_file(argv[1])) { + fprintf(stderr, "%s: failed to open or parse XPM file\n", argv[1]); + return 1; + } + + /* nplanes = ceil(log2(Colors)) */ + nplanes = 0; + i = XpmScreen.Colors - 1; + while (i > 0) { nplanes++; i >>= 1; } + + colors = 1 << nplanes; + + XpmScreen.BytesPerRow = ((XpmScreen.Width + 15) / 16) * 2; + pbytes = (uint32_t)XpmScreen.BytesPerRow * (uint32_t)XpmScreen.Height; + + /* Allocate zero-initialised bitplane buffers */ + planes = malloc(nplanes * sizeof(char *)); + if (!planes) { perror("malloc"); return 1; } + for (i = 0; i < nplanes; ++i) { + planes[i] = calloc(1, pbytes); + if (!planes[i]) { perror("calloc"); return 1; } + } + + /* Pack pixel data into bitplanes */ + conv_image(nplanes); + fclose(xpmfh); + + /* Open output IFF file */ + iff_out = fopen(argv[2], "wb"); + if (!iff_out) { perror(argv[2]); return 1; } + + /* Pre-compute FORM size: + * 4 (BMAP type tag) + * 8 + 20 (BMHD) + * 8 + 4 (CAMG) + * 8 + colors*3 (CMAP; colors is a power of 2, so colors*3 is even) + * 8 + 28 (PDAT: 7 x uint32_t) + * 8 + nplanes*pbytes (PLNE; pbytes is always even) + */ + plne_size = (uint32_t)nplanes * pbytes; + form_size = 4 + + (8 + 20) + + (8 + 4) + + (8 + (uint32_t)colors * 3) + + (8 + 28) + + (8 + plne_size); + + /* FORM header */ + fwrite("FORM", 1, 4, iff_out); + wr32(form_size); + fwrite("BMAP", 1, 4, iff_out); + + /* BMHD chunk */ + { + uint8_t bmhd[20]; + uint8_t *p = bmhd; + uint16_t w = (uint16_t)XpmScreen.Width; + uint16_t h = (uint16_t)XpmScreen.Height; + + p[0] = w >> 8; p[1] = w & 0xff; p += 2; /* w */ + p[0] = h >> 8; p[1] = h & 0xff; p += 2; /* h */ + memset(p, 0, 4); p += 4; /* x=0, y=0 */ + *p++ = (uint8_t)nplanes; /* nPlanes */ + *p++ = 0; /* masking: none */ + *p++ = 0; /* compression: none */ + *p++ = 0; /* reserved1 */ + p[0] = 0; p[1] = 0; p += 2; /* transparentColor */ + *p++ = 100; /* xAspect */ + *p++ = 100; /* yAspect */ + p[0] = 0; p[1] = 0; p += 2; /* pageWidth (not used) */ + p[0] = 0; p[1] = 0; p += 2; /* pageHeight (not used) */ + + write_chunk("BMHD", bmhd, 20); + } + + /* CAMG chunk: HIRES | LACE = 0x00008004 */ + { + uint8_t camg[4] = { 0x00, 0x00, 0x80, 0x04 }; + write_chunk("CAMG", camg, 4); + } + + /* CMAP chunk: built from ttable (no color reordering for XPM images) */ + { + uint8_t *cmap = calloc(colors, 3); + if (!cmap) { perror("calloc"); return 1; } + for (i = 0; i < 256; i++) { + if (ttable[i].flag) { + int s = ttable[i].slot; + cmap[s * 3 + 0] = ttable[i].r; + cmap[s * 3 + 1] = ttable[i].g; + cmap[s * 3 + 2] = ttable[i].b; + } + } + write_chunk("CMAP", cmap, (uint32_t)colors * 3); + free(cmap); + } + + /* PDAT chunk: 7 x uint32_t big-endian + * nplanes, pbytes, across=0, down=0, npics=1, xsize=Width, ysize=Height */ + { + uint32_t vals[7]; + uint8_t pdat[28]; + uint8_t *p = pdat; + + vals[0] = (uint32_t)nplanes; + vals[1] = pbytes; + vals[2] = 0; /* across: not a tile sheet */ + vals[3] = 0; /* down */ + vals[4] = 1; /* npics */ + vals[5] = (uint32_t)XpmScreen.Width; + vals[6] = (uint32_t)XpmScreen.Height; + + for (i = 0; i < 7; i++) { + p[0] = (vals[i] >> 24) & 0xff; + p[1] = (vals[i] >> 16) & 0xff; + p[2] = (vals[i] >> 8) & 0xff; + p[3] = vals[i] & 0xff; + p += 4; + } + write_chunk("PDAT", pdat, 28); + } + + /* PLNE chunk: concatenated bitplane data */ + fwrite("PLNE", 1, 4, iff_out); + wr32(plne_size); + for (i = 0; i < nplanes; ++i) + fwrite(planes[i], 1, pbytes, iff_out); + if (plne_size & 1) + fputc(0, iff_out); + + fclose(iff_out); + + for (i = 0; i < nplanes; ++i) free(planes[i]); + free(planes); + + printf("tomb.iff: %dx%d, %d colors (%d planes), %u bytes/plane\n", + XpmScreen.Width, XpmScreen.Height, colors, nplanes, + (unsigned)pbytes); + return 0; +} diff --git a/sys/unix/hints/include/cross-pre2.370 b/sys/unix/hints/include/cross-pre2.370 index eca47550f..d27301af1 100644 --- a/sys/unix/hints/include/cross-pre2.370 +++ b/sys/unix/hints/include/cross-pre2.370 @@ -470,8 +470,8 @@ ifdef CROSS_TO_AMIGA # # Cross-compiler: https://franke.ms/git/bebbo/amiga-gcc # Install to /opt/amiga, then: -# make fetch-lua # sys/unix/setup.sh sys/unix/hints/linux.370 +# make fetch-lua # make CROSS_TO_AMIGA=1 all # make CROSS_TO_AMIGA=1 package #================================================================= From c5dfd1fc5c0d1f261baf12d0e81e282a44e7b183 Mon Sep 17 00:00:00 2001 From: Ingo Paschke Date: Tue, 24 Mar 2026 01:21:00 +0100 Subject: [PATCH 343/442] Fetch BSD regex from GitHub at build time Replace bundled Spencer regex with a fetch-regex build step that clones https://github.com/garyhouston/regex.git, generates the required .ih and regex.h headers via mkh, and copies the result into sys/amiga/regex/. Usage: make CROSS_TO_AMIGA=1 fetch-regex The fetched sources are not tracked in git. --- .gitignore | 2 + sys/amiga/README.amiga | 1 + sys/amiga/regex/cclass.h | 72 -- sys/amiga/regex/cname.h | 143 --- sys/amiga/regex/engine.h | 1093 ---------------- sys/amiga/regex/regcomp.c | 1704 ------------------------- sys/amiga/regex/regerror.c | 186 --- sys/amiga/regex/regex2.h | 175 --- sys/amiga/regex/regexec.c | 187 --- sys/amiga/regex/regfree.c | 86 -- sys/amiga/regex/utils.h | 59 - sys/unix/hints/include/cross-post.370 | 34 +- sys/unix/hints/include/cross-pre2.370 | 1 + 13 files changed, 37 insertions(+), 3706 deletions(-) delete mode 100755 sys/amiga/regex/cclass.h delete mode 100755 sys/amiga/regex/cname.h delete mode 100755 sys/amiga/regex/engine.h delete mode 100755 sys/amiga/regex/regcomp.c delete mode 100755 sys/amiga/regex/regerror.c delete mode 100755 sys/amiga/regex/regex2.h delete mode 100755 sys/amiga/regex/regexec.c delete mode 100755 sys/amiga/regex/regfree.c delete mode 100755 sys/amiga/regex/utils.h diff --git a/.gitignore b/.gitignore index f5a18b7e3..68c544da6 100644 --- a/.gitignore +++ b/.gitignore @@ -96,3 +96,5 @@ bundle/* util/*.lib util/*.exp submodules/CHKSUMS.tmp +sys/amiga/regex/ +sys/amiga/regex/ diff --git a/sys/amiga/README.amiga b/sys/amiga/README.amiga index 8eda92c31..6aa2a2a95 100644 --- a/sys/amiga/README.amiga +++ b/sys/amiga/README.amiga @@ -75,6 +75,7 @@ Cross-compilation from Linux using bebbo's m68k-amigaos-gcc toolchain cd NetHack sys/unix/setup.sh sys/unix/hints/linux.370 make fetch-lua + make CROSS_TO_AMIGA=1 fetch-regex make CROSS_TO_AMIGA=1 all make CROSS_TO_AMIGA=1 package diff --git a/sys/amiga/regex/cclass.h b/sys/amiga/regex/cclass.h deleted file mode 100755 index 52cd47b8f..000000000 --- a/sys/amiga/regex/cclass.h +++ /dev/null @@ -1,72 +0,0 @@ -/* $NetBSD: cclass.h,v 1.3 1995/02/27 13:28:29 cgd Exp $ */ - -/*- - * Copyright (c) 1992, 1993, 1994 Henry Spencer. - * Copyright (c) 1992, 1993, 1994 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Henry Spencer. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)cclass.h 8.3 (Berkeley) 3/20/94 - */ - -/* character-class table */ -static struct cclass { - char *name; - char *chars; - char *multis; -} cclasses[] = { - "alnum", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\ -0123456789", "", - "alpha", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", - "", - "blank", " \t", "", - "cntrl", "\007\b\t\n\v\f\r\1\2\3\4\5\6\16\17\20\21\22\23\24\ -\25\26\27\30\31\32\33\34\35\36\37\177", "", - "digit", "0123456789", "", - "graph", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\ -0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", - "", - "lower", "abcdefghijklmnopqrstuvwxyz", - "", - "print", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\ -0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ ", - "", - "punct", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", - "", - "space", "\t\n\v\f\r ", "", - "upper", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", - "", - "xdigit", "0123456789ABCDEFabcdef", - "", - NULL, 0, "" -}; diff --git a/sys/amiga/regex/cname.h b/sys/amiga/regex/cname.h deleted file mode 100755 index 50479db08..000000000 --- a/sys/amiga/regex/cname.h +++ /dev/null @@ -1,143 +0,0 @@ -/* $NetBSD: cname.h,v 1.3 1995/02/27 13:28:33 cgd Exp $ */ - -/*- - * Copyright (c) 1992, 1993, 1994 Henry Spencer. - * Copyright (c) 1992, 1993, 1994 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Henry Spencer. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)cname.h 8.3 (Berkeley) 3/20/94 - */ - -/* character-name table */ -static struct cname { - char *name; - char code; -} cnames[] = { - "NUL", '\0', - "SOH", '\001', - "STX", '\002', - "ETX", '\003', - "EOT", '\004', - "ENQ", '\005', - "ACK", '\006', - "BEL", '\007', - "alert", '\007', - "BS", '\010', - "backspace", '\b', - "HT", '\011', - "tab", '\t', - "LF", '\012', - "newline", '\n', - "VT", '\013', - "vertical-tab", '\v', - "FF", '\014', - "form-feed", '\f', - "CR", '\015', - "carriage-return", '\r', - "SO", '\016', - "SI", '\017', - "DLE", '\020', - "DC1", '\021', - "DC2", '\022', - "DC3", '\023', - "DC4", '\024', - "NAK", '\025', - "SYN", '\026', - "ETB", '\027', - "CAN", '\030', - "EM", '\031', - "SUB", '\032', - "ESC", '\033', - "IS4", '\034', - "FS", '\034', - "IS3", '\035', - "GS", '\035', - "IS2", '\036', - "RS", '\036', - "IS1", '\037', - "US", '\037', - "space", ' ', - "exclamation-mark", '!', - "quotation-mark", '"', - "number-sign", '#', - "dollar-sign", '$', - "percent-sign", '%', - "ampersand", '&', - "apostrophe", '\'', - "left-parenthesis", '(', - "right-parenthesis", ')', - "asterisk", '*', - "plus-sign", '+', - "comma", ',', - "hyphen", '-', - "hyphen-minus", '-', - "period", '.', - "full-stop", '.', - "slash", '/', - "solidus", '/', - "zero", '0', - "one", '1', - "two", '2', - "three", '3', - "four", '4', - "five", '5', - "six", '6', - "seven", '7', - "eight", '8', - "nine", '9', - "colon", ':', - "semicolon", ';', - "less-than-sign", '<', - "equals-sign", '=', - "greater-than-sign", '>', - "question-mark", '?', - "commercial-at", '@', - "left-square-bracket", '[', - "backslash", '\\', - "reverse-solidus", '\\', - "right-square-bracket", ']', - "circumflex", '^', - "circumflex-accent", '^', - "underscore", '_', - "low-line", '_', - "grave-accent", '`', - "left-brace", '{', - "left-curly-bracket", '{', - "vertical-line", '|', - "right-brace", '}', - "right-curly-bracket", '}', - "tilde", '~', - "DEL", '\177', - NULL, 0, -}; diff --git a/sys/amiga/regex/engine.h b/sys/amiga/regex/engine.h deleted file mode 100755 index 93f6842ac..000000000 --- a/sys/amiga/regex/engine.h +++ /dev/null @@ -1,1093 +0,0 @@ -/* $NetBSD: engine.c,v 1.5 1995/02/27 13:28:39 cgd Exp $ */ - -/*- - * Copyright (c) 1992, 1993, 1994 Henry Spencer. - * Copyright (c) 1992, 1993, 1994 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Henry Spencer. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)engine.c 8.5 (Berkeley) 3/20/94 - */ - -/* - * The matching engine and friends. This file is #included by regexec.c - * after suitable #defines of a variety of macros used herein, so that - * different state representations can be used without duplicating masses - * of code. - */ - -#ifdef SNAMES -#define matcher smatcher -#define fast sfast -#define slow sslow -#define dissect sdissect -#define backref sbackref -#define step sstep -#define print sprint -#define at sat -#define match smat -#endif -#ifdef LNAMES -#define matcher lmatcher -#define fast lfast -#define slow lslow -#define dissect ldissect -#define backref lbackref -#define step lstep -#define print lprint -#define at lat -#define match lmat -#endif - -/* another structure passed up and down to avoid zillions of parameters */ -struct match { - struct re_guts *g; - int eflags; - regmatch_t *pmatch; /* [nsub+1] (0 element unused) */ - char *offp; /* offsets work from here */ - char *beginp; /* start of string -- virtual NUL precedes */ - char *endp; /* end of string -- virtual NUL here */ - char *coldp; /* can be no match starting before here */ - char **lastpos; /* [nplus+1] */ - STATEVARS; - states st; /* current states */ - states fresh; /* states for a fresh start */ - states tmp; /* temporary */ - states empty; /* empty set of states */ -}; - -/* ========= begin header generated by ./mkh ========= */ -#ifdef __cplusplus -extern "C" { -#endif - -/* === engine.c === */ -static int matcher __P((struct re_guts *g, char *string, size_t nmatch, regmatch_t pmatch[], int eflags)); -static char *dissect __P((struct match *m, char *start, char *stop, sopno startst, sopno stopst)); -static char *backref __P((struct match *m, char *start, char *stop, sopno startst, sopno stopst, sopno lev)); -static char *fast __P((struct match *m, char *start, char *stop, sopno startst, sopno stopst)); -static char *slow __P((struct match *m, char *start, char *stop, sopno startst, sopno stopst)); -static states step __P((struct re_guts *g, sopno start, sopno stop, states bef, int ch, states aft)); -#define BOL (OUT+1) -#define EOL (BOL+1) -#define BOLEOL (BOL+2) -#define NOTHING (BOL+3) -#define BOW (BOL+4) -#define EOW (BOL+5) -#define CODEMAX (BOL+5) /* highest code used */ -#define NONCHAR(c) ((c) > CHAR_MAX) -#define NNONCHAR (CODEMAX-CHAR_MAX) -#ifdef REDEBUG -static void print __P((struct match *m, char *caption, states st, int ch, FILE *d)); -#endif -#ifdef REDEBUG -static void at __P((struct match *m, char *title, char *start, char *stop, sopno startst, sopno stopst)); -#endif -#ifdef REDEBUG -static char *pchar __P((int ch)); -#endif - -#ifdef __cplusplus -} -#endif -/* ========= end header generated by ./mkh ========= */ - -#ifdef REDEBUG -#define SP(t, s, c) print(m, t, s, c, stdout) -#define AT(t, p1, p2, s1, s2) at(m, t, p1, p2, s1, s2) -#define NOTE(str) { if (m->eflags®_TRACE) printf("=%s\n", (str)); } -#else -#define SP(t, s, c) /* nothing */ -#define AT(t, p1, p2, s1, s2) /* nothing */ -#define NOTE(s) /* nothing */ -#endif - -/* - - matcher - the actual matching engine - == static int matcher(register struct re_guts *g, char *string, \ - == size_t nmatch, regmatch_t pmatch[], int eflags); - */ -static int /* 0 success, REG_NOMATCH failure */ -matcher(g, string, nmatch, pmatch, eflags) -register struct re_guts *g; -char *string; -size_t nmatch; -regmatch_t pmatch[]; -int eflags; -{ - register char *endp; - register int i; - struct match mv; - register struct match *m = &mv; - register char *dp; - const register sopno gf = g->firststate+1; /* +1 for OEND */ - const register sopno gl = g->laststate; - char *start; - char *stop; - - /* simplify the situation where possible */ - if (g->cflags®_NOSUB) - nmatch = 0; - if (eflags®_STARTEND) { - start = string + pmatch[0].rm_so; - stop = string + pmatch[0].rm_eo; - } else { - start = string; - stop = start + strlen(start); - } - if (stop < start) - return(REG_INVARG); - - /* prescreening; this does wonders for this rather slow code */ - if (g->must != NULL) { - for (dp = start; dp < stop; dp++) - if (*dp == g->must[0] && stop - dp >= g->mlen && - memcmp(dp, g->must, (size_t)g->mlen) == 0) - break; - if (dp == stop) /* we didn't find g->must */ - return(REG_NOMATCH); - } - - /* match struct setup */ - m->g = g; - m->eflags = eflags; - m->pmatch = NULL; - m->lastpos = NULL; - m->offp = string; - m->beginp = start; - m->endp = stop; - STATESETUP(m, 4); - SETUP(m->st); - SETUP(m->fresh); - SETUP(m->tmp); - SETUP(m->empty); - CLEAR(m->empty); - - /* this loop does only one repetition except for backrefs */ - for (;;) { - endp = fast(m, start, stop, gf, gl); - if (endp == NULL) { /* a miss */ - STATETEARDOWN(m); - return(REG_NOMATCH); - } - if (nmatch == 0 && !g->backrefs) - break; /* no further info needed */ - - /* where? */ - assert(m->coldp != NULL); - for (;;) { - NOTE("finding start"); - endp = slow(m, m->coldp, stop, gf, gl); - if (endp != NULL) - break; - assert(m->coldp < m->endp); - m->coldp++; - } - if (nmatch == 1 && !g->backrefs) - break; /* no further info needed */ - - /* oh my, he wants the subexpressions... */ - if (m->pmatch == NULL) - m->pmatch = (regmatch_t *)malloc((m->g->nsub + 1) * - sizeof(regmatch_t)); - if (m->pmatch == NULL) { - STATETEARDOWN(m); - return(REG_ESPACE); - } - for (i = 1; i <= m->g->nsub; i++) - m->pmatch[i].rm_so = m->pmatch[i].rm_eo = -1; - if (!g->backrefs && !(m->eflags®_BACKR)) { - NOTE("dissecting"); - dp = dissect(m, m->coldp, endp, gf, gl); - } else { - if (g->nplus > 0 && m->lastpos == NULL) - m->lastpos = (char **)malloc((g->nplus+1) * - sizeof(char *)); - if (g->nplus > 0 && m->lastpos == NULL) { - free(m->pmatch); - STATETEARDOWN(m); - return(REG_ESPACE); - } - NOTE("backref dissect"); - dp = backref(m, m->coldp, endp, gf, gl, (sopno)0); - } - if (dp != NULL) - break; - - /* uh-oh... we couldn't find a subexpression-level match */ - assert(g->backrefs); /* must be back references doing it */ - assert(g->nplus == 0 || m->lastpos != NULL); - for (;;) { - if (dp != NULL || endp <= m->coldp) - break; /* defeat */ - NOTE("backoff"); - endp = slow(m, m->coldp, endp-1, gf, gl); - if (endp == NULL) - break; /* defeat */ - /* try it on a shorter possibility */ -#ifndef NDEBUG - for (i = 1; i <= m->g->nsub; i++) { - assert(m->pmatch[i].rm_so == -1); - assert(m->pmatch[i].rm_eo == -1); - } -#endif - NOTE("backoff dissect"); - dp = backref(m, m->coldp, endp, gf, gl, (sopno)0); - } - assert(dp == NULL || dp == endp); - if (dp != NULL) /* found a shorter one */ - break; - - /* despite initial appearances, there is no match here */ - NOTE("false alarm"); - start = m->coldp + 1; /* recycle starting later */ - assert(start <= stop); - } - - /* fill in the details if requested */ - if (nmatch > 0) { - pmatch[0].rm_so = m->coldp - m->offp; - pmatch[0].rm_eo = endp - m->offp; - } - if (nmatch > 1) { - assert(m->pmatch != NULL); - for (i = 1; i < nmatch; i++) - if (i <= m->g->nsub) - pmatch[i] = m->pmatch[i]; - else { - pmatch[i].rm_so = -1; - pmatch[i].rm_eo = -1; - } - } - - if (m->pmatch != NULL) - free((char *)m->pmatch); - if (m->lastpos != NULL) - free((char *)m->lastpos); - STATETEARDOWN(m); - return(0); -} - -/* - - dissect - figure out what matched what, no back references - == static char *dissect(register struct match *m, char *start, \ - == char *stop, sopno startst, sopno stopst); - */ -static char * /* == stop (success) always */ -dissect(m, start, stop, startst, stopst) -register struct match *m; -char *start; -char *stop; -sopno startst; -sopno stopst; -{ - register int i; - register sopno ss; /* start sop of current subRE */ - register sopno es; /* end sop of current subRE */ - register char *sp; /* start of string matched by it */ - register char *stp; /* string matched by it cannot pass here */ - register char *rest; /* start of rest of string */ - register char *tail; /* string unmatched by rest of RE */ - register sopno ssub; /* start sop of subsubRE */ - register sopno esub; /* end sop of subsubRE */ - register char *ssp; /* start of string matched by subsubRE */ - register char *sep; /* end of string matched by subsubRE */ - register char *oldssp; /* previous ssp */ - register char *dp; - - AT("diss", start, stop, startst, stopst); - sp = start; - for (ss = startst; ss < stopst; ss = es) { - /* identify end of subRE */ - es = ss; - switch (OP(m->g->strip[es])) { - case OPLUS_: - case OQUEST_: - es += OPND(m->g->strip[es]); - break; - case OCH_: - while (OP(m->g->strip[es]) != O_CH) - es += OPND(m->g->strip[es]); - break; - } - es++; - - /* figure out what it matched */ - switch (OP(m->g->strip[ss])) { - case OEND: - assert(nope); - break; - case OCHAR: - sp++; - break; - case OBOL: - case OEOL: - case OBOW: - case OEOW: - break; - case OANY: - case OANYOF: - sp++; - break; - case OBACK_: - case O_BACK: - assert(nope); - break; - /* cases where length of match is hard to find */ - case OQUEST_: - stp = stop; - for (;;) { - /* how long could this one be? */ - rest = slow(m, sp, stp, ss, es); - assert(rest != NULL); /* it did match */ - /* could the rest match the rest? */ - tail = slow(m, rest, stop, es, stopst); - if (tail == stop) - break; /* yes! */ - /* no -- try a shorter match for this one */ - stp = rest - 1; - assert(stp >= sp); /* it did work */ - } - ssub = ss + 1; - esub = es - 1; - /* did innards match? */ - if (slow(m, sp, rest, ssub, esub) != NULL) { - dp = dissect(m, sp, rest, ssub, esub); - assert(dp == rest); - } else /* no */ - assert(sp == rest); - sp = rest; - break; - case OPLUS_: - stp = stop; - for (;;) { - /* how long could this one be? */ - rest = slow(m, sp, stp, ss, es); - assert(rest != NULL); /* it did match */ - /* could the rest match the rest? */ - tail = slow(m, rest, stop, es, stopst); - if (tail == stop) - break; /* yes! */ - /* no -- try a shorter match for this one */ - stp = rest - 1; - assert(stp >= sp); /* it did work */ - } - ssub = ss + 1; - esub = es - 1; - ssp = sp; - oldssp = ssp; - for (;;) { /* find last match of innards */ - sep = slow(m, ssp, rest, ssub, esub); - if (sep == NULL || sep == ssp) - break; /* failed or matched null */ - oldssp = ssp; /* on to next try */ - ssp = sep; - } - if (sep == NULL) { - /* last successful match */ - sep = ssp; - ssp = oldssp; - } - assert(sep == rest); /* must exhaust substring */ - assert(slow(m, ssp, sep, ssub, esub) == rest); - dp = dissect(m, ssp, sep, ssub, esub); - assert(dp == sep); - sp = rest; - break; - case OCH_: - stp = stop; - for (;;) { - /* how long could this one be? */ - rest = slow(m, sp, stp, ss, es); - assert(rest != NULL); /* it did match */ - /* could the rest match the rest? */ - tail = slow(m, rest, stop, es, stopst); - if (tail == stop) - break; /* yes! */ - /* no -- try a shorter match for this one */ - stp = rest - 1; - assert(stp >= sp); /* it did work */ - } - ssub = ss + 1; - esub = ss + OPND(m->g->strip[ss]) - 1; - assert(OP(m->g->strip[esub]) == OOR1); - for (;;) { /* find first matching branch */ - if (slow(m, sp, rest, ssub, esub) == rest) - break; /* it matched all of it */ - /* that one missed, try next one */ - assert(OP(m->g->strip[esub]) == OOR1); - esub++; - assert(OP(m->g->strip[esub]) == OOR2); - ssub = esub + 1; - esub += OPND(m->g->strip[esub]); - if (OP(m->g->strip[esub]) == OOR2) - esub--; - else - assert(OP(m->g->strip[esub]) == O_CH); - } - dp = dissect(m, sp, rest, ssub, esub); - assert(dp == rest); - sp = rest; - break; - case O_PLUS: - case O_QUEST: - case OOR1: - case OOR2: - case O_CH: - assert(nope); - break; - case OLPAREN: - i = OPND(m->g->strip[ss]); - assert(0 < i && i <= m->g->nsub); - m->pmatch[i].rm_so = sp - m->offp; - break; - case ORPAREN: - i = OPND(m->g->strip[ss]); - assert(0 < i && i <= m->g->nsub); - m->pmatch[i].rm_eo = sp - m->offp; - break; - default: /* uh oh */ - assert(nope); - break; - } - } - - assert(sp == stop); - return(sp); -} - -/* - - backref - figure out what matched what, figuring in back references - == static char *backref(register struct match *m, char *start, \ - == char *stop, sopno startst, sopno stopst, sopno lev); - */ -static char * /* == stop (success) or NULL (failure) */ -backref(m, start, stop, startst, stopst, lev) -register struct match *m; -char *start; -char *stop; -sopno startst; -sopno stopst; -sopno lev; /* PLUS nesting level */ -{ - register int i; - register sopno ss; /* start sop of current subRE */ - register char *sp; /* start of string matched by it */ - register sopno ssub; /* start sop of subsubRE */ - register sopno esub; /* end sop of subsubRE */ - register char *ssp; /* start of string matched by subsubRE */ - register char *dp; - register size_t len; - register int hard; - register sop s; - register regoff_t offsave; - register cset *cs; - - AT("back", start, stop, startst, stopst); - sp = start; - - /* get as far as we can with easy stuff */ - hard = 0; - for (ss = startst; !hard && ss < stopst; ss++) - switch (OP(s = m->g->strip[ss])) { - case OCHAR: - if (sp == stop || *sp++ != (char)OPND(s)) - return(NULL); - break; - case OANY: - if (sp == stop) - return(NULL); - sp++; - break; - case OANYOF: - cs = &m->g->sets[OPND(s)]; - if (sp == stop || !CHIN(cs, *sp++)) - return(NULL); - break; - case OBOL: - if ( (sp == m->beginp && !(m->eflags®_NOTBOL)) || - (sp < m->endp && *(sp-1) == '\n' && - (m->g->cflags®_NEWLINE)) ) - { /* yes */ } - else - return(NULL); - break; - case OEOL: - if ( (sp == m->endp && !(m->eflags®_NOTEOL)) || - (sp < m->endp && *sp == '\n' && - (m->g->cflags®_NEWLINE)) ) - { /* yes */ } - else - return(NULL); - break; - case OBOW: - if (( (sp == m->beginp && !(m->eflags®_NOTBOL)) || - (sp < m->endp && *(sp-1) == '\n' && - (m->g->cflags®_NEWLINE)) || - (sp > m->beginp && - !ISWORD(*(sp-1))) ) && - (sp < m->endp && ISWORD(*sp)) ) - { /* yes */ } - else - return(NULL); - break; - case OEOW: - if (( (sp == m->endp && !(m->eflags®_NOTEOL)) || - (sp < m->endp && *sp == '\n' && - (m->g->cflags®_NEWLINE)) || - (sp < m->endp && !ISWORD(*sp)) ) && - (sp > m->beginp && ISWORD(*(sp-1))) ) - { /* yes */ } - else - return(NULL); - break; - case O_QUEST: - break; - case OOR1: /* matches null but needs to skip */ - ss++; - s = m->g->strip[ss]; - do { - assert(OP(s) == OOR2); - ss += OPND(s); - } while (OP(s = m->g->strip[ss]) != O_CH); - /* note that the ss++ gets us past the O_CH */ - break; - default: /* have to make a choice */ - hard = 1; - break; - } - if (!hard) { /* that was it! */ - if (sp != stop) - return(NULL); - return(sp); - } - ss--; /* adjust for the for's final increment */ - - /* the hard stuff */ - AT("hard", sp, stop, ss, stopst); - s = m->g->strip[ss]; - switch (OP(s)) { - case OBACK_: /* the vilest depths */ - i = OPND(s); - assert(0 < i && i <= m->g->nsub); - if (m->pmatch[i].rm_eo == -1) - return(NULL); - assert(m->pmatch[i].rm_so != -1); - len = m->pmatch[i].rm_eo - m->pmatch[i].rm_so; - assert(stop - m->beginp >= len); - if (sp > stop - len) - return(NULL); /* not enough left to match */ - ssp = m->offp + m->pmatch[i].rm_so; - if (memcmp(sp, ssp, len) != 0) - return(NULL); - while (m->g->strip[ss] != SOP(O_BACK, i)) - ss++; - return(backref(m, sp+len, stop, ss+1, stopst, lev)); - break; - case OQUEST_: /* to null or not */ - dp = backref(m, sp, stop, ss+1, stopst, lev); - if (dp != NULL) - return(dp); /* not */ - return(backref(m, sp, stop, ss+OPND(s)+1, stopst, lev)); - break; - case OPLUS_: - assert(m->lastpos != NULL); - assert(lev+1 <= m->g->nplus); - m->lastpos[lev+1] = sp; - return(backref(m, sp, stop, ss+1, stopst, lev+1)); - break; - case O_PLUS: - if (sp == m->lastpos[lev]) /* last pass matched null */ - return(backref(m, sp, stop, ss+1, stopst, lev-1)); - /* try another pass */ - m->lastpos[lev] = sp; - dp = backref(m, sp, stop, ss-OPND(s)+1, stopst, lev); - if (dp == NULL) - return(backref(m, sp, stop, ss+1, stopst, lev-1)); - else - return(dp); - break; - case OCH_: /* find the right one, if any */ - ssub = ss + 1; - esub = ss + OPND(s) - 1; - assert(OP(m->g->strip[esub]) == OOR1); - for (;;) { /* find first matching branch */ - dp = backref(m, sp, stop, ssub, esub, lev); - if (dp != NULL) - return(dp); - /* that one missed, try next one */ - if (OP(m->g->strip[esub]) == O_CH) - return(NULL); /* there is none */ - esub++; - assert(OP(m->g->strip[esub]) == OOR2); - ssub = esub + 1; - esub += OPND(m->g->strip[esub]); - if (OP(m->g->strip[esub]) == OOR2) - esub--; - else - assert(OP(m->g->strip[esub]) == O_CH); - } - break; - case OLPAREN: /* must undo assignment if rest fails */ - i = OPND(s); - assert(0 < i && i <= m->g->nsub); - offsave = m->pmatch[i].rm_so; - m->pmatch[i].rm_so = sp - m->offp; - dp = backref(m, sp, stop, ss+1, stopst, lev); - if (dp != NULL) - return(dp); - m->pmatch[i].rm_so = offsave; - return(NULL); - break; - case ORPAREN: /* must undo assignment if rest fails */ - i = OPND(s); - assert(0 < i && i <= m->g->nsub); - offsave = m->pmatch[i].rm_eo; - m->pmatch[i].rm_eo = sp - m->offp; - dp = backref(m, sp, stop, ss+1, stopst, lev); - if (dp != NULL) - return(dp); - m->pmatch[i].rm_eo = offsave; - return(NULL); - break; - default: /* uh oh */ - assert(nope); - break; - } - - /* "can't happen" */ - assert(nope); - /* NOTREACHED */ -} - -/* - - fast - step through the string at top speed - == static char *fast(register struct match *m, char *start, \ - == char *stop, sopno startst, sopno stopst); - */ -static char * /* where tentative match ended, or NULL */ -fast(m, start, stop, startst, stopst) -register struct match *m; -char *start; -char *stop; -sopno startst; -sopno stopst; -{ - register states st = m->st; - register states fresh = m->fresh; - register states tmp = m->tmp; - register char *p = start; - register int c = (start == m->beginp) ? OUT : *(start-1); - register int lastc; /* previous c */ - register int flagch; - register int i; - register char *coldp; /* last p after which no match was underway */ - - CLEAR(st); - SET1(st, startst); - st = step(m->g, startst, stopst, st, NOTHING, st); - ASSIGN(fresh, st); - SP("start", st, *p); - coldp = NULL; - for (;;) { - /* next character */ - lastc = c; - c = (p == m->endp) ? OUT : *p; - if (EQ(st, fresh)) - coldp = p; - - /* is there an EOL and/or BOL between lastc and c? */ - flagch = '\0'; - i = 0; - if ( (lastc == '\n' && m->g->cflags®_NEWLINE) || - (lastc == OUT && !(m->eflags®_NOTBOL)) ) { - flagch = BOL; - i = m->g->nbol; - } - if ( (c == '\n' && m->g->cflags®_NEWLINE) || - (c == OUT && !(m->eflags®_NOTEOL)) ) { - flagch = (flagch == BOL) ? BOLEOL : EOL; - i += m->g->neol; - } - if (i != 0) { - for (; i > 0; i--) - st = step(m->g, startst, stopst, st, flagch, st); - SP("boleol", st, c); - } - - /* how about a word boundary? */ - if ( (flagch == BOL || (lastc != OUT && !ISWORD(lastc))) && - (c != OUT && ISWORD(c)) ) { - flagch = BOW; - } - if ( (lastc != OUT && ISWORD(lastc)) && - (flagch == EOL || (c != OUT && !ISWORD(c))) ) { - flagch = EOW; - } - if (flagch == BOW || flagch == EOW) { - st = step(m->g, startst, stopst, st, flagch, st); - SP("boweow", st, c); - } - - /* are we done? */ - if (ISSET(st, stopst) || p == stop) - break; /* NOTE BREAK OUT */ - - /* no, we must deal with this character */ - ASSIGN(tmp, st); - ASSIGN(st, fresh); - assert(c != OUT); - st = step(m->g, startst, stopst, tmp, c, st); - SP("aft", st, c); - assert(EQ(step(m->g, startst, stopst, st, NOTHING, st), st)); - p++; - } - - assert(coldp != NULL); - m->coldp = coldp; - if (ISSET(st, stopst)) - return(p+1); - else - return(NULL); -} - -/* - - slow - step through the string more deliberately - == static char *slow(register struct match *m, char *start, \ - == char *stop, sopno startst, sopno stopst); - */ -static char * /* where it ended */ -slow(m, start, stop, startst, stopst) -register struct match *m; -char *start; -char *stop; -sopno startst; -sopno stopst; -{ - register states st = m->st; - register states empty = m->empty; - register states tmp = m->tmp; - register char *p = start; - register int c = (start == m->beginp) ? OUT : *(start-1); - register int lastc; /* previous c */ - register int flagch; - register int i; - register char *matchp; /* last p at which a match ended */ - - AT("slow", start, stop, startst, stopst); - CLEAR(st); - SET1(st, startst); - SP("sstart", st, *p); - st = step(m->g, startst, stopst, st, NOTHING, st); - matchp = NULL; - for (;;) { - /* next character */ - lastc = c; - c = (p == m->endp) ? OUT : *p; - - /* is there an EOL and/or BOL between lastc and c? */ - flagch = '\0'; - i = 0; - if ( (lastc == '\n' && m->g->cflags®_NEWLINE) || - (lastc == OUT && !(m->eflags®_NOTBOL)) ) { - flagch = BOL; - i = m->g->nbol; - } - if ( (c == '\n' && m->g->cflags®_NEWLINE) || - (c == OUT && !(m->eflags®_NOTEOL)) ) { - flagch = (flagch == BOL) ? BOLEOL : EOL; - i += m->g->neol; - } - if (i != 0) { - for (; i > 0; i--) - st = step(m->g, startst, stopst, st, flagch, st); - SP("sboleol", st, c); - } - - /* how about a word boundary? */ - if ( (flagch == BOL || (lastc != OUT && !ISWORD(lastc))) && - (c != OUT && ISWORD(c)) ) { - flagch = BOW; - } - if ( (lastc != OUT && ISWORD(lastc)) && - (flagch == EOL || (c != OUT && !ISWORD(c))) ) { - flagch = EOW; - } - if (flagch == BOW || flagch == EOW) { - st = step(m->g, startst, stopst, st, flagch, st); - SP("sboweow", st, c); - } - - /* are we done? */ - if (ISSET(st, stopst)) - matchp = p; - if (EQ(st, empty) || p == stop) - break; /* NOTE BREAK OUT */ - - /* no, we must deal with this character */ - ASSIGN(tmp, st); - ASSIGN(st, empty); - assert(c != OUT); - st = step(m->g, startst, stopst, tmp, c, st); - SP("saft", st, c); - assert(EQ(step(m->g, startst, stopst, st, NOTHING, st), st)); - p++; - } - - return(matchp); -} - - -/* - - step - map set of states reachable before char to set reachable after - == static states step(register struct re_guts *g, sopno start, sopno stop, \ - == register states bef, int ch, register states aft); - == #define BOL (OUT+1) - == #define EOL (BOL+1) - == #define BOLEOL (BOL+2) - == #define NOTHING (BOL+3) - == #define BOW (BOL+4) - == #define EOW (BOL+5) - == #define CODEMAX (BOL+5) // highest code used - == #define NONCHAR(c) ((c) > CHAR_MAX) - == #define NNONCHAR (CODEMAX-CHAR_MAX) - */ -static states -step(g, start, stop, bef, ch, aft) -register struct re_guts *g; -sopno start; /* start state within strip */ -sopno stop; /* state after stop state within strip */ -register states bef; /* states reachable before */ -int ch; /* character or NONCHAR code */ -register states aft; /* states already known reachable after */ -{ - register cset *cs; - register sop s; - register sopno pc; - register onestate here; /* note, macros know this name */ - register sopno look; - register int i; - - for (pc = start, INIT(here, pc); pc != stop; pc++, INC(here)) { - s = g->strip[pc]; - switch (OP(s)) { - case OEND: - assert(pc == stop-1); - break; - case OCHAR: - /* only characters can match */ - assert(!NONCHAR(ch) || ch != (char)OPND(s)); - if (ch == (char)OPND(s)) - FWD(aft, bef, 1); - break; - case OBOL: - if (ch == BOL || ch == BOLEOL) - FWD(aft, bef, 1); - break; - case OEOL: - if (ch == EOL || ch == BOLEOL) - FWD(aft, bef, 1); - break; - case OBOW: - if (ch == BOW) - FWD(aft, bef, 1); - break; - case OEOW: - if (ch == EOW) - FWD(aft, bef, 1); - break; - case OANY: - if (!NONCHAR(ch)) - FWD(aft, bef, 1); - break; - case OANYOF: - cs = &g->sets[OPND(s)]; - if (!NONCHAR(ch) && CHIN(cs, ch)) - FWD(aft, bef, 1); - break; - case OBACK_: /* ignored here */ - case O_BACK: - FWD(aft, aft, 1); - break; - case OPLUS_: /* forward, this is just an empty */ - FWD(aft, aft, 1); - break; - case O_PLUS: /* both forward and back */ - FWD(aft, aft, 1); - i = ISSETBACK(aft, OPND(s)); - BACK(aft, aft, OPND(s)); - if (!i && ISSETBACK(aft, OPND(s))) { - /* oho, must reconsider loop body */ - pc -= OPND(s) + 1; - INIT(here, pc); - } - break; - case OQUEST_: /* two branches, both forward */ - FWD(aft, aft, 1); - FWD(aft, aft, OPND(s)); - break; - case O_QUEST: /* just an empty */ - FWD(aft, aft, 1); - break; - case OLPAREN: /* not significant here */ - case ORPAREN: - FWD(aft, aft, 1); - break; - case OCH_: /* mark the first two branches */ - FWD(aft, aft, 1); - assert(OP(g->strip[pc+OPND(s)]) == OOR2); - FWD(aft, aft, OPND(s)); - break; - case OOR1: /* done a branch, find the O_CH */ - if (ISSTATEIN(aft, here)) { - for (look = 1; - OP(s = g->strip[pc+look]) != O_CH; - look += OPND(s)) - assert(OP(s) == OOR2); - FWD(aft, aft, look); - } - break; - case OOR2: /* propagate OCH_'s marking */ - FWD(aft, aft, 1); - if (OP(g->strip[pc+OPND(s)]) != O_CH) { - assert(OP(g->strip[pc+OPND(s)]) == OOR2); - FWD(aft, aft, OPND(s)); - } - break; - case O_CH: /* just empty */ - FWD(aft, aft, 1); - break; - default: /* ooooops... */ - assert(nope); - break; - } - } - - return(aft); -} - -#ifdef REDEBUG -/* - - print - print a set of states - == #ifdef REDEBUG - == static void print(struct match *m, char *caption, states st, \ - == int ch, FILE *d); - == #endif - */ -static void -print(m, caption, st, ch, d) -struct match *m; -char *caption; -states st; -int ch; -FILE *d; -{ - register struct re_guts *g = m->g; - register int i; - register int first = 1; - - if (!(m->eflags®_TRACE)) - return; - - fprintf(d, "%s", caption); - if (ch != '\0') - fprintf(d, " %s", pchar(ch)); - for (i = 0; i < g->nstates; i++) - if (ISSET(st, i)) { - fprintf(d, "%s%d", (first) ? "\t" : ", ", i); - first = 0; - } - fprintf(d, "\n"); -} - -/* - - at - print current situation - == #ifdef REDEBUG - == static void at(struct match *m, char *title, char *start, char *stop, \ - == sopno startst, sopno stopst); - == #endif - */ -static void -at(m, title, start, stop, startst, stopst) -struct match *m; -char *title; -char *start; -char *stop; -sopno startst; -sopno stopst; -{ - if (!(m->eflags®_TRACE)) - return; - - printf("%s %s-", title, pchar(*start)); - printf("%s ", pchar(*stop)); - printf("%ld-%ld\n", (long)startst, (long)stopst); -} - -#ifndef PCHARDONE -#define PCHARDONE /* never again */ -/* - - pchar - make a character printable - == #ifdef REDEBUG - == static char *pchar(int ch); - == #endif - * - * Is this identical to regchar() over in debug.c? Well, yes. But a - * duplicate here avoids having a debugging-capable regexec.o tied to - * a matching debug.o, and this is convenient. It all disappears in - * the non-debug compilation anyway, so it doesn't matter much. - */ -static char * /* -> representation */ -pchar(ch) -int ch; -{ - static char pbuf[10]; - - if (isprint(ch) || ch == ' ') - sprintf(pbuf, "%c", ch); - else - sprintf(pbuf, "\\%o", ch); - return(pbuf); -} -#endif -#endif - -#undef matcher -#undef fast -#undef slow -#undef dissect -#undef backref -#undef step -#undef print -#undef at -#undef match diff --git a/sys/amiga/regex/regcomp.c b/sys/amiga/regex/regcomp.c deleted file mode 100755 index 9b64aa4b2..000000000 --- a/sys/amiga/regex/regcomp.c +++ /dev/null @@ -1,1704 +0,0 @@ -/* $NetBSD: regcomp.c,v 1.6 1995/02/27 13:29:01 cgd Exp $ */ - -/*- - * Copyright (c) 1992, 1993, 1994 Henry Spencer. - * Copyright (c) 1992, 1993, 1994 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Henry Spencer. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)regcomp.c 8.5 (Berkeley) 3/20/94 - */ - -#if defined(LIBC_SCCS) && !defined(lint) -#if 0 -static char sccsid[] = "@(#)regcomp.c 8.5 (Berkeley) 3/20/94"; -#else -static char rcsid[] = "$NetBSD: regcomp.c,v 1.6 1995/02/27 13:29:01 cgd Exp $"; -#endif -#endif /* LIBC_SCCS and not lint */ - -#include -#include -#include -#include -#include -#include -#include - -#include "utils.h" -#include "regex2.h" - -#include "cclass.h" -#include "cname.h" - -/* - * parse structure, passed up and down to avoid global variables and - * other clumsinesses - */ -struct parse { - char *next; /* next character in RE */ - char *end; /* end of string (-> NUL normally) */ - int error; /* has an error been seen? */ - sop *strip; /* malloced strip */ - sopno ssize; /* malloced strip size (allocated) */ - sopno slen; /* malloced strip length (used) */ - int ncsalloc; /* number of csets allocated */ - struct re_guts *g; -# define NPAREN 10 /* we need to remember () 1-9 for back refs */ - sopno pbegin[NPAREN]; /* -> ( ([0] unused) */ - sopno pend[NPAREN]; /* -> ) ([0] unused) */ -}; - -/* ========= begin header generated by ./mkh ========= */ -#ifdef __cplusplus -extern "C" { -#endif - -/* === regcomp.c === */ -static void p_ere __P((struct parse *p, int stop)); -static void p_ere_exp __P((struct parse *p)); -static void p_str __P((struct parse *p)); -static void p_bre __P((struct parse *p, int end1, int end2)); -static int p_simp_re __P((struct parse *p, int starordinary)); -static int p_count __P((struct parse *p)); -static void p_bracket __P((struct parse *p)); -static void p_b_term __P((struct parse *p, cset *cs)); -static void p_b_cclass __P((struct parse *p, cset *cs)); -static void p_b_eclass __P((struct parse *p, cset *cs)); -static char p_b_symbol __P((struct parse *p)); -static char p_b_coll_elem __P((struct parse *p, int endc)); -static char othercase __P((int ch)); -static void bothcases __P((struct parse *p, int ch)); -static void ordinary __P((struct parse *p, int ch)); -static void nonnewline __P((struct parse *p)); -static void repeat __P((struct parse *p, sopno start, int from, int to)); -static int seterr __P((struct parse *p, int e)); -static cset *allocset __P((struct parse *p)); -static void freeset __P((struct parse *p, cset *cs)); -static int freezeset __P((struct parse *p, cset *cs)); -static int firstch __P((struct parse *p, cset *cs)); -static int nch __P((struct parse *p, cset *cs)); -static void mcadd __P((struct parse *p, cset *cs, char *cp)); -static void mcsub __P((cset *cs, char *cp)); -static int mcin __P((cset *cs, char *cp)); -static char *mcfind __P((cset *cs, char *cp)); -static void mcinvert __P((struct parse *p, cset *cs)); -static void mccase __P((struct parse *p, cset *cs)); -static int isinsets __P((struct re_guts *g, int c)); -static int samesets __P((struct re_guts *g, int c1, int c2)); -static void categorize __P((struct parse *p, struct re_guts *g)); -static sopno dupl __P((struct parse *p, sopno start, sopno finish)); -static void doemit __P((struct parse *p, sop op, size_t opnd)); -static void doinsert __P((struct parse *p, sop op, size_t opnd, sopno pos)); -static void dofwd __P((struct parse *p, sopno pos, sop value)); -static void enlarge __P((struct parse *p, sopno size)); -static void stripsnug __P((struct parse *p, struct re_guts *g)); -static void findmust __P((struct parse *p, struct re_guts *g)); -static sopno pluscount __P((struct parse *p, struct re_guts *g)); - -#ifdef __cplusplus -} -#endif -/* ========= end header generated by ./mkh ========= */ - -static char nuls[10]; /* place to point scanner in event of error */ - -/* - * macros for use with parse structure - * BEWARE: these know that the parse structure is named `p' !!! - */ -#define PEEK() (*p->next) -#define PEEK2() (*(p->next+1)) -#define MORE() (p->next < p->end) -#define MORE2() (p->next+1 < p->end) -#define SEE(c) (MORE() && PEEK() == (c)) -#define SEETWO(a, b) (MORE() && MORE2() && PEEK() == (a) && PEEK2() == (b)) -#define EAT(c) ((SEE(c)) ? (NEXT(), 1) : 0) -#define EATTWO(a, b) ((SEETWO(a, b)) ? (NEXT2(), 1) : 0) -#define NEXT() (p->next++) -#define NEXT2() (p->next += 2) -#define NEXTn(n) (p->next += (n)) -#define GETNEXT() (*p->next++) -#define SETERROR(e) seterr(p, (e)) -#define REQUIRE(co, e) ((co) || SETERROR(e)) -#define MUSTSEE(c, e) (REQUIRE(MORE() && PEEK() == (c), e)) -#define MUSTEAT(c, e) (REQUIRE(MORE() && GETNEXT() == (c), e)) -#define MUSTNOTSEE(c, e) (REQUIRE(!MORE() || PEEK() != (c), e)) -#define EMIT(op, sopnd) doemit(p, (sop)(op), (size_t)(sopnd)) -#define INSERT(op, pos) doinsert(p, (sop)(op), HERE()-(pos)+1, pos) -#define AHEAD(pos) dofwd(p, pos, HERE()-(pos)) -#define ASTERN(sop, pos) EMIT(sop, HERE()-pos) -#define HERE() (p->slen) -#define THERE() (p->slen - 1) -#define THERETHERE() (p->slen - 2) -#define DROP(n) (p->slen -= (n)) - -#ifndef NDEBUG -static int never = 0; /* for use in asserts; shuts lint up */ -#else -#define never 0 /* some s have bugs too */ -#endif - -/* - - regcomp - interface for parser and compilation - = extern int regcomp(regex_t *, const char *, int); - = #define REG_BASIC 0000 - = #define REG_EXTENDED 0001 - = #define REG_ICASE 0002 - = #define REG_NOSUB 0004 - = #define REG_NEWLINE 0010 - = #define REG_NOSPEC 0020 - = #define REG_PEND 0040 - = #define REG_DUMP 0200 - */ -int /* 0 success, otherwise REG_something */ -regcomp(preg, pattern, cflags) -regex_t *preg; -const char *pattern; -int cflags; -{ - struct parse pa; - register struct re_guts *g; - register struct parse *p = &pa; - register int i; - register size_t len; -#ifdef REDEBUG -# define GOODFLAGS(f) (f) -#else -# define GOODFLAGS(f) ((f)&~REG_DUMP) -#endif - - cflags = GOODFLAGS(cflags); - if ((cflags®_EXTENDED) && (cflags®_NOSPEC)) - return(REG_INVARG); - - if (cflags®_PEND) { - if (preg->re_endp < pattern) - return(REG_INVARG); - len = preg->re_endp - pattern; - } else - len = strlen((char *)pattern); - - /* do the mallocs early so failure handling is easy */ - g = (struct re_guts *)malloc(sizeof(struct re_guts) + - (NC-1)*sizeof(cat_t)); - if (g == NULL) - return(REG_ESPACE); - p->ssize = len/(size_t)2*(size_t)3 + (size_t)1; /* ugh */ - p->strip = (sop *)malloc(p->ssize * sizeof(sop)); - p->slen = 0; - if (p->strip == NULL) { - free((char *)g); - return(REG_ESPACE); - } - - /* set things up */ - p->g = g; - p->next = (char *)pattern; /* convenience; we do not modify it */ - p->end = p->next + len; - p->error = 0; - p->ncsalloc = 0; - for (i = 0; i < NPAREN; i++) { - p->pbegin[i] = 0; - p->pend[i] = 0; - } - g->csetsize = NC; - g->sets = NULL; - g->setbits = NULL; - g->ncsets = 0; - g->cflags = cflags; - g->iflags = 0; - g->nbol = 0; - g->neol = 0; - g->must = NULL; - g->mlen = 0; - g->nsub = 0; - g->ncategories = 1; /* category 0 is "everything else" */ - g->categories = &g->catspace[-(CHAR_MIN)]; - (void) memset((char *)g->catspace, 0, NC*sizeof(cat_t)); - g->backrefs = 0; - - /* do it */ - EMIT(OEND, 0); - g->firststate = THERE(); - if (cflags®_EXTENDED) - p_ere(p, OUT); - else if (cflags®_NOSPEC) - p_str(p); - else - p_bre(p, OUT, OUT); - EMIT(OEND, 0); - g->laststate = THERE(); - - /* tidy up loose ends and fill things in */ - categorize(p, g); - stripsnug(p, g); - findmust(p, g); - g->nplus = pluscount(p, g); - g->magic = MAGIC2; - preg->re_nsub = g->nsub; - preg->re_g = g; - preg->re_magic = MAGIC1; -#ifndef REDEBUG - /* not debugging, so can't rely on the assert() in regexec() */ - if (g->iflags&BAD) - SETERROR(REG_ASSERT); -#endif - - /* win or lose, we're done */ - if (p->error != 0) /* lose */ - regfree(preg); - return(p->error); -} - -/* - - p_ere - ERE parser top level, concatenation and alternation - == static void p_ere(register struct parse *p, int stop); - */ -static void -p_ere(p, stop) -register struct parse *p; -int stop; /* character this ERE should end at */ -{ - register char c; - register sopno prevback; - register sopno prevfwd; - register sopno conc; - register int first = 1; /* is this the first alternative? */ - - for (;;) { - /* do a bunch of concatenated expressions */ - conc = HERE(); - while (MORE() && (c = PEEK()) != '|' && c != stop) - p_ere_exp(p); - REQUIRE(HERE() != conc, REG_EMPTY); /* require nonempty */ - - if (!EAT('|')) - break; /* NOTE BREAK OUT */ - - if (first) { - INSERT(OCH_, conc); /* offset is wrong */ - prevfwd = conc; - prevback = conc; - first = 0; - } - ASTERN(OOR1, prevback); - prevback = THERE(); - AHEAD(prevfwd); /* fix previous offset */ - prevfwd = HERE(); - EMIT(OOR2, 0); /* offset is very wrong */ - } - - if (!first) { /* tail-end fixups */ - AHEAD(prevfwd); - ASTERN(O_CH, prevback); - } - - assert(!MORE() || SEE(stop)); -} - -/* - - p_ere_exp - parse one subERE, an atom possibly followed by a repetition op - == static void p_ere_exp(register struct parse *p); - */ -static void -p_ere_exp(p) -register struct parse *p; -{ - register char c; - register sopno pos; - register int count; - register int count2; - register sopno subno; - int wascaret = 0; - - assert(MORE()); /* caller should have ensured this */ - c = GETNEXT(); - - pos = HERE(); - switch (c) { - case '(': - REQUIRE(MORE(), REG_EPAREN); - p->g->nsub++; - subno = p->g->nsub; - if (subno < NPAREN) - p->pbegin[subno] = HERE(); - EMIT(OLPAREN, subno); - if (!SEE(')')) - p_ere(p, ')'); - if (subno < NPAREN) { - p->pend[subno] = HERE(); - assert(p->pend[subno] != 0); - } - EMIT(ORPAREN, subno); - MUSTEAT(')', REG_EPAREN); - break; -#ifndef POSIX_MISTAKE - case ')': /* happens only if no current unmatched ( */ - /* - * You may ask, why the ifndef? Because I didn't notice - * this until slightly too late for 1003.2, and none of the - * other 1003.2 regular-expression reviewers noticed it at - * all. So an unmatched ) is legal POSIX, at least until - * we can get it fixed. - */ - SETERROR(REG_EPAREN); - break; -#endif - case '^': - EMIT(OBOL, 0); - p->g->iflags |= USEBOL; - p->g->nbol++; - wascaret = 1; - break; - case '$': - EMIT(OEOL, 0); - p->g->iflags |= USEEOL; - p->g->neol++; - break; - case '|': - SETERROR(REG_EMPTY); - break; - case '*': - case '+': - case '?': - SETERROR(REG_BADRPT); - break; - case '.': - if (p->g->cflags®_NEWLINE) - nonnewline(p); - else - EMIT(OANY, 0); - break; - case '[': - p_bracket(p); - break; - case '\\': - REQUIRE(MORE(), REG_EESCAPE); - c = GETNEXT(); - ordinary(p, c); - break; - case '{': /* okay as ordinary except if digit follows */ - REQUIRE(!MORE() || !isdigit(PEEK()), REG_BADRPT); - /* FALLTHROUGH */ - default: - ordinary(p, c); - break; - } - - if (!MORE()) - return; - c = PEEK(); - /* we call { a repetition if followed by a digit */ - if (!( c == '*' || c == '+' || c == '?' || - (c == '{' && MORE2() && isdigit(PEEK2())) )) - return; /* no repetition, we're done */ - NEXT(); - - REQUIRE(!wascaret, REG_BADRPT); - switch (c) { - case '*': /* implemented as +? */ - /* this case does not require the (y|) trick, noKLUDGE */ - INSERT(OPLUS_, pos); - ASTERN(O_PLUS, pos); - INSERT(OQUEST_, pos); - ASTERN(O_QUEST, pos); - break; - case '+': - INSERT(OPLUS_, pos); - ASTERN(O_PLUS, pos); - break; - case '?': - /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */ - INSERT(OCH_, pos); /* offset slightly wrong */ - ASTERN(OOR1, pos); /* this one's right */ - AHEAD(pos); /* fix the OCH_ */ - EMIT(OOR2, 0); /* offset very wrong... */ - AHEAD(THERE()); /* ...so fix it */ - ASTERN(O_CH, THERETHERE()); - break; - case '{': - count = p_count(p); - if (EAT(',')) { - if (isdigit(PEEK())) { - count2 = p_count(p); - REQUIRE(count <= count2, REG_BADBR); - } else /* single number with comma */ - count2 = INFINITY; - } else /* just a single number */ - count2 = count; - repeat(p, pos, count, count2); - if (!EAT('}')) { /* error heuristics */ - while (MORE() && PEEK() != '}') - NEXT(); - REQUIRE(MORE(), REG_EBRACE); - SETERROR(REG_BADBR); - } - break; - } - - if (!MORE()) - return; - c = PEEK(); - if (!( c == '*' || c == '+' || c == '?' || - (c == '{' && MORE2() && isdigit(PEEK2())) ) ) - return; - SETERROR(REG_BADRPT); -} - -/* - - p_str - string (no metacharacters) "parser" - == static void p_str(register struct parse *p); - */ -static void -p_str(p) -register struct parse *p; -{ - REQUIRE(MORE(), REG_EMPTY); - while (MORE()) - ordinary(p, GETNEXT()); -} - -/* - - p_bre - BRE parser top level, anchoring and concatenation - == static void p_bre(register struct parse *p, register int end1, \ - == register int end2); - * Giving end1 as OUT essentially eliminates the end1/end2 check. - * - * This implementation is a bit of a kludge, in that a trailing $ is first - * taken as an ordinary character and then revised to be an anchor. The - * only undesirable side effect is that '$' gets included as a character - * category in such cases. This is fairly harmless; not worth fixing. - * The amount of lookahead needed to avoid this kludge is excessive. - */ -static void -p_bre(p, end1, end2) -register struct parse *p; -register int end1; /* first terminating character */ -register int end2; /* second terminating character */ -{ - register sopno start = HERE(); - register int first = 1; /* first subexpression? */ - register int wasdollar = 0; - - if (EAT('^')) { - EMIT(OBOL, 0); - p->g->iflags |= USEBOL; - p->g->nbol++; - } - while (MORE() && !SEETWO(end1, end2)) { - wasdollar = p_simp_re(p, first); - first = 0; - } - if (wasdollar) { /* oops, that was a trailing anchor */ - DROP(1); - EMIT(OEOL, 0); - p->g->iflags |= USEEOL; - p->g->neol++; - } - - REQUIRE(HERE() != start, REG_EMPTY); /* require nonempty */ -} - -/* - - p_simp_re - parse a simple RE, an atom possibly followed by a repetition - == static int p_simp_re(register struct parse *p, int starordinary); - */ -static int /* was the simple RE an unbackslashed $? */ -p_simp_re(p, starordinary) -register struct parse *p; -int starordinary; /* is a leading * an ordinary character? */ -{ - register int c; - register int count; - register int count2; - register sopno pos; - register int i; - register sopno subno; -# define BACKSL (1<g->cflags®_NEWLINE) - nonnewline(p); - else - EMIT(OANY, 0); - break; - case '[': - p_bracket(p); - break; - case BACKSL|'{': - SETERROR(REG_BADRPT); - break; - case BACKSL|'(': - p->g->nsub++; - subno = p->g->nsub; - if (subno < NPAREN) - p->pbegin[subno] = HERE(); - EMIT(OLPAREN, subno); - /* the MORE here is an error heuristic */ - if (MORE() && !SEETWO('\\', ')')) - p_bre(p, '\\', ')'); - if (subno < NPAREN) { - p->pend[subno] = HERE(); - assert(p->pend[subno] != 0); - } - EMIT(ORPAREN, subno); - REQUIRE(EATTWO('\\', ')'), REG_EPAREN); - break; - case BACKSL|')': /* should not get here -- must be user */ - case BACKSL|'}': - SETERROR(REG_EPAREN); - break; - case BACKSL|'1': - case BACKSL|'2': - case BACKSL|'3': - case BACKSL|'4': - case BACKSL|'5': - case BACKSL|'6': - case BACKSL|'7': - case BACKSL|'8': - case BACKSL|'9': - i = (c&~BACKSL) - '0'; - assert(i < NPAREN); - if (p->pend[i] != 0) { - assert(i <= p->g->nsub); - EMIT(OBACK_, i); - assert(p->pbegin[i] != 0); - assert(OP(p->strip[p->pbegin[i]]) == OLPAREN); - assert(OP(p->strip[p->pend[i]]) == ORPAREN); - (void) dupl(p, p->pbegin[i]+1, p->pend[i]); - EMIT(O_BACK, i); - } else - SETERROR(REG_ESUBREG); - p->g->backrefs = 1; - break; - case '*': - REQUIRE(starordinary, REG_BADRPT); - /* FALLTHROUGH */ - default: - ordinary(p, c &~ BACKSL); - break; - } - - if (EAT('*')) { /* implemented as +? */ - /* this case does not require the (y|) trick, noKLUDGE */ - INSERT(OPLUS_, pos); - ASTERN(O_PLUS, pos); - INSERT(OQUEST_, pos); - ASTERN(O_QUEST, pos); - } else if (EATTWO('\\', '{')) { - count = p_count(p); - if (EAT(',')) { - if (MORE() && isdigit(PEEK())) { - count2 = p_count(p); - REQUIRE(count <= count2, REG_BADBR); - } else /* single number with comma */ - count2 = INFINITY; - } else /* just a single number */ - count2 = count; - repeat(p, pos, count, count2); - if (!EATTWO('\\', '}')) { /* error heuristics */ - while (MORE() && !SEETWO('\\', '}')) - NEXT(); - REQUIRE(MORE(), REG_EBRACE); - SETERROR(REG_BADBR); - } - } else if (c == (unsigned char)'$') /* $ (but not \$) ends it */ - return(1); - - return(0); -} - -/* - - p_count - parse a repetition count - == static int p_count(register struct parse *p); - */ -static int /* the value */ -p_count(p) -register struct parse *p; -{ - register int count = 0; - register int ndigits = 0; - - while (MORE() && isdigit(PEEK()) && count <= DUPMAX) { - count = count*10 + (GETNEXT() - '0'); - ndigits++; - } - - REQUIRE(ndigits > 0 && count <= DUPMAX, REG_BADBR); - return(count); -} - -/* - - p_bracket - parse a bracketed character list - == static void p_bracket(register struct parse *p); - * - * Note a significant property of this code: if the allocset() did SETERROR, - * no set operations are done. - */ -static void -p_bracket(p) -register struct parse *p; -{ - register char c; - register cset *cs = allocset(p); - register int invert = 0; - - /* Dept of Truly Sickening Special-Case Kludges */ - if (p->next + 5 < p->end && strncmp(p->next, "[:<:]]", 6) == 0) { - EMIT(OBOW, 0); - NEXTn(6); - return; - } - if (p->next + 5 < p->end && strncmp(p->next, "[:>:]]", 6) == 0) { - EMIT(OEOW, 0); - NEXTn(6); - return; - } - - if (EAT('^')) - invert++; /* make note to invert set at end */ - if (EAT(']')) - CHadd(cs, ']'); - else if (EAT('-')) - CHadd(cs, '-'); - while (MORE() && PEEK() != ']' && !SEETWO('-', ']')) - p_b_term(p, cs); - if (EAT('-')) - CHadd(cs, '-'); - MUSTEAT(']', REG_EBRACK); - - if (p->error != 0) /* don't mess things up further */ - return; - - if (p->g->cflags®_ICASE) { - register int i; - register int ci; - - for (i = p->g->csetsize - 1; i >= 0; i--) - if (CHIN(cs, i) && isalpha(i)) { - ci = othercase(i); - if (ci != i) - CHadd(cs, ci); - } - if (cs->multis != NULL) - mccase(p, cs); - } - if (invert) { - register int i; - - for (i = p->g->csetsize - 1; i >= 0; i--) - if (CHIN(cs, i)) - CHsub(cs, i); - else - CHadd(cs, i); - if (p->g->cflags®_NEWLINE) - CHsub(cs, '\n'); - if (cs->multis != NULL) - mcinvert(p, cs); - } - - assert(cs->multis == NULL); /* xxx */ - - if (nch(p, cs) == 1) { /* optimize singleton sets */ - ordinary(p, firstch(p, cs)); - freeset(p, cs); - } else - EMIT(OANYOF, freezeset(p, cs)); -} - -/* - - p_b_term - parse one term of a bracketed character list - == static void p_b_term(register struct parse *p, register cset *cs); - */ -static void -p_b_term(p, cs) -register struct parse *p; -register cset *cs; -{ - register char c; - register char start, finish; - register int i; - - /* classify what we've got */ - switch ((MORE()) ? PEEK() : '\0') { - case '[': - c = (MORE2()) ? PEEK2() : '\0'; - break; - case '-': - SETERROR(REG_ERANGE); - return; /* NOTE RETURN */ - break; - default: - c = '\0'; - break; - } - - switch (c) { - case ':': /* character class */ - NEXT2(); - REQUIRE(MORE(), REG_EBRACK); - c = PEEK(); - REQUIRE(c != '-' && c != ']', REG_ECTYPE); - p_b_cclass(p, cs); - REQUIRE(MORE(), REG_EBRACK); - REQUIRE(EATTWO(':', ']'), REG_ECTYPE); - break; - case '=': /* equivalence class */ - NEXT2(); - REQUIRE(MORE(), REG_EBRACK); - c = PEEK(); - REQUIRE(c != '-' && c != ']', REG_ECOLLATE); - p_b_eclass(p, cs); - REQUIRE(MORE(), REG_EBRACK); - REQUIRE(EATTWO('=', ']'), REG_ECOLLATE); - break; - default: /* symbol, ordinary character, or range */ -/* xxx revision needed for multichar stuff */ - start = p_b_symbol(p); - if (SEE('-') && MORE2() && PEEK2() != ']') { - /* range */ - NEXT(); - if (EAT('-')) - finish = '-'; - else - finish = p_b_symbol(p); - } else - finish = start; -/* xxx what about signed chars here... */ - REQUIRE(start <= finish, REG_ERANGE); - for (i = start; i <= finish; i++) - CHadd(cs, i); - break; - } -} - -/* - - p_b_cclass - parse a character-class name and deal with it - == static void p_b_cclass(register struct parse *p, register cset *cs); - */ -static void -p_b_cclass(p, cs) -register struct parse *p; -register cset *cs; -{ - register char *sp = p->next; - register struct cclass *cp; - register size_t len; - register char *u; - register char c; - - while (MORE() && isalpha(PEEK())) - NEXT(); - len = p->next - sp; - for (cp = cclasses; cp->name != NULL; cp++) - if (strncmp(cp->name, sp, len) == 0 && cp->name[len] == '\0') - break; - if (cp->name == NULL) { - /* oops, didn't find it */ - SETERROR(REG_ECTYPE); - return; - } - - u = cp->chars; - while ((c = *u++) != '\0') - CHadd(cs, c); - for (u = cp->multis; *u != '\0'; u += strlen(u) + 1) - MCadd(p, cs, u); -} - -/* - - p_b_eclass - parse an equivalence-class name and deal with it - == static void p_b_eclass(register struct parse *p, register cset *cs); - * - * This implementation is incomplete. xxx - */ -static void -p_b_eclass(p, cs) -register struct parse *p; -register cset *cs; -{ - register char c; - - c = p_b_coll_elem(p, '='); - CHadd(cs, c); -} - -/* - - p_b_symbol - parse a character or [..]ed multicharacter collating symbol - == static char p_b_symbol(register struct parse *p); - */ -static char /* value of symbol */ -p_b_symbol(p) -register struct parse *p; -{ - register char value; - - REQUIRE(MORE(), REG_EBRACK); - if (!EATTWO('[', '.')) - return(GETNEXT()); - - /* collating symbol */ - value = p_b_coll_elem(p, '.'); - REQUIRE(EATTWO('.', ']'), REG_ECOLLATE); - return(value); -} - -/* - - p_b_coll_elem - parse a collating-element name and look it up - == static char p_b_coll_elem(register struct parse *p, int endc); - */ -static char /* value of collating element */ -p_b_coll_elem(p, endc) -register struct parse *p; -int endc; /* name ended by endc,']' */ -{ - register char *sp = p->next; - register struct cname *cp; - register int len; - register char c; - - while (MORE() && !SEETWO(endc, ']')) - NEXT(); - if (!MORE()) { - SETERROR(REG_EBRACK); - return(0); - } - len = p->next - sp; - for (cp = cnames; cp->name != NULL; cp++) - if (strncmp(cp->name, sp, len) == 0 && cp->name[len] == '\0') - return(cp->code); /* known name */ - if (len == 1) - return(*sp); /* single character */ - SETERROR(REG_ECOLLATE); /* neither */ - return(0); -} - -/* - - othercase - return the case counterpart of an alphabetic - == static char othercase(int ch); - */ -static char /* if no counterpart, return ch */ -othercase(ch) -int ch; -{ - assert(isalpha(ch)); - if (isupper(ch)) - return(tolower(ch)); - else if (islower(ch)) - return(toupper(ch)); - else /* peculiar, but could happen */ - return(ch); -} - -/* - - bothcases - emit a dualcase version of a two-case character - == static void bothcases(register struct parse *p, int ch); - * - * Boy, is this implementation ever a kludge... - */ -static void -bothcases(p, ch) -register struct parse *p; -int ch; -{ - register char *oldnext = p->next; - register char *oldend = p->end; - char bracket[3]; - - assert(othercase(ch) != ch); /* p_bracket() would recurse */ - p->next = bracket; - p->end = bracket+2; - bracket[0] = ch; - bracket[1] = ']'; - bracket[2] = '\0'; - p_bracket(p); - assert(p->next == bracket+2); - p->next = oldnext; - p->end = oldend; -} - -/* - - ordinary - emit an ordinary character - == static void ordinary(register struct parse *p, register int ch); - */ -static void -ordinary(p, ch) -register struct parse *p; -register int ch; -{ - register cat_t *cap = p->g->categories; - - if ((p->g->cflags®_ICASE) && isalpha(ch) && othercase(ch) != ch) - bothcases(p, ch); - else { - EMIT(OCHAR, (unsigned char)ch); - if (cap[ch] == 0) - cap[ch] = p->g->ncategories++; - } -} - -/* - - nonnewline - emit REG_NEWLINE version of OANY - == static void nonnewline(register struct parse *p); - * - * Boy, is this implementation ever a kludge... - */ -static void -nonnewline(p) -register struct parse *p; -{ - register char *oldnext = p->next; - register char *oldend = p->end; - char bracket[4]; - - p->next = bracket; - p->end = bracket+3; - bracket[0] = '^'; - bracket[1] = '\n'; - bracket[2] = ']'; - bracket[3] = '\0'; - p_bracket(p); - assert(p->next == bracket+3); - p->next = oldnext; - p->end = oldend; -} - -/* - - repeat - generate code for a bounded repetition, recursively if needed - == static void repeat(register struct parse *p, sopno start, int from, int to); - */ -static void -repeat(p, start, from, to) -register struct parse *p; -sopno start; /* operand from here to end of strip */ -int from; /* repeated from this number */ -int to; /* to this number of times (maybe INFINITY) */ -{ - register sopno finish = HERE(); -# define N 2 -# define INF 3 -# define REP(f, t) ((f)*8 + (t)) -# define MAP(n) (((n) <= 1) ? (n) : ((n) == INFINITY) ? INF : N) - register sopno copy; - - if (p->error != 0) /* head off possible runaway recursion */ - return; - - assert(from <= to); - - switch (REP(MAP(from), MAP(to))) { - case REP(0, 0): /* must be user doing this */ - DROP(finish-start); /* drop the operand */ - break; - case REP(0, 1): /* as x{1,1}? */ - case REP(0, N): /* as x{1,n}? */ - case REP(0, INF): /* as x{1,}? */ - /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */ - INSERT(OCH_, start); /* offset is wrong... */ - repeat(p, start+1, 1, to); - ASTERN(OOR1, start); - AHEAD(start); /* ... fix it */ - EMIT(OOR2, 0); - AHEAD(THERE()); - ASTERN(O_CH, THERETHERE()); - break; - case REP(1, 1): /* trivial case */ - /* done */ - break; - case REP(1, N): /* as x?x{1,n-1} */ - /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */ - INSERT(OCH_, start); - ASTERN(OOR1, start); - AHEAD(start); - EMIT(OOR2, 0); /* offset very wrong... */ - AHEAD(THERE()); /* ...so fix it */ - ASTERN(O_CH, THERETHERE()); - copy = dupl(p, start+1, finish+1); - assert(copy == finish+4); - repeat(p, copy, 1, to-1); - break; - case REP(1, INF): /* as x+ */ - INSERT(OPLUS_, start); - ASTERN(O_PLUS, start); - break; - case REP(N, N): /* as xx{m-1,n-1} */ - copy = dupl(p, start, finish); - repeat(p, copy, from-1, to-1); - break; - case REP(N, INF): /* as xx{n-1,INF} */ - copy = dupl(p, start, finish); - repeat(p, copy, from-1, to); - break; - default: /* "can't happen" */ - SETERROR(REG_ASSERT); /* just in case */ - break; - } -} - -/* - - seterr - set an error condition - == static int seterr(register struct parse *p, int e); - */ -static int /* useless but makes type checking happy */ -seterr(p, e) -register struct parse *p; -int e; -{ - if (p->error == 0) /* keep earliest error condition */ - p->error = e; - p->next = nuls; /* try to bring things to a halt */ - p->end = nuls; - return(0); /* make the return value well-defined */ -} - -/* - - allocset - allocate a set of characters for [] - == static cset *allocset(register struct parse *p); - */ -static cset * -allocset(p) -register struct parse *p; -{ - register int no = p->g->ncsets++; - register size_t nc; - register size_t nbytes; - register cset *cs; - register size_t css = (size_t)p->g->csetsize; - register int i; - - if (no >= p->ncsalloc) { /* need another column of space */ - p->ncsalloc += CHAR_BIT; - nc = p->ncsalloc; - assert(nc % CHAR_BIT == 0); - nbytes = nc / CHAR_BIT * css; - if (p->g->sets == NULL) - p->g->sets = (cset *)malloc(nc * sizeof(cset)); - else - p->g->sets = (cset *)realloc((char *)p->g->sets, - nc * sizeof(cset)); - if (p->g->setbits == NULL) - p->g->setbits = (uch *)malloc(nbytes); - else { - p->g->setbits = (uch *)realloc((char *)p->g->setbits, - nbytes); - /* xxx this isn't right if setbits is now NULL */ - for (i = 0; i < no; i++) - p->g->sets[i].ptr = p->g->setbits + css*(i/CHAR_BIT); - } - if (p->g->sets != NULL && p->g->setbits != NULL) - (void) memset((char *)p->g->setbits + (nbytes - css), - 0, css); - else { - no = 0; - SETERROR(REG_ESPACE); - /* caller's responsibility not to do set ops */ - } - } - - assert(p->g->sets != NULL); /* xxx */ - cs = &p->g->sets[no]; - cs->ptr = p->g->setbits + css*((no)/CHAR_BIT); - cs->mask = 1 << ((no) % CHAR_BIT); - cs->hash = 0; - cs->smultis = 0; - cs->multis = NULL; - - return(cs); -} - -/* - - freeset - free a now-unused set - == static void freeset(register struct parse *p, register cset *cs); - */ -static void -freeset(p, cs) -register struct parse *p; -register cset *cs; -{ - register int i; - register cset *top = &p->g->sets[p->g->ncsets]; - register size_t css = (size_t)p->g->csetsize; - - for (i = 0; i < css; i++) - CHsub(cs, i); - if (cs == top-1) /* recover only the easy case */ - p->g->ncsets--; -} - -/* - - freezeset - final processing on a set of characters - == static int freezeset(register struct parse *p, register cset *cs); - * - * The main task here is merging identical sets. This is usually a waste - * of time (although the hash code minimizes the overhead), but can win - * big if REG_ICASE is being used. REG_ICASE, by the way, is why the hash - * is done using addition rather than xor -- all ASCII [aA] sets xor to - * the same value! - */ -static int /* set number */ -freezeset(p, cs) -register struct parse *p; -register cset *cs; -{ - register uch h = cs->hash; - register int i; - register cset *top = &p->g->sets[p->g->ncsets]; - register cset *cs2; - register size_t css = (size_t)p->g->csetsize; - - /* look for an earlier one which is the same */ - for (cs2 = &p->g->sets[0]; cs2 < top; cs2++) - if (cs2->hash == h && cs2 != cs) { - /* maybe */ - for (i = 0; i < css; i++) - if (!!CHIN(cs2, i) != !!CHIN(cs, i)) - break; /* no */ - if (i == css) - break; /* yes */ - } - - if (cs2 < top) { /* found one */ - freeset(p, cs); - cs = cs2; - } - - return((int)(cs - p->g->sets)); -} - -/* - - firstch - return first character in a set (which must have at least one) - == static int firstch(register struct parse *p, register cset *cs); - */ -static int /* character; there is no "none" value */ -firstch(p, cs) -register struct parse *p; -register cset *cs; -{ - register int i; - register size_t css = (size_t)p->g->csetsize; - - for (i = 0; i < css; i++) - if (CHIN(cs, i)) - return((char)i); - assert(never); - return(0); /* arbitrary */ -} - -/* - - nch - number of characters in a set - == static int nch(register struct parse *p, register cset *cs); - */ -static int -nch(p, cs) -register struct parse *p; -register cset *cs; -{ - register int i; - register size_t css = (size_t)p->g->csetsize; - register int n = 0; - - for (i = 0; i < css; i++) - if (CHIN(cs, i)) - n++; - return(n); -} - -/* - - mcadd - add a collating element to a cset - == static void mcadd(register struct parse *p, register cset *cs, \ - == register char *cp); - */ -static void -mcadd(p, cs, cp) -register struct parse *p; -register cset *cs; -register char *cp; -{ - register size_t oldend = cs->smultis; - - cs->smultis += strlen(cp) + 1; - if (cs->multis == NULL) - cs->multis = malloc(cs->smultis); - else - cs->multis = realloc(cs->multis, cs->smultis); - if (cs->multis == NULL) { - SETERROR(REG_ESPACE); - return; - } - - (void) strcpy(cs->multis + oldend - 1, cp); - cs->multis[cs->smultis - 1] = '\0'; -} - -/* - - mcsub - subtract a collating element from a cset - == static void mcsub(register cset *cs, register char *cp); - */ -static void -mcsub(cs, cp) -register cset *cs; -register char *cp; -{ - register char *fp = mcfind(cs, cp); - register size_t len = strlen(fp); - - assert(fp != NULL); - (void) memmove(fp, fp + len + 1, - cs->smultis - (fp + len + 1 - cs->multis)); - cs->smultis -= len; - - if (cs->smultis == 0) { - free(cs->multis); - cs->multis = NULL; - return; - } - - cs->multis = realloc(cs->multis, cs->smultis); - assert(cs->multis != NULL); -} - -/* - - mcin - is a collating element in a cset? - == static int mcin(register cset *cs, register char *cp); - */ -static int -mcin(cs, cp) -register cset *cs; -register char *cp; -{ - return(mcfind(cs, cp) != NULL); -} - -/* - - mcfind - find a collating element in a cset - == static char *mcfind(register cset *cs, register char *cp); - */ -static char * -mcfind(cs, cp) -register cset *cs; -register char *cp; -{ - register char *p; - - if (cs->multis == NULL) - return(NULL); - for (p = cs->multis; *p != '\0'; p += strlen(p) + 1) - if (strcmp(cp, p) == 0) - return(p); - return(NULL); -} - -/* - - mcinvert - invert the list of collating elements in a cset - == static void mcinvert(register struct parse *p, register cset *cs); - * - * This would have to know the set of possibilities. Implementation - * is deferred. - */ -static void -mcinvert(p, cs) -register struct parse *p; -register cset *cs; -{ - assert(cs->multis == NULL); /* xxx */ -} - -/* - - mccase - add case counterparts of the list of collating elements in a cset - == static void mccase(register struct parse *p, register cset *cs); - * - * This would have to know the set of possibilities. Implementation - * is deferred. - */ -static void -mccase(p, cs) -register struct parse *p; -register cset *cs; -{ - assert(cs->multis == NULL); /* xxx */ -} - -/* - - isinsets - is this character in any sets? - == static int isinsets(register struct re_guts *g, int c); - */ -static int /* predicate */ -isinsets(g, c) -register struct re_guts *g; -int c; -{ - register uch *col; - register int i; - register int ncols = (g->ncsets+(CHAR_BIT-1)) / CHAR_BIT; - register unsigned uc = (unsigned char)c; - - for (i = 0, col = g->setbits; i < ncols; i++, col += g->csetsize) - if (col[uc] != 0) - return(1); - return(0); -} - -/* - - samesets - are these two characters in exactly the same sets? - == static int samesets(register struct re_guts *g, int c1, int c2); - */ -static int /* predicate */ -samesets(g, c1, c2) -register struct re_guts *g; -int c1; -int c2; -{ - register uch *col; - register int i; - register int ncols = (g->ncsets+(CHAR_BIT-1)) / CHAR_BIT; - register unsigned uc1 = (unsigned char)c1; - register unsigned uc2 = (unsigned char)c2; - - for (i = 0, col = g->setbits; i < ncols; i++, col += g->csetsize) - if (col[uc1] != col[uc2]) - return(0); - return(1); -} - -/* - - categorize - sort out character categories - == static void categorize(struct parse *p, register struct re_guts *g); - */ -static void -categorize(p, g) -struct parse *p; -register struct re_guts *g; -{ - register cat_t *cats = g->categories; - register int c; - register int c2; - register cat_t cat; - - /* avoid making error situations worse */ - if (p->error != 0) - return; - - for (c = CHAR_MIN; c <= CHAR_MAX; c++) - if (cats[c] == 0 && isinsets(g, c)) { - cat = g->ncategories++; - cats[c] = cat; - for (c2 = c+1; c2 <= CHAR_MAX; c2++) - if (cats[c2] == 0 && samesets(g, c, c2)) - cats[c2] = cat; - } -} - -/* - - dupl - emit a duplicate of a bunch of sops - == static sopno dupl(register struct parse *p, sopno start, sopno finish); - */ -static sopno /* start of duplicate */ -dupl(p, start, finish) -register struct parse *p; -sopno start; /* from here */ -sopno finish; /* to this less one */ -{ - register sopno ret = HERE(); - register sopno len = finish - start; - - assert(finish >= start); - if (len == 0) - return(ret); - enlarge(p, p->ssize + len); /* this many unexpected additions */ - assert(p->ssize >= p->slen + len); - (void) memcpy((char *)(p->strip + p->slen), - (char *)(p->strip + start), (size_t)len*sizeof(sop)); - p->slen += len; - return(ret); -} - -/* - - doemit - emit a strip operator - == static void doemit(register struct parse *p, sop op, size_t opnd); - * - * It might seem better to implement this as a macro with a function as - * hard-case backup, but it's just too big and messy unless there are - * some changes to the data structures. Maybe later. - */ -static void -doemit(p, op, opnd) -register struct parse *p; -sop op; -size_t opnd; -{ - /* avoid making error situations worse */ - if (p->error != 0) - return; - - /* deal with oversize operands ("can't happen", more or less) */ - assert(opnd < 1<slen >= p->ssize) - enlarge(p, (p->ssize+1) / 2 * 3); /* +50% */ - assert(p->slen < p->ssize); - - /* finally, it's all reduced to the easy case */ - p->strip[p->slen++] = SOP(op, opnd); -} - -/* - - doinsert - insert a sop into the strip - == static void doinsert(register struct parse *p, sop op, size_t opnd, sopno pos); - */ -static void -doinsert(p, op, opnd, pos) -register struct parse *p; -sop op; -size_t opnd; -sopno pos; -{ - register sopno sn; - register sop s; - register int i; - - /* avoid making error situations worse */ - if (p->error != 0) - return; - - sn = HERE(); - EMIT(op, opnd); /* do checks, ensure space */ - assert(HERE() == sn+1); - s = p->strip[sn]; - - /* adjust paren pointers */ - assert(pos > 0); - for (i = 1; i < NPAREN; i++) { - if (p->pbegin[i] >= pos) { - p->pbegin[i]++; - } - if (p->pend[i] >= pos) { - p->pend[i]++; - } - } - - memmove((char *)&p->strip[pos+1], (char *)&p->strip[pos], - (HERE()-pos-1)*sizeof(sop)); - p->strip[pos] = s; -} - -/* - - dofwd - complete a forward reference - == static void dofwd(register struct parse *p, sopno pos, sop value); - */ -static void -dofwd(p, pos, value) -register struct parse *p; -register sopno pos; -sop value; -{ - /* avoid making error situations worse */ - if (p->error != 0) - return; - - assert(value < 1<strip[pos] = OP(p->strip[pos]) | value; -} - -/* - - enlarge - enlarge the strip - == static void enlarge(register struct parse *p, sopno size); - */ -static void -enlarge(p, size) -register struct parse *p; -register sopno size; -{ - register sop *sp; - - if (p->ssize >= size) - return; - - sp = (sop *)realloc(p->strip, size*sizeof(sop)); - if (sp == NULL) { - SETERROR(REG_ESPACE); - return; - } - p->strip = sp; - p->ssize = size; -} - -/* - - stripsnug - compact the strip - == static void stripsnug(register struct parse *p, register struct re_guts *g); - */ -static void -stripsnug(p, g) -register struct parse *p; -register struct re_guts *g; -{ - g->nstates = p->slen; - g->strip = (sop *)realloc((char *)p->strip, p->slen * sizeof(sop)); - if (g->strip == NULL) { - SETERROR(REG_ESPACE); - g->strip = p->strip; - } -} - -/* - - findmust - fill in must and mlen with longest mandatory literal string - == static void findmust(register struct parse *p, register struct re_guts *g); - * - * This algorithm could do fancy things like analyzing the operands of | - * for common subsequences. Someday. This code is simple and finds most - * of the interesting cases. - * - * Note that must and mlen got initialized during setup. - */ -static void -findmust(p, g) -struct parse *p; -register struct re_guts *g; -{ - register sop *scan; - sop *start; - register sop *newstart; - register sopno newlen; - register sop s; - register char *cp; - register sopno i; - - /* avoid making error situations worse */ - if (p->error != 0) - return; - - /* find the longest OCHAR sequence in strip */ - newlen = 0; - scan = g->strip + 1; - do { - s = *scan++; - switch (OP(s)) { - case OCHAR: /* sequence member */ - if (newlen == 0) /* new sequence */ - newstart = scan - 1; - newlen++; - break; - case OPLUS_: /* things that don't break one */ - case OLPAREN: - case ORPAREN: - break; - case OQUEST_: /* things that must be skipped */ - case OCH_: - scan--; - do { - scan += OPND(s); - s = *scan; - /* assert() interferes w debug printouts */ - if (OP(s) != O_QUEST && OP(s) != O_CH && - OP(s) != OOR2) { - g->iflags |= BAD; - return; - } - } while (OP(s) != O_QUEST && OP(s) != O_CH); - /* fallthrough */ - default: /* things that break a sequence */ - if (newlen > g->mlen) { /* ends one */ - start = newstart; - g->mlen = newlen; - } - newlen = 0; - break; - } - } while (OP(s) != OEND); - - if (g->mlen == 0) /* there isn't one */ - return; - - /* turn it into a character string */ - g->must = malloc((size_t)g->mlen + 1); - if (g->must == NULL) { /* argh; just forget it */ - g->mlen = 0; - return; - } - cp = g->must; - scan = start; - for (i = g->mlen; i > 0; i--) { - while (OP(s = *scan++) != OCHAR) - continue; - assert(cp < g->must + g->mlen); - *cp++ = (char)OPND(s); - } - assert(cp == g->must + g->mlen); - *cp++ = '\0'; /* just on general principles */ -} - -/* - - pluscount - count + nesting - == static sopno pluscount(register struct parse *p, register struct re_guts *g); - */ -static sopno /* nesting depth */ -pluscount(p, g) -struct parse *p; -register struct re_guts *g; -{ - register sop *scan; - register sop s; - register sopno plusnest = 0; - register sopno maxnest = 0; - - if (p->error != 0) - return(0); /* there may not be an OEND */ - - scan = g->strip + 1; - do { - s = *scan++; - switch (OP(s)) { - case OPLUS_: - plusnest++; - break; - case O_PLUS: - if (plusnest > maxnest) - maxnest = plusnest; - plusnest--; - break; - } - } while (OP(s) != OEND); - if (plusnest != 0) - g->iflags |= BAD; - return(maxnest); -} diff --git a/sys/amiga/regex/regerror.c b/sys/amiga/regex/regerror.c deleted file mode 100755 index 54c1333d9..000000000 --- a/sys/amiga/regex/regerror.c +++ /dev/null @@ -1,186 +0,0 @@ -/* $NetBSD: regerror.c,v 1.4 1995/02/27 13:29:20 cgd Exp $ */ - -/*- - * Copyright (c) 1992, 1993, 1994 Henry Spencer. - * Copyright (c) 1992, 1993, 1994 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Henry Spencer. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)regerror.c 8.4 (Berkeley) 3/20/94 - */ - -#if defined(LIBC_SCCS) && !defined(lint) -#if 0 -static char sccsid[] = "@(#)regerror.c 8.4 (Berkeley) 3/20/94"; -#else -static char rcsid[] = "$NetBSD: regerror.c,v 1.4 1995/02/27 13:29:20 cgd Exp $"; -#endif -#endif /* LIBC_SCCS and not lint */ - -#include -#include -#include -#include -#include -#include -#include - -#include "utils.h" - -/* ========= begin header generated by ./mkh ========= */ -#ifdef __cplusplus -extern "C" { -#endif - -/* === regerror.c === */ -static char *regatoi __P((const regex_t *preg, char *localbuf)); - -#ifdef __cplusplus -} -#endif -/* ========= end header generated by ./mkh ========= */ -/* - = #define REG_NOMATCH 1 - = #define REG_BADPAT 2 - = #define REG_ECOLLATE 3 - = #define REG_ECTYPE 4 - = #define REG_EESCAPE 5 - = #define REG_ESUBREG 6 - = #define REG_EBRACK 7 - = #define REG_EPAREN 8 - = #define REG_EBRACE 9 - = #define REG_BADBR 10 - = #define REG_ERANGE 11 - = #define REG_ESPACE 12 - = #define REG_BADRPT 13 - = #define REG_EMPTY 14 - = #define REG_ASSERT 15 - = #define REG_INVARG 16 - = #define REG_ATOI 255 // convert name to number (!) - = #define REG_ITOA 0400 // convert number to name (!) - */ -static struct rerr { - int code; - char *name; - char *explain; -} rerrs[] = { - REG_NOMATCH, "REG_NOMATCH", "regexec() failed to match", - REG_BADPAT, "REG_BADPAT", "invalid regular expression", - REG_ECOLLATE, "REG_ECOLLATE", "invalid collating element", - REG_ECTYPE, "REG_ECTYPE", "invalid character class", - REG_EESCAPE, "REG_EESCAPE", "trailing backslash (\\)", - REG_ESUBREG, "REG_ESUBREG", "invalid backreference number", - REG_EBRACK, "REG_EBRACK", "brackets ([ ]) not balanced", - REG_EPAREN, "REG_EPAREN", "parentheses not balanced", - REG_EBRACE, "REG_EBRACE", "braces not balanced", - REG_BADBR, "REG_BADBR", "invalid repetition count(s)", - REG_ERANGE, "REG_ERANGE", "invalid character range", - REG_ESPACE, "REG_ESPACE", "out of memory", - REG_BADRPT, "REG_BADRPT", "repetition-operator operand invalid", - REG_EMPTY, "REG_EMPTY", "empty (sub)expression", - REG_ASSERT, "REG_ASSERT", "\"can't happen\" -- you found a bug", - REG_INVARG, "REG_INVARG", "invalid argument to regex routine", - 0, "", "*** unknown regexp error code ***", -}; - -/* - - regerror - the interface to error numbers - = extern size_t regerror(int, const regex_t *, char *, size_t); - */ -/* ARGSUSED */ -size_t -regerror(errcode, preg, errbuf, errbuf_size) -int errcode; -const regex_t *preg; -char *errbuf; -size_t errbuf_size; -{ - register struct rerr *r; - register size_t len; - register int target = errcode &~ REG_ITOA; - register char *s; - char convbuf[50]; - - if (errcode == REG_ATOI) - s = regatoi(preg, convbuf); - else { - for (r = rerrs; r->code != 0; r++) - if (r->code == target) - break; - - if (errcode®_ITOA) { - if (r->code != 0) - (void) strcpy(convbuf, r->name); - else - sprintf(convbuf, "REG_0x%x", target); - assert(strlen(convbuf) < sizeof(convbuf)); - s = convbuf; - } else - s = r->explain; - } - - len = strlen(s) + 1; - if (errbuf_size > 0) { - if (errbuf_size > len) - (void) strcpy(errbuf, s); - else { - (void) strncpy(errbuf, s, errbuf_size-1); - errbuf[errbuf_size-1] = '\0'; - } - } - - return(len); -} - -/* - - regatoi - internal routine to implement REG_ATOI - == static char *regatoi(const regex_t *preg, char *localbuf); - */ -static char * -regatoi(preg, localbuf) -const regex_t *preg; -char *localbuf; -{ - register struct rerr *r; - register size_t siz; - register char *p; - - for (r = rerrs; r->code != 0; r++) - if (strcmp(r->name, preg->re_endp) == 0) - break; - if (r->code == 0) - return("0"); - - sprintf(localbuf, "%d", r->code); - return(localbuf); -} diff --git a/sys/amiga/regex/regex2.h b/sys/amiga/regex/regex2.h deleted file mode 100755 index 901a6393b..000000000 --- a/sys/amiga/regex/regex2.h +++ /dev/null @@ -1,175 +0,0 @@ -/* $NetBSD: regex2.h,v 1.5 1995/02/27 13:29:40 cgd Exp $ */ - -/*- - * Copyright (c) 1992, 1993, 1994 Henry Spencer. - * Copyright (c) 1992, 1993, 1994 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Henry Spencer. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)regex2.h 8.4 (Berkeley) 3/20/94 - */ - -/* - * First, the stuff that ends up in the outside-world include file - = typedef off_t regoff_t; - = typedef struct { - = int re_magic; - = size_t re_nsub; // number of parenthesized subexpressions - = const char *re_endp; // end pointer for REG_PEND - = struct re_guts *re_g; // none of your business :-) - = } regex_t; - = typedef struct { - = regoff_t rm_so; // start of match - = regoff_t rm_eo; // end of match - = } regmatch_t; - */ -/* - * internals of regex_t - */ -#define MAGIC1 ((('r'^0200)<<8) | 'e') - -/* - * The internal representation is a *strip*, a sequence of - * operators ending with an endmarker. (Some terminology etc. is a - * historical relic of earlier versions which used multiple strips.) - * Certain oddities in the representation are there to permit running - * the machinery backwards; in particular, any deviation from sequential - * flow must be marked at both its source and its destination. Some - * fine points: - * - * - OPLUS_ and O_PLUS are *inside* the loop they create. - * - OQUEST_ and O_QUEST are *outside* the bypass they create. - * - OCH_ and O_CH are *outside* the multi-way branch they create, while - * OOR1 and OOR2 are respectively the end and the beginning of one of - * the branches. Note that there is an implicit OOR2 following OCH_ - * and an implicit OOR1 preceding O_CH. - * - * In state representations, an operator's bit is on to signify a state - * immediately *preceding* "execution" of that operator. - */ -typedef unsigned long sop; /* strip operator */ -typedef long sopno; -#define OPRMASK 0xf8000000 -#define OPDMASK 0x07ffffff -#define OPSHIFT ((unsigned)27) -#define OP(n) ((n)&OPRMASK) -#define OPND(n) ((n)&OPDMASK) -#define SOP(op, opnd) ((op)|(opnd)) -/* operators meaning operand */ -/* (back, fwd are offsets) */ -#define OEND (1< uch [csetsize] */ - uch mask; /* bit within array */ - uch hash; /* hash code */ - size_t smultis; - char *multis; /* -> char[smulti] ab\0cd\0ef\0\0 */ -} cset; -/* note that CHadd and CHsub are unsafe, and CHIN doesn't yield 0/1 */ -#define CHadd(cs, c) ((cs)->ptr[(uch)(c)] |= (cs)->mask, (cs)->hash += (c)) -#define CHsub(cs, c) ((cs)->ptr[(uch)(c)] &= ~(cs)->mask, (cs)->hash -= (c)) -#define CHIN(cs, c) ((cs)->ptr[(uch)(c)] & (cs)->mask) -#define MCadd(p, cs, cp) mcadd(p, cs, cp) /* regcomp() internal fns */ -#define MCsub(p, cs, cp) mcsub(p, cs, cp) -#define MCin(p, cs, cp) mcin(p, cs, cp) - -/* stuff for character categories */ -typedef unsigned char cat_t; - -/* - * main compiled-expression structure - */ -struct re_guts { - int magic; -# define MAGIC2 ((('R'^0200)<<8)|'E') - sop *strip; /* malloced area for strip */ - int csetsize; /* number of bits in a cset vector */ - int ncsets; /* number of csets in use */ - cset *sets; /* -> cset [ncsets] */ - uch *setbits; /* -> uch[csetsize][ncsets/CHAR_BIT] */ - int cflags; /* copy of regcomp() cflags argument */ - sopno nstates; /* = number of sops */ - sopno firststate; /* the initial OEND (normally 0) */ - sopno laststate; /* the final OEND */ - int iflags; /* internal flags */ -# define USEBOL 01 /* used ^ */ -# define USEEOL 02 /* used $ */ -# define BAD 04 /* something wrong */ - int nbol; /* number of ^ used */ - int neol; /* number of $ used */ - int ncategories; /* how many character categories */ - cat_t *categories; /* ->catspace[-CHAR_MIN] */ - char *must; /* match must contain this string */ - int mlen; /* length of must */ - size_t nsub; /* copy of re_nsub */ - int backrefs; /* does it use back references? */ - sopno nplus; /* how deep does it nest +s? */ - /* catspace must be last */ - cat_t catspace[1]; /* actually [NC] */ -}; - -/* misc utilities */ -#define OUT (CHAR_MAX+1) /* a non-character value */ -#define ISWORD(c) (isalnum(c) || (c) == '_') diff --git a/sys/amiga/regex/regexec.c b/sys/amiga/regex/regexec.c deleted file mode 100755 index 3b8ef5341..000000000 --- a/sys/amiga/regex/regexec.c +++ /dev/null @@ -1,187 +0,0 @@ -/* $NetBSD: regexec.c,v 1.6 1995/02/27 13:29:48 cgd Exp $ */ - -/*- - * Copyright (c) 1992, 1993, 1994 Henry Spencer. - * Copyright (c) 1992, 1993, 1994 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Henry Spencer. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)regexec.c 8.3 (Berkeley) 3/20/94 - */ - -#if defined(LIBC_SCCS) && !defined(lint) -#if 0 -static char sccsid[] = "@(#)regexec.c 8.3 (Berkeley) 3/20/94"; -#else -static char rcsid[] = "$NetBSD: regexec.c,v 1.6 1995/02/27 13:29:48 cgd Exp $"; -#endif -#endif /* LIBC_SCCS and not lint */ - -/* - * the outer shell of regexec() - * - * This file includes engine.h *twice*, after muchos fiddling with the - * macros that code uses. This lets the same code operate on two different - * representations for state sets. - */ -#include -#include -#include -#include -#include -#include -#include - -#include "utils.h" -#include "regex2.h" - -static int nope = 0; /* for use in asserts; shuts lint up */ - -/* macros for manipulating states, small version */ -#define states long -#define states1 states /* for later use in regexec() decision */ -#define CLEAR(v) ((v) = 0) -#define SET0(v, n) ((v) &= ~((unsigned long)1 << (n))) -#define SET1(v, n) ((v) |= (unsigned long)1 << (n)) -#define ISSET(v, n) (((v) & ((unsigned long)1 << (n))) != 0) -#define ASSIGN(d, s) ((d) = (s)) -#define EQ(a, b) ((a) == (b)) -#define STATEVARS long dummy /* dummy version */ -#define STATESETUP(m, n) /* nothing */ -#define STATETEARDOWN(m) /* nothing */ -#define SETUP(v) ((v) = 0) -#define onestate long -#define INIT(o, n) ((o) = (unsigned long)1 << (n)) -#define INC(o) ((o) = (unsigned long)(o) << 1) -#define ISSTATEIN(v, o) (((v) & (o)) != 0) -/* some abbreviations; note that some of these know variable names! */ -/* do "if I'm here, I can also be there" etc without branches */ -#define FWD(dst, src, n) ((dst) |= ((unsigned long)(src)&(here)) << (n)) -#define BACK(dst, src, n) ((dst) |= ((unsigned long)(src)&(here)) >> (n)) -#define ISSETBACK(v, n) (((v) & ((unsigned long)here >> (n))) != 0) -/* function names */ -#define SNAMES /* engine.h looks after details */ - -#include "engine.h" - -/* now undo things */ -#undef states -#undef CLEAR -#undef SET0 -#undef SET1 -#undef ISSET -#undef ASSIGN -#undef EQ -#undef STATEVARS -#undef STATESETUP -#undef STATETEARDOWN -#undef SETUP -#undef onestate -#undef INIT -#undef INC -#undef ISSTATEIN -#undef FWD -#undef BACK -#undef ISSETBACK -#undef SNAMES - -/* macros for manipulating states, large version */ -#define states char * -#define CLEAR(v) memset(v, 0, m->g->nstates) -#define SET0(v, n) ((v)[n] = 0) -#define SET1(v, n) ((v)[n] = 1) -#define ISSET(v, n) ((v)[n]) -#define ASSIGN(d, s) memcpy(d, s, m->g->nstates) -#define EQ(a, b) (memcmp(a, b, m->g->nstates) == 0) -#define STATEVARS long vn; char *space -#define STATESETUP(m, nv) { (m)->space = malloc((nv)*(m)->g->nstates); \ - if ((m)->space == NULL) return(REG_ESPACE); \ - (m)->vn = 0; } -#define STATETEARDOWN(m) { free((m)->space); } -#define SETUP(v) ((v) = &m->space[m->vn++ * m->g->nstates]) -#define onestate long -#define INIT(o, n) ((o) = (n)) -#define INC(o) ((o)++) -#define ISSTATEIN(v, o) ((v)[o]) -/* some abbreviations; note that some of these know variable names! */ -/* do "if I'm here, I can also be there" etc without branches */ -#define FWD(dst, src, n) ((dst)[here+(n)] |= (src)[here]) -#define BACK(dst, src, n) ((dst)[here-(n)] |= (src)[here]) -#define ISSETBACK(v, n) ((v)[here - (n)]) -/* function names */ -#define LNAMES /* flag */ - -#include "engine.h" - -/* - - regexec - interface for matching - = extern int regexec(const regex_t *, const char *, size_t, \ - = regmatch_t [], int); - = #define REG_NOTBOL 00001 - = #define REG_NOTEOL 00002 - = #define REG_STARTEND 00004 - = #define REG_TRACE 00400 // tracing of execution - = #define REG_LARGE 01000 // force large representation - = #define REG_BACKR 02000 // force use of backref code - * - * We put this here so we can exploit knowledge of the state representation - * when choosing which matcher to call. Also, by this point the matchers - * have been prototyped. - */ -int /* 0 success, REG_NOMATCH failure */ -regexec(preg, string, nmatch, pmatch, eflags) -const regex_t *preg; -const char *string; -size_t nmatch; -regmatch_t pmatch[]; -int eflags; -{ - register struct re_guts *g = preg->re_g; -#ifdef REDEBUG -# define GOODFLAGS(f) (f) -#else -# define GOODFLAGS(f) ((f)&(REG_NOTBOL|REG_NOTEOL|REG_STARTEND)) -#endif - - if (preg->re_magic != MAGIC1 || g->magic != MAGIC2) - return(REG_BADPAT); - assert(!(g->iflags&BAD)); - if (g->iflags&BAD) /* backstop for no-debug case */ - return(REG_BADPAT); - eflags = GOODFLAGS(eflags); - - if (g->nstates <= CHAR_BIT*sizeof(states1) && !(eflags®_LARGE)) - return(smatcher(g, (char *)string, nmatch, pmatch, eflags)); - else - return(lmatcher(g, (char *)string, nmatch, pmatch, eflags)); -} diff --git a/sys/amiga/regex/regfree.c b/sys/amiga/regex/regfree.c deleted file mode 100755 index e01e30627..000000000 --- a/sys/amiga/regex/regfree.c +++ /dev/null @@ -1,86 +0,0 @@ -/* $NetBSD: regfree.c,v 1.4 1995/02/27 13:29:56 cgd Exp $ */ - -/*- - * Copyright (c) 1992, 1993, 1994 Henry Spencer. - * Copyright (c) 1992, 1993, 1994 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Henry Spencer. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)regfree.c 8.3 (Berkeley) 3/20/94 - */ - -#if defined(LIBC_SCCS) && !defined(lint) -#if 0 -static char sccsid[] = "@(#)regfree.c 8.3 (Berkeley) 3/20/94"; -#else -static char rcsid[] = "$NetBSD: regfree.c,v 1.4 1995/02/27 13:29:56 cgd Exp $"; -#endif -#endif /* LIBC_SCCS and not lint */ - -#include -#include -#include -#include - -#include "utils.h" -#include "regex2.h" - -/* - - regfree - free everything - = extern void regfree(regex_t *); - */ -void -regfree(preg) -regex_t *preg; -{ - register struct re_guts *g; - - if (preg->re_magic != MAGIC1) /* oops */ - return; /* nice to complain, but hard */ - - g = preg->re_g; - if (g == NULL || g->magic != MAGIC2) /* oops again */ - return; - preg->re_magic = 0; /* mark it invalid */ - g->magic = 0; /* mark it invalid */ - - if (g->strip != NULL) - free((char *)g->strip); - if (g->sets != NULL) - free((char *)g->sets); - if (g->setbits != NULL) - free((char *)g->setbits); - if (g->must != NULL) - free(g->must); - free((char *)g); -} diff --git a/sys/amiga/regex/utils.h b/sys/amiga/regex/utils.h deleted file mode 100755 index 2f372ce29..000000000 --- a/sys/amiga/regex/utils.h +++ /dev/null @@ -1,59 +0,0 @@ -/* $NetBSD: utils.h,v 1.5 1995/02/27 13:29:59 cgd Exp $ */ - -/*- - * Copyright (c) 1992, 1993, 1994 Henry Spencer. - * Copyright (c) 1992, 1993, 1994 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Henry Spencer. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)utils.h 8.3 (Berkeley) 3/20/94 - */ - -/* utility definitions */ -#define DUPMAX _POSIX2_RE_DUP_MAX /* xxx is this right? */ -#define INFINITY (DUPMAX + 1) -#define NC (CHAR_MAX - CHAR_MIN + 1) -typedef unsigned char uch; - -/* switch off assertions (if not already off) if no REDEBUG */ -#ifndef REDEBUG -#ifndef NDEBUG -#define NDEBUG /* no assertions please */ -#endif -#endif -#include - -/* for old systems with bcopy() but no memmove() */ -#ifdef USEBCOPY -#define memmove(d, s, c) bcopy(s, d, c) -#endif diff --git a/sys/unix/hints/include/cross-post.370 b/sys/unix/hints/include/cross-post.370 index 9d7b3a313..e3b792edd 100644 --- a/sys/unix/hints/include/cross-post.370 +++ b/sys/unix/hints/include/cross-post.370 @@ -269,7 +269,39 @@ $(TARGETPFX)tiles32.iff: ../dat/nhtiles.bmp $(TARGETPFX)bmp2iff_host AMITILES = $(TARGETPFX)tiles16.iff $(TARGETPFX)tiles32.iff $(TARGETPFX)tomb.iff -.PHONY: amigapkg amitiles +AMIREGEX_URL = https://github.com/garyhouston/regex.git +AMIREGEX_SRCDIR = $(AMISRC)/regex + +.PHONY: fetch-regex amigapkg amitiles + +fetch-regex: + @DSTDIR=sys/amiga/regex; \ + if [ ! -d src ]; then DSTDIR=../$$DSTDIR; fi; \ + if [ -f $$DSTDIR/regcomp.c ]; then \ + echo "BSD regex already present"; \ + else \ + echo "Fetching BSD regex from $(AMIREGEX_URL)"; \ + tmpdir=$$(mktemp -d) && \ + git clone --depth 1 $(AMIREGEX_URL) $$tmpdir && \ + cd $$tmpdir && \ + sh ./mkh -p regcomp.c > regcomp.ih && \ + sh ./mkh -p engine.c > engine.ih && \ + sh ./mkh -p regexec.c > regexec.ih && \ + sh ./mkh -p regerror.c > regerror.ih && \ + sh ./mkh -i _REGEX_H_ regex2.h regcomp.c \ + regexec.c regerror.c regfree.c > regex.h && \ + cd - > /dev/null && \ + mkdir -p $$DSTDIR && \ + for f in regcomp.c regexec.c regerror.c regfree.c \ + engine.c regex.h regex2.h cclass.h cname.h \ + utils.h regcomp.ih engine.ih regexec.ih \ + regerror.ih \ + COPYRIGHT; do \ + cp $$tmpdir/$$f $$DSTDIR/; \ + done && \ + rm -rf $$tmpdir && \ + echo "BSD regex installed in $$DSTDIR"; \ + fi amitiles: $(AMITILES) amigapkg: $(AMITILES) diff --git a/sys/unix/hints/include/cross-pre2.370 b/sys/unix/hints/include/cross-pre2.370 index d27301af1..9e873e978 100644 --- a/sys/unix/hints/include/cross-pre2.370 +++ b/sys/unix/hints/include/cross-pre2.370 @@ -472,6 +472,7 @@ ifdef CROSS_TO_AMIGA # Install to /opt/amiga, then: # sys/unix/setup.sh sys/unix/hints/linux.370 # make fetch-lua +# make CROSS_TO_AMIGA=1 fetch-regex # make CROSS_TO_AMIGA=1 all # make CROSS_TO_AMIGA=1 package #================================================================= From 2979030af146e65bfbab693073d649310530180b Mon Sep 17 00:00:00 2001 From: Ingo Paschke Date: Tue, 24 Mar 2026 02:37:02 +0100 Subject: [PATCH 344/442] Use NetHack's util/uudecode instead of system uudecode --- sys/unix/hints/include/cross-post.370 | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/sys/unix/hints/include/cross-post.370 b/sys/unix/hints/include/cross-post.370 index e3b792edd..f28ec0fe1 100644 --- a/sys/unix/hints/include/cross-post.370 +++ b/sys/unix/hints/include/cross-post.370 @@ -304,7 +304,12 @@ fetch-regex: fi amitiles: $(AMITILES) -amigapkg: $(AMITILES) +UUDECODE = ../util/uudecode + +../util/uudecode: ../sys/share/uudecode.c + $(CC) $(CFLAGS) -o $@ $< + +amigapkg: $(AMITILES) ../util/uudecode mkdir -p $(TARGETPFX)pkg/tiles $(TARGETPFX)pkg/hack cp $(GAMEBIN) $(TARGETPFX)pkg/nethack cp ../dat/nhdat $(TARGETPFX)pkg/nhdat @@ -315,17 +320,17 @@ amigapkg: $(AMITILES) cp $(TARGETPFX)tomb.iff $(TARGETPFX)pkg/tomb.iff cp ../sys/msdos/sysconf $(TARGETPFX)pkg/sysconf cp ../doc/nethack.txt $(TARGETPFX)pkg/nethack.txt - ( cd $(TARGETPFX)pkg && uudecode ../../../sys/amiga/amifont8.uu && mv 8 hack/8 ) - ( cd $(TARGETPFX)pkg && uudecode ../../../sys/amiga/amifont.uu ) + ( cd $(TARGETPFX)pkg && ../../../util/uudecode ../../../sys/amiga/amifont8.uu && mv 8 hack/8 ) + ( cd $(TARGETPFX)pkg && ../../../util/uudecode ../../../sys/amiga/amifont.uu ) cp $(AMISRC)/nethack.cnf $(TARGETPFX)pkg/nethack.cnf -( cd $(TARGETPFX)pkg && test -f ../../../sys/amiga/dflticon.uu && \ - uudecode ../../../sys/amiga/dflticon.uu ) + ../../../util/uudecode ../../../sys/amiga/dflticon.uu ) -( cd $(TARGETPFX)pkg && test -f ../../../sys/amiga/NHinfo.uu && \ - uudecode ../../../sys/amiga/NHinfo.uu ) + ../../../util/uudecode ../../../sys/amiga/NHinfo.uu ) -( cd $(TARGETPFX)pkg && test -f ../../../sys/amiga/NewGame.uu && \ - uudecode ../../../sys/amiga/NewGame.uu ) + ../../../util/uudecode ../../../sys/amiga/NewGame.uu ) -( cd $(TARGETPFX)pkg && test -f ../../../sys/amiga/HackWB.uu && \ - uudecode ../../../sys/amiga/HackWB.uu ) + ../../../util/uudecode ../../../sys/amiga/HackWB.uu ) touch $(TARGETPFX)pkg/record ( cd $(TARGETPFX)pkg && zip -9r ../NH370AMI.ZIP * ) @echo amiga package zip file $(TARGETPFX)NH370AMI.ZIP From 14c5f318e1316f96d4b2166e6431bd72fdd381a7 Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 24 Mar 2026 05:43:46 -0400 Subject: [PATCH 345/442] Add a fixes3-7-0.txt entry for Amiga build pr1494 Build instructions in sys/amiga/README.amiga The Amiga cross-compile support makes the following file changes to the repository: rename {outdated/include => include}/amiconf.h (96%) create mode 100644 sys/amiga/README.amiga rename {outdated/sys => sys}/amiga/amidos.c (91%) rename {outdated/sys => sys}/amiga/amidos.p (100%) rename {outdated/sys => sys}/amiga/amifont.uu (100%) rename {outdated/sys => sys}/amiga/amifont8.uu (100%) rename {outdated/sys => sys}/amiga/amigst.c (100%) rename {outdated/sys => sys}/amiga/amii.hlp (100%) rename {outdated/sys => sys}/amiga/amimenu.c (100%) rename {outdated/sys => sys}/amiga/amirip.c (93%) rename {outdated/sys => sys}/amiga/amistack.c (55%) rename {outdated/sys => sys}/amiga/amitty.c (97%) rename {outdated/sys => sys}/amiga/amiwind.c (90%) rename {outdated/sys => sys}/amiga/amiwind.p (100%) create mode 100644 sys/amiga/bmp2iff_host.c rename {outdated/sys => sys}/amiga/clipwin.c (100%) rename {outdated/sys => sys}/amiga/colorwin.c (100%) rename {outdated/sys => sys}/amiga/grave16.xpm (100%) create mode 100644 sys/amiga/nethack.cnf rename {outdated/sys => sys}/amiga/winamenu.c (94%) rename {outdated/sys => sys}/amiga/winami.c (94%) rename {outdated/sys => sys}/amiga/winami.p (87%) rename {outdated/sys => sys}/amiga/winchar.c (78%) rename {outdated/sys => sys}/amiga/windefs.h (98%) rename {outdated/sys => sys}/amiga/winext.h (100%) rename {outdated/sys => sys}/amiga/winfuncs.c (86%) rename {outdated/sys => sys}/amiga/winkey.c (89%) rename {outdated/sys => sys}/amiga/winproto.h (88%) rename {outdated/sys => sys}/amiga/winreq.c (95%) rename {outdated/sys => sys}/amiga/winstr.c (95%) create mode 100644 sys/amiga/xpm2iff_host.c --- doc/fixes3-7-0.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 9d7415ba5..1428c1500 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -3121,6 +3121,7 @@ applied several source comment spelling or grammar fixes that were linked modernize code of LaTeX Guidebook (pr #1473 by Daniel Clow) restore ability for a characteristic also resets exercise/abuse for that characteristic (pr #1403 by greg-kennedy) +cross-compilation for the Amiga on Linux (pr #1494 by ingpaschke) Code Cleanup and Reorganization From 507f526b4302377b3007c3dd15e130f297036a12 Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 24 Mar 2026 05:48:51 -0400 Subject: [PATCH 346/442] remove duplicate entry in .gitignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 68c544da6..5615fe332 100644 --- a/.gitignore +++ b/.gitignore @@ -97,4 +97,3 @@ util/*.lib util/*.exp submodules/CHKSUMS.tmp sys/amiga/regex/ -sys/amiga/regex/ From 875efba21098d1e79c334c1e5bc56c316997e7d7 Mon Sep 17 00:00:00 2001 From: nhw_cron Date: Tue, 24 Mar 2026 06:16:07 -0400 Subject: [PATCH 347/442] This is cron-daily v1-Jan-12-2026. 000files updated: Files --- Files | 56 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/Files b/Files index 7154e17c2..355da2305 100644 --- a/Files +++ b/Files @@ -87,23 +87,23 @@ include: tile2x11.h winX.h xwindow.h xwindowp.h (files for all versions) -align.h artifact.h artilist.h attrib.h botl.h -color.h config.h config1.h context.h coord.h -cstd.h decl.h defsym.h dgn_file.h display.h -dlb.h dungeon.h engrave.h extern.h flag.h -fnamesiz.h func_tab.h global.h hack.h hacklib.h -integer.h isaac64.h lint.h mail.h mextra.h -mfndpos.h micro.h mkroom.h monattk.h mondata.h -monflag.h monst.h monsters.h nhmd4.h nhregex.h -obj.h objclass.h objects.h optlist.h patchlevel.h -pcconf.h permonst.h prop.h quest.h rect.h -region.h rm.h savefile.h seffects.h selvar.h -sfmacros.h sfprocs.h skills.h sndprocs.h sp_lev.h -spell.h stairs.h sym.h sys.h tcap.h -tileset.h timeout.h tradstdc.h trap.h unixconf.h -vision.h vmsconf.h warnings.h weight.h winami.h -wincurs.h windconf.h winprocs.h wintype.h you.h -youprop.h +align.h amiconf.h artifact.h artilist.h attrib.h +botl.h color.h config.h config1.h context.h +coord.h cstd.h decl.h defsym.h dgn_file.h +display.h dlb.h dungeon.h engrave.h extern.h +flag.h fnamesiz.h func_tab.h global.h hack.h +hacklib.h integer.h isaac64.h lint.h mail.h +mextra.h mfndpos.h micro.h mkroom.h monattk.h +mondata.h monflag.h monst.h monsters.h nhmd4.h +nhregex.h obj.h objclass.h objects.h optlist.h +patchlevel.h pcconf.h permonst.h prop.h quest.h +rect.h region.h rm.h savefile.h seffects.h +selvar.h sfmacros.h sfprocs.h skills.h sndprocs.h +sp_lev.h spell.h stairs.h sym.h sys.h +tcap.h tileset.h timeout.h tradstdc.h trap.h +unixconf.h vision.h vmsconf.h warnings.h weight.h +winami.h wincurs.h windconf.h winprocs.h wintype.h +you.h youprop.h (file for tty versions) wintty.h @@ -124,8 +124,8 @@ bitmfile.h gem_rsc.h load_img.h wingem.h winGnome.h (files for all versions) -amiconf.h beconf.h def_os2.h os2conf.h system.h tosconf.h -trampoli.h wceconf.h +beconf.h def_os2.h os2conf.h system.h tosconf.h trampoli.h +wceconf.h (files for various Macintosh versions) mac-carbon.h mac-qt.h mac-term.h macconf.h macpopup.h @@ -134,12 +134,7 @@ mactty.h macwin.h mttypriv.h outdated/sys/amiga: (files for Amiga versions - untested for 3.7) Build.ami Install.ami Makefile.agc Makefile.ami NetHack.cnf -amidos.c amidos.p amifont.uu amifont8.uu amigst.c -amii.hlp amimenu.c amirip.c amistack.c amitty.c -amiwind.c amiwind.p clipwin.c colorwin.c grave16.xpm -ifchange mkdmake txt2iff.c winamenu.c winami.c -winami.p winchar.c windefs.h winext.h winfuncs.c -winkey.c winproto.h winreq.c winstr.c xpm2iff.c +ifchange mkdmake txt2iff.c xpm2iff.c outdated/sys/atari: (files for Atari version - untested for 3.7) @@ -319,6 +314,17 @@ submodules: (files in top directory) CHKSUMS lua pdcurses pdcursesmod +sys/amiga: +(files in top directory) +README.amiga amidos.c amidos.p amifont.uu +amifont8.uu amigst.c amii.hlp amimenu.c +amirip.c amistack.c amitty.c amiwind.c +amiwind.p bmp2iff_host.c clipwin.c colorwin.c +grave16.xpm nethack.cnf winamenu.c winami.c +winami.p winchar.c windefs.h winext.h +winfuncs.c winkey.c winproto.h winreq.c +winstr.c xpm2iff_host.c + sys/libnh: (files in top directory) README.md libnhmain.c sysconf From 7f8f7c54dca0ca80fd9e45724118d224eed65b50 Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 24 Mar 2026 07:13:04 -0400 Subject: [PATCH 348/442] Files identification update --- sys/amiga/.gitattributes | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 sys/amiga/.gitattributes diff --git a/sys/amiga/.gitattributes b/sys/amiga/.gitattributes new file mode 100644 index 000000000..45572b7d6 --- /dev/null +++ b/sys/amiga/.gitattributes @@ -0,0 +1,2 @@ +*.p NHSUBST +* NH_filestag=(file%s_for_Amiga_versions) From ab04f5455d75596643374dcda5ba669bf516c253 Mon Sep 17 00:00:00 2001 From: nhw_cron Date: Tue, 24 Mar 2026 07:16:06 -0400 Subject: [PATCH 349/442] This is cron-daily v1-Jan-12-2026. 000files updated: Files --- Files | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Files b/Files index 355da2305..3bbf8912e 100644 --- a/Files +++ b/Files @@ -315,7 +315,7 @@ submodules: CHKSUMS lua pdcurses pdcursesmod sys/amiga: -(files in top directory) +(files for Amiga versions) README.amiga amidos.c amidos.p amifont.uu amifont8.uu amigst.c amii.hlp amimenu.c amirip.c amistack.c amitty.c amiwind.c From a398a9bdc7419a0b05077aa86490a8820ea62a1e Mon Sep 17 00:00:00 2001 From: Ingo Paschke Date: Tue, 24 Mar 2026 12:49:14 +0100 Subject: [PATCH 350/442] Use NetHack's util/uudecode instead of system uudecode --- sys/unix/hints/include/cross-post.370 | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/sys/unix/hints/include/cross-post.370 b/sys/unix/hints/include/cross-post.370 index e3b792edd..f28ec0fe1 100644 --- a/sys/unix/hints/include/cross-post.370 +++ b/sys/unix/hints/include/cross-post.370 @@ -304,7 +304,12 @@ fetch-regex: fi amitiles: $(AMITILES) -amigapkg: $(AMITILES) +UUDECODE = ../util/uudecode + +../util/uudecode: ../sys/share/uudecode.c + $(CC) $(CFLAGS) -o $@ $< + +amigapkg: $(AMITILES) ../util/uudecode mkdir -p $(TARGETPFX)pkg/tiles $(TARGETPFX)pkg/hack cp $(GAMEBIN) $(TARGETPFX)pkg/nethack cp ../dat/nhdat $(TARGETPFX)pkg/nhdat @@ -315,17 +320,17 @@ amigapkg: $(AMITILES) cp $(TARGETPFX)tomb.iff $(TARGETPFX)pkg/tomb.iff cp ../sys/msdos/sysconf $(TARGETPFX)pkg/sysconf cp ../doc/nethack.txt $(TARGETPFX)pkg/nethack.txt - ( cd $(TARGETPFX)pkg && uudecode ../../../sys/amiga/amifont8.uu && mv 8 hack/8 ) - ( cd $(TARGETPFX)pkg && uudecode ../../../sys/amiga/amifont.uu ) + ( cd $(TARGETPFX)pkg && ../../../util/uudecode ../../../sys/amiga/amifont8.uu && mv 8 hack/8 ) + ( cd $(TARGETPFX)pkg && ../../../util/uudecode ../../../sys/amiga/amifont.uu ) cp $(AMISRC)/nethack.cnf $(TARGETPFX)pkg/nethack.cnf -( cd $(TARGETPFX)pkg && test -f ../../../sys/amiga/dflticon.uu && \ - uudecode ../../../sys/amiga/dflticon.uu ) + ../../../util/uudecode ../../../sys/amiga/dflticon.uu ) -( cd $(TARGETPFX)pkg && test -f ../../../sys/amiga/NHinfo.uu && \ - uudecode ../../../sys/amiga/NHinfo.uu ) + ../../../util/uudecode ../../../sys/amiga/NHinfo.uu ) -( cd $(TARGETPFX)pkg && test -f ../../../sys/amiga/NewGame.uu && \ - uudecode ../../../sys/amiga/NewGame.uu ) + ../../../util/uudecode ../../../sys/amiga/NewGame.uu ) -( cd $(TARGETPFX)pkg && test -f ../../../sys/amiga/HackWB.uu && \ - uudecode ../../../sys/amiga/HackWB.uu ) + ../../../util/uudecode ../../../sys/amiga/HackWB.uu ) touch $(TARGETPFX)pkg/record ( cd $(TARGETPFX)pkg && zip -9r ../NH370AMI.ZIP * ) @echo amiga package zip file $(TARGETPFX)NH370AMI.ZIP From ced24f6fc9a9fd47ce7ec6a8c3d453741450e9cf Mon Sep 17 00:00:00 2001 From: Ingo Paschke Date: Tue, 24 Mar 2026 12:49:14 +0100 Subject: [PATCH 351/442] Remove dead -DLUA_32BITS=1 flag luaconf.h hardcodes #define LUA_32BITS 0 which overrides the command-line flag. m68k-amigaos-gcc supports long long so 32-bit mode is not needed anyway. --- sys/unix/hints/include/cross-pre2.370 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/unix/hints/include/cross-pre2.370 b/sys/unix/hints/include/cross-pre2.370 index 9e873e978..3476682ad 100644 --- a/sys/unix/hints/include/cross-pre2.370 +++ b/sys/unix/hints/include/cross-pre2.370 @@ -501,7 +501,7 @@ override TARGET_CFLAGS = -c -O2 -noixemul $(TOOLARCH) \ -DCROSSCOMPILE -DCROSSCOMPILE_TARGET -DCROSS_TO_AMIGA \ -DAMIGA_VERSION_STRING=\""VER: NetHack 3.7.0"\" override TARGET_CXXFLAGS = $(TARGET_CFLAGS) -LUA_TARGET_CFLAGS = $(TARGET_CFLAGS) -DLUA_32BITS=1 +LUA_TARGET_CFLAGS = $(TARGET_CFLAGS) ifeq "$(REGEXOBJ)" "$(TARGETPFX)cppregex.o" override TARGET_LINK = $(TARGET_CXX) else From 4991339655cbe9baae1d7144331b850c21a6395f Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 24 Mar 2026 07:49:38 -0400 Subject: [PATCH 352/442] don't abort clean if target cross-compile directory isn't there --- sys/unix/Makefile.src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/unix/Makefile.src b/sys/unix/Makefile.src index 81a6b72a6..a6fcb6e94 100644 --- a/sys/unix/Makefile.src +++ b/sys/unix/Makefile.src @@ -812,7 +812,7 @@ tags: $(CSOURCES) clean: -rm -f *.o $(HACK_H) $(CONFIG_H) -rm -f monstr.c vis_tab.c ../include/vis_tab.h #obsolete generated files - true; $(CLEANMORE) + -true; $(CLEANMORE) true; $(CLEAN_CC_RESPONSEFILE) $(CLEAN_CXX_RESPONSEFILE) spotless: clean From d929adc85cf18543d823ff390977e9ab0c0761aa Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 24 Mar 2026 10:05:48 -0400 Subject: [PATCH 353/442] warning fix in sp_lev.c In function 'create_monster', inlined from 'lspo_monster' at sp_lev.c:3385:5: sp_lev.c:2169:32: warning: 'tmpmons.m_lev_adj' may be used uninitialized [-Wmaybe-uninitialized] 2169 | if (mtmp->m_lev + m->m_lev_adj > 49) | ~^~~~~~~~~~~ sp_lev.c: In function 'lspo_monster': sp_lev.c:3217:13: note: 'tmpmons.m_lev_adj' was declared here 3217 | monster tmpmons; | ^~~~~~~ --- src/sp_lev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sp_lev.c b/src/sp_lev.c index d6324827e..007d65404 100644 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -3241,6 +3241,7 @@ lspo_monster(lua_State *L) tmpmons.has_invent = DEFAULT_INVENT; tmpmons.waiting = 0; tmpmons.mm_flags = NO_MM_FLAGS; + tmpmons.m_lev_adj = 0; if (argc == 1 && lua_type(L, 1) == LUA_TSTRING) { const char *paramstr = luaL_checkstring(L, 1); From d78af5cec7289510f4dd1a604721b896eceffbbd Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Tue, 24 Mar 2026 17:23:43 +0200 Subject: [PATCH 354/442] Add monster spell header file mcastu.h Move the monster spell definitions there, and use hackery (similar to objects.h) to generate enum and data from the header file. I have not tested Windows, VMS, or Amiga builds. --- include/mcastu.h | 35 +++++++++++ src/mcastu.c | 75 ++++++++---------------- sys/msdos/Makefile.GCC | 3 +- sys/unix/Makefile.src | 4 +- sys/windows/GNUmakefile | 1 + sys/windows/Makefile.nmake | 7 ++- sys/windows/vs/NetHack/NetHack.vcxproj | 1 + sys/windows/vs/NetHackW/NetHackW.vcxproj | 1 + 8 files changed, 72 insertions(+), 55 deletions(-) create mode 100644 include/mcastu.h diff --git a/include/mcastu.h b/include/mcastu.h new file mode 100644 index 000000000..fd59871c9 --- /dev/null +++ b/include/mcastu.h @@ -0,0 +1,35 @@ + + +#define MCF_NONE 0x0000 +#define MCF_INDIRECT 0x0001 /* untargeted/indirect spell */ +#define MCF_SIGHT 0x0002 /* monster needs to see hero */ +#define MCF_HOSTILE 0x0004 /* cast by hostile monsters only */ + +#if defined(MCASTU_ENUM) +#define MONSPELL(def, flags) MCAST_##def +#elif defined(MCASTU_INIT) +#define MONSPELL(def, flags) flags +#endif + +MONSPELL(PSI_BOLT, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(OPEN_WOUNDS, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(LIGHTNING, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(FIRE_PILLAR, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(GEYSER, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(DEATH_TOUCH, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(CURE_SELF, MCF_INDIRECT), +MONSPELL(HASTE_SELF, MCF_INDIRECT), +MONSPELL(DISAPPEAR, MCF_INDIRECT), +MONSPELL(AGGRAVATION, MCF_INDIRECT|MCF_HOSTILE|MCF_SIGHT), +MONSPELL(STUN_YOU, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(WEAKEN_YOU, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(CONFUSE_YOU, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(PARALYZE, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(BLIND_YOU, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(DESTRY_ARMR, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(CURSE_ITEMS, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(INSECTS, MCF_HOSTILE|MCF_INDIRECT|MCF_SIGHT), +MONSPELL(SUMMON_MONS, MCF_HOSTILE|MCF_INDIRECT|MCF_SIGHT), +MONSPELL(CLONE_WIZ, MCF_HOSTILE|MCF_INDIRECT|MCF_SIGHT), + +#undef MONSPELL diff --git a/src/mcastu.c b/src/mcastu.c index 3e32f2e4f..0d8af9d68 100644 --- a/src/mcastu.c +++ b/src/mcastu.c @@ -5,32 +5,17 @@ #include "hack.h" +#define MCASTU_ENUM enum mcast_spells { - MCAST_PSI_BOLT = 0, - MCAST_OPEN_WOUNDS, - MCAST_LIGHTNING, - MCAST_FIRE_PILLAR, - MCAST_GEYSER, - MCAST_DEATH_TOUCH, - - MCAST_CURE_SELF, - MCAST_HASTE_SELF, - MCAST_DISAPPEAR, - - MCAST_AGGRAVATION, - MCAST_STUN_YOU, - MCAST_WEAKEN_YOU, - MCAST_CONFUSE_YOU, - MCAST_PARALYZE, - MCAST_BLIND_YOU, - - MCAST_DESTRY_ARMR, - MCAST_CURSE_ITEMS, - - MCAST_INSECTS, - MCAST_SUMMON_MONS, - MCAST_CLONE_WIZ + #include "mcastu.h" }; +#undef MCASTU_ENUM + +#define MCASTU_INIT +static int mcast_flags[] = { + #include "mcastu.h" +}; +#undef MCASTU_INIT staticfn void cursetxt(struct monst *, boolean); staticfn int choose_magic_spell(struct monst *); @@ -965,18 +950,8 @@ mcast_spell(struct monst *mtmp, int dmg, int spellnum) staticfn boolean is_undirected_spell(int spellnum) { - switch (spellnum) { - case MCAST_CLONE_WIZ: - case MCAST_SUMMON_MONS: - case MCAST_AGGRAVATION: - case MCAST_DISAPPEAR: - case MCAST_HASTE_SELF: - case MCAST_CURE_SELF: - case MCAST_INSECTS: + if ((mcast_flags[spellnum] & MCF_INDIRECT) != 0) return TRUE; - default: - break; - } return FALSE; } @@ -990,25 +965,28 @@ spell_would_be_useless(struct monst *mtmp, int spellnum) * This check isn't quite right because it always uses your real position. * We really want something like "if the monster could see mux, muy". */ - boolean mcouldseeu = couldsee(mtmp->mx, mtmp->my); + + /* spell is only cast by hostile monsters */ + if ((mcast_flags[spellnum] & MCF_HOSTILE) != 0) { + if (mtmp->mpeaceful) + return TRUE; + } + + /* spell needs the monster to see hero */ + if ((mcast_flags[spellnum] & MCF_SIGHT) != 0) { + boolean mcouldseeu = couldsee(mtmp->mx, mtmp->my); + + if (!mcouldseeu) + return TRUE; + } switch (spellnum) { case MCAST_CLONE_WIZ: /* only the Wizard is allowed to clone himself */ if (!mtmp->iswiz || svc.context.no_of_wizards > 1) return TRUE; - if (!mcouldseeu) - return TRUE; - break; - case MCAST_SUMMON_MONS: - /* don't summon monsters if it doesn't think you're around */ - if (!mcouldseeu || mtmp->mpeaceful) - return TRUE; break; case MCAST_AGGRAVATION: - /* aggravate monsters, etc. won't be cast by peaceful monsters */ - if (!mcouldseeu || mtmp->mpeaceful) - return TRUE; /* aggravation (global wakeup) when everyone is already active */ /* if nothing needs to be awakened then this spell is useless but caster might not realize that [chance to pick it then @@ -1039,11 +1017,6 @@ spell_would_be_useless(struct monst *mtmp, int spellnum) if (mtmp->mhp == mtmp->mhpmax) return TRUE; break; - case MCAST_INSECTS: - /* summon insects/sticks to snakes won't be cast by peaceful monsters */ - if (!mcouldseeu || mtmp->mpeaceful) - return TRUE; - break; case MCAST_BLIND_YOU: if (Blinded) return TRUE; diff --git a/sys/msdos/Makefile.GCC b/sys/msdos/Makefile.GCC index bdf4391e3..17d3a8bd8 100644 --- a/sys/msdos/Makefile.GCC +++ b/sys/msdos/Makefile.GCC @@ -416,6 +416,7 @@ GLOBAL_H = $(PCCONF_H) $(INCL)/coord.h $(INCL)/global.h HACK_H = $(CONFIG_H) $(INCL)/context.h $(DUNGEON_H) \ $(DECL_H) $(DISPLAY_H) $(INCL)/sym.h \ $(INCL)/defsym.h $(INCL)/mkroom.h $(INCL)/objclass.h \ + $(INCL)/mcastu.h \ $(INCL)/trap.h $(INCL)/flag.h $(RM_H) \ $(INCL)/vision.h $(INCL)/wintype.h $(INCL)/engrave.h \ $(INCL)/rect.h $(INCL)/hack.h $(REGION_H) \ @@ -1407,7 +1408,7 @@ $(TARGETPFX)light.o: light.c $(HACK_H) $(TARGETPFX)lock.o: lock.c $(HACK_H) $(TARGETPFX)mail.o: mail.c $(HACK_H) ../include/mail.h $(TARGETPFX)makemon.o: makemon.c $(HACK_H) -$(TARGETPFX)mcastu.o: mcastu.c $(HACK_H) +$(TARGETPFX)mcastu.o: mcastu.c $(HACK_H) ../include/mcastu.h $(TARGETPFX)mdlib.o: mdlib.c $(CONFIG_H) ../include/permonst.h \ ../include/align.h ../include/monattk.h ../include/monflag.h \ ../include/monsters.h ../include/objclass.h \ diff --git a/sys/unix/Makefile.src b/sys/unix/Makefile.src index a6fcb6e94..861afc2d3 100644 --- a/sys/unix/Makefile.src +++ b/sys/unix/Makefile.src @@ -571,6 +571,7 @@ HACKINCL = align.h artifact.h artilist.h attrib.h botl.h \ color.h config.h config1.h context.h coord.h cstd.h decl.h \ defsym.h display.h dlb.h dungeon.h engrave.h extern.h flag.h \ fnamesiz.h func_tab.h global.h warnings.h hack.h lint.h mextra.h \ + mcastu.h \ micro.h mfndpos.h mkroom.h monattk.h mondata.h monflag.h monst.h \ monsters.h nhmd4.h obj.h objects.h objclass.h optlist.h patchlevel.h \ pcconf.h permonst.h prop.h rect.h region.h savefile.h selvar.h sym.h \ @@ -896,6 +897,7 @@ $(HACK_H): $(CONFIG_H) ../include/align.h ../include/artilist.h \ ../include/decl.h ../include/defsym.h ../include/display.h \ ../include/dungeon.h ../include/engrave.h ../include/flag.h \ ../include/hack.h ../include/lint.h ../include/mextra.h \ + ../include/mcastu.h \ ../include/mkroom.h ../include/monattk.h ../include/mondata.h \ ../include/monflag.h ../include/monst.h ../include/monsters.h \ ../include/nhlua.h ../include/obj.h ../include/objclass.h \ @@ -1185,7 +1187,7 @@ $(TARGETPFX)light.o: light.c $(HACK_H) $(TARGETPFX)lock.o: lock.c $(HACK_H) $(TARGETPFX)mail.o: mail.c $(HACK_H) ../include/mail.h $(TARGETPFX)makemon.o: makemon.c $(HACK_H) -$(TARGETPFX)mcastu.o: mcastu.c $(HACK_H) +$(TARGETPFX)mcastu.o: mcastu.c $(HACK_H) ../include/mcastu.h $(TARGETPFX)mdlib.o: mdlib.c $(CONFIG_H) ../include/align.h \ ../include/artilist.h ../include/attrib.h \ ../include/context.h ../include/defsym.h ../include/dlb.h \ diff --git a/sys/windows/GNUmakefile b/sys/windows/GNUmakefile index 6f282c632..d4cbafead 100644 --- a/sys/windows/GNUmakefile +++ b/sys/windows/GNUmakefile @@ -794,6 +794,7 @@ HACK_H = $(CONFIG_H) ../include/align.h ../include/artilist.h \ ../include/decl.h ../include/defsym.h ../include/display.h \ ../include/dungeon.h ../include/engrave.h ../include/flag.h \ ../include/hack.h ../include/lint.h ../include/mextra.h \ + ../include/mcastu.h \ ../include/mkroom.h ../include/monattk.h ../include/mondata.h \ ../include/monflag.h ../include/monst.h ../include/monsters.h \ ../include/nhlua.h ../include/obj.h ../include/objclass.h \ diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index 044fb9e70..5a549d3ef 100644 --- a/sys/windows/Makefile.nmake +++ b/sys/windows/Makefile.nmake @@ -694,6 +694,7 @@ HACKINCL = \ $(INCL)flag.h $(INCL)fnamesiz.h $(INCL)func_tab.h \ $(INCL)global.h $(INCL)warnings.h $(INCL)hack.h \ $(INCL)lint.h $(INCL)mextra.h $(INCL)micro.h \ + $(INCL)mcastu.h \ $(INCL)mfndpos.h $(INCL)mkroom.h $(INCL)monattk.h \ $(INCL)mondata.h $(INCL)monflag.h $(INCL)monst.h \ $(INCL)monsters.h $(INCL)nhmd4.h $(INCL)obj.h \ @@ -1131,6 +1132,7 @@ HACK_H = $(CONFIG_H) $(INCL)align.h $(INCL)artilist.h \ $(INCL)decl.h $(INCL)defsym.h $(INCL)display.h \ $(INCL)dungeon.h $(INCL)engrave.h $(INCL)flag.h \ $(INCL)hack.h $(INCL)lint.h $(INCL)mextra.h \ + $(INCL)mcastu.h \ $(INCL)mkroom.h $(INCL)monattk.h $(INCL)mondata.h \ $(INCL)monflag.h $(INCL)monst.h $(INCL)monsters.h \ $(INCL)nhlua.h $(INCL)obj.h $(INCL)objclass.h \ @@ -2664,7 +2666,7 @@ CTAGDEP = $(INCL)align.h $(INCL)artifact.h $(INCL)artilist.h \ $(INCL)attrib.h $(INCL)context.h $(INCL)coord.h \ $(INCL)decl.h $(INCL)dungeon.h $(INCL)engrave.h \ $(INCL)flag.h $(INCL)func_tab.h $(INCL)global.h \ - $(INCL)hack.h $(INCL)mextra.h \ + $(INCL)hack.h $(INCL)mextra.h $(INCL)mcastu.h \ $(INCL)mkroom.h $(INCL)monst.h \ $(INCL)obj.h $(INCL)objclass.h $(INCL)prop.h \ $(INCL)quest.h $(INCL)rect.h $(INCL)region.h \ @@ -2694,6 +2696,7 @@ $(UTIL)sf.tags: $(CTAGSCMD) $(CTAGDEP) $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)global.h $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)hack.h $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)mextra.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)mcastu.h $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)mkroom.h $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)monst.h $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)defsym.h @@ -3269,7 +3272,7 @@ $(OTTY)light.o: light.c $(HACK_H) $(OTTY)lock.o: lock.c $(HACK_H) $(OTTY)mail.o: mail.c $(HACK_H) $(INCL)mail.h $(OTTY)makemon.o: makemon.c $(HACK_H) -$(OTTY)mcastu.o: mcastu.c $(HACK_H) +$(OTTY)mcastu.o: mcastu.c $(HACK_H) $(INCL)mcastu.h $(OTTY)mdlib.o: mdlib.c $(CONFIG_H) $(INCL)align.h \ $(INCL)artilist.h $(INCL)attrib.h \ $(INCL)context.h $(INCL)defsym.h $(INCL)dlb.h \ diff --git a/sys/windows/vs/NetHack/NetHack.vcxproj b/sys/windows/vs/NetHack/NetHack.vcxproj index 5df071e6c..72d8986b0 100644 --- a/sys/windows/vs/NetHack/NetHack.vcxproj +++ b/sys/windows/vs/NetHack/NetHack.vcxproj @@ -259,6 +259,7 @@ + diff --git a/sys/windows/vs/NetHackW/NetHackW.vcxproj b/sys/windows/vs/NetHackW/NetHackW.vcxproj index df43cdd0c..ba3b21b1e 100644 --- a/sys/windows/vs/NetHackW/NetHackW.vcxproj +++ b/sys/windows/vs/NetHackW/NetHackW.vcxproj @@ -327,6 +327,7 @@ + From aac60db8d6273be75a2d62b2c34c3bd85989bb2e Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 24 Mar 2026 11:46:07 -0400 Subject: [PATCH 355/442] update Cross-compiling for Amiga steps --- Cross-compiling | 106 ++++++++++++++++++++++++------------------------ 1 file changed, 52 insertions(+), 54 deletions(-) diff --git a/Cross-compiling b/Cross-compiling index d609ab585..43155a024 100644 --- a/Cross-compiling +++ b/Cross-compiling @@ -528,11 +528,11 @@ Cross-compiler pre-built binary downloads: Disclaimer: This is a minimal recipe, just to help someone else get started if they have a desire to get a full cross-compile of NetHack going for the Amiga. - See CAVEATS below. Cross-compiler used: bebbo's amiga-gcc -Cross-compiler url: https://github.com/bebbo/amiga-gcc - +Cross-compiler url: https://github.com/AmigaPorts/m68k-amigaos-gcc +Author of 3.7 support: Ingo Paschke + To our knowledge, a pre-built copy of the cross-compiler isn't available, so you will likely have to obtain the cross-compiler sources via git and build it on your system. @@ -542,86 +542,84 @@ Cross-compiler url: https://github.com/bebbo/amiga-gcc sudo apt install make wget git gcc g++ lhasa libgmp-dev \ libmpfr-dev libmpc-dev flex bison gettext texinfo ncurses-dev \ - autoconf rsync + autoconf rsync libreadline-dev The build prerequisite packages for macOS via homebrew are documented but not tested by us any of us to date. brew install bash wget make lhasa gmp mpfr libmpc flex gettext \ - texinfo gcc make autoconf + gnu-sed texinfo gcc@12 make autoconf bison After installing the prerequisite packages and the cross-compiler - it was a straightforward build: + the build is straightforward: - git clone https://github.com/bebbo/amiga-gcc.git + Create the /opt/amiga directory for holding the compiler and tools. + sudo mkdir /opt/amiga + sudo chgrp users /opt/amiga + sudo chmod 775 /opt/amiga + sudo usermod -a -G users username + (you may have to log off and back on) + + Build the cross-compiler. + git clone https://github.com/AmigaPorts/m68k-amigaos-gcc.git amiga-gcc cd amiga-gcc make update - [Note that you may have to take ownership of the files in the bebbo - repo via chown before successfully carrying out the next steps] + The compiler pieces are installed in /opt/amiga by default, and the + NetHack Makefiles expect them there. - make clean - make clean-prefix - date; make all -j3 >&b.log; date + If you prefer, you can alter the prefix before you build if you want. + The instructions for doing so were spelled out at the time of this writing at: - The compiler pieces are installed in /opt/amiga by default. If you prefer, - you can alter the prefix before you build if you want. The instructions - for doing so were spelled out at the time of this writing at: + https://github.com/AmigaPorts/m68k-amigaos-gcc + Makefile adjustments will be needed, however. - https://github.com/bebbo/amiga-gcc + On your linux host: - On your linux host: - - cd sys/unix ; sh setup.sh hints/linux.370 ; cd ../.. + # Clone NetHack 3.7 source + cd NetHack + sys/unix/setup.sh sys/unix/hints/linux.370 make fetch-lua - - On your macOS host: - - cd sys/unix ; sh setup.sh hints/macOS.370 ; cd ../.. - make fetch-lua - - The Amiga cross-compile can then be carried out by specifying - CROSS_TO_AMIGA=1 on the make command line: - + make CROSS_TO_AMIGA=1 fetch-regex make CROSS_TO_AMIGA=1 all make CROSS_TO_AMIGA=1 package - You can explicitly include tty and curses support if desired, otherwise - you'll end up with a tty-only cross-compile build. The SDL1 pdcurses - support has not been tested. + The distribution ZIP is created at targets/amiga/NH370AMI.ZIP. - make WANT_WIN_TTY=1 WANT_WIN_CURSES=1 CROSS_TO_AMIGA=1 all + On your macOS host, the Amiga cross-compile can be carried out by specifying + CROSS_TO_AMIGA=1 on the make command line: + + # Clone NetHack 3.7 source + cd NetHack + sys/unix/setup.sh sys/unix/hints/macOS.370 + make fetch-lua + make CROSS_TO_AMIGA=1 fetch-regex + make CROSS_TO_AMIGA=1 all + make CROSS_TO_AMIGA=1 package Result: The "make package" target will bundle the (hopefully) necessary - components to run NetHack on msdos into a folder: + components to run NetHack on an Amiga into a folder: targets/amiga/pkg and then it zips the contents of that folder into: - targets/amiga/nh370ami.zip + targets/amiga/NH370AMI.ZIP - Also note that building the amiga targets using the make command - above, does not preclude you from building local linux or macOS - targets as well. Just drop the CROSS_TO_AMIGA=1 from the make - command line. + Display Modes + ------------- + Two display modes are available, selected in nethack.cnf: - The cross-compiler hints additions are enclosed inside ifdef sections - and won't interfere with the non-cross-compile build in that case. + Text mode (AMII): + OPTIONS=symset:AmigaFont - CAVEATS: The original NetHack Amiga build steps included the source for - some utilities that were built and executed on the amiga: - txt2iff and xpm2iff - as part of the NetHack build procedure on amiga. - Those did not compile out-of-the-box on the linux host. They - will either have to be: - - ported to build and run on the linux or macOS cross-compile - host - or + Tile mode (AMIV): + OPTIONS=windowtype:amiv - - their functionality will have to be rolled into amiga NetHack - itself and executed on the target Amiga the first time the game - is run, perhaps. + Tile mode auto-selects tiles32.iff (32 colors, 5 bitplanes) when the + screen supports 32+ colors, otherwise tiles16.iff (16 colors, 4 planes). + + For further details about playing the game, and setting up your NetHack + configuration, please see the following file: + sys/amiga/README.amiga - If you make headway, or are successful getting a working copy of - NetHack going on the amiga, drop us a note at devteam@nethack.org. +--------------------------------+ | B6. Case sample: Web Assembly | From 0f461df338cb7bc311ab5ef16ea1df6329d7e19b Mon Sep 17 00:00:00 2001 From: nhw_cron Date: Tue, 24 Mar 2026 12:16:07 -0400 Subject: [PATCH 356/442] This is cron-daily v1-Jan-12-2026. 000files updated: Files --- Files | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Files b/Files index 3bbf8912e..a02179034 100644 --- a/Files +++ b/Files @@ -93,17 +93,17 @@ coord.h cstd.h decl.h defsym.h dgn_file.h display.h dlb.h dungeon.h engrave.h extern.h flag.h fnamesiz.h func_tab.h global.h hack.h hacklib.h integer.h isaac64.h lint.h mail.h -mextra.h mfndpos.h micro.h mkroom.h monattk.h -mondata.h monflag.h monst.h monsters.h nhmd4.h -nhregex.h obj.h objclass.h objects.h optlist.h -patchlevel.h pcconf.h permonst.h prop.h quest.h -rect.h region.h rm.h savefile.h seffects.h -selvar.h sfmacros.h sfprocs.h skills.h sndprocs.h -sp_lev.h spell.h stairs.h sym.h sys.h -tcap.h tileset.h timeout.h tradstdc.h trap.h -unixconf.h vision.h vmsconf.h warnings.h weight.h -winami.h wincurs.h windconf.h winprocs.h wintype.h -you.h youprop.h +mcastu.h mextra.h mfndpos.h micro.h mkroom.h +monattk.h mondata.h monflag.h monst.h monsters.h +nhmd4.h nhregex.h obj.h objclass.h objects.h +optlist.h patchlevel.h pcconf.h permonst.h prop.h +quest.h rect.h region.h rm.h savefile.h +seffects.h selvar.h sfmacros.h sfprocs.h skills.h +sndprocs.h sp_lev.h spell.h stairs.h sym.h +sys.h tcap.h tileset.h timeout.h tradstdc.h +trap.h unixconf.h vision.h vmsconf.h warnings.h +weight.h winami.h wincurs.h windconf.h winprocs.h +wintype.h you.h youprop.h (file for tty versions) wintty.h From 41b2087436d396f3e57b8c0de5e7d9f6b19f1764 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Wed, 25 Mar 2026 09:35:39 +0200 Subject: [PATCH 357/442] Hero has a small chance of catching items thrown at them --- doc/fixes3-7-0.txt | 1 + src/mthrowu.c | 33 +++++++++++++++++++++++++++++---- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 1428c1500..0d85f432f 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1589,6 +1589,7 @@ amulet of magical breathing increases power regeneration change some command keys, 'v' is now chronicle, 'V' is versionshort, m-prefix 'V' is longer version, remove key binding of #history one orc-town shaman has a higher level, affecting spellcasting +hero has a small chance of catching items thrown at them Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/mthrowu.c b/src/mthrowu.c index 88066266d..24001738a 100644 --- a/src/mthrowu.c +++ b/src/mthrowu.c @@ -8,6 +8,7 @@ staticfn int monmulti(struct monst *, struct obj *, struct obj *); staticfn void monshoot(struct monst *, struct obj *, struct obj *); staticfn boolean ucatchgem(struct obj *, struct monst *); +staticfn boolean u_catch_thrown_obj(struct obj *); staticfn const char *breathwep_name(int); staticfn boolean drop_throw(struct obj *, boolean, coordxy, coordxy); staticfn boolean blocking_terrain(coordxy, coordxy); @@ -527,6 +528,27 @@ ucatchgem( return FALSE; } +/* hero may catch thrown obj. it is added to inventory, if possible */ +staticfn boolean +u_catch_thrown_obj(struct obj *otmp) +{ + int catch_chance = 100 - ACURR(A_DEX) + - ((Role_if(PM_MONK) || Role_if(PM_ROGUE)) ? 20 : 0); + + if (!Blind && !Confusion && !Stunned && !Fumbling + && otmp->oclass != VENOM_CLASS + && !nohands(gy.youmonst.data) && freehand() + && calc_capacity(otmp->owt) <= SLT_ENCUMBER && !rn2(catch_chance)) { + char buf[BUFSZ]; + + Snprintf(buf, BUFSZ, "You catch the %s!", simpleonames(otmp)); + (void) hold_another_object(otmp, "You catch, but drop, the %s.", + simpleonames(otmp), buf); + return TRUE; + } + return FALSE; +} + #define MT_FLIGHTCHECK(pre,forcehit) \ (/* missile hits edge of screen */ \ !isok(gb.bhitpos.x + dx, gb.bhitpos.y + dy) \ @@ -666,13 +688,16 @@ m_throw( if (gm.multi) nomul(0); + /* hero might be poly'd into a unicorn */ + if (singleobj->oclass == GEM_CLASS && ucatchgem(singleobj, mon)) + break; + + if (!tethered_weapon && u_catch_thrown_obj(singleobj)) + break; + if (singleobj->oclass == POTION_CLASS) { potionhit(&gy.youmonst, singleobj, POTHIT_MONST_THROW); break; - } else if (singleobj->oclass == GEM_CLASS) { - /* hero might be poly'd into a unicorn */ - if (ucatchgem(singleobj, mon)) - break; } oldumort = u.umortality; From f8bd79354c4546c9a6b6e36227e1a5a2de5b9747 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Wed, 25 Mar 2026 13:46:03 +0200 Subject: [PATCH 358/442] Add a history menu for wizwish Use the 'm' Prefix to make wizwish show the history menu. Also entries wished via WIZKIT are added to the history. While debugging, I often need to wish the same thing multiple times, and typing or pasting it with mouse is annoying... --- doc/Guidebook.mn | 2 ++ doc/Guidebook.tex | 2 ++ doc/fixes3-7-0.txt | 1 + include/extern.h | 1 + src/cmd.c | 2 +- src/files.c | 4 ++- src/zap.c | 84 +++++++++++++++++++++++++++++++++++++++++++++- 7 files changed, 93 insertions(+), 3 deletions(-) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 314567f71..fd295e786 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -1935,6 +1935,7 @@ Wish for something. Autocompletes. Debug mode only. Default key is \(oq\(haW\(cq. +Precede this command with the \(oq\f(CRm\fP\(cq prefix to show a wish history menu. .lp "#wmode " Show wall modes. Autocompletes. @@ -3747,6 +3748,7 @@ Debug mode only: extra items to add to initial inventory. Value is the name of a text file containing a list of item names, one per line, up to a maximum of 128 lines. Each line is processed by the function that handles wishing. +Entries are added to the wish history; see the wizwish-command. .lp "" Example: .sd diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 7fa1724af..7bfa18221 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -2028,6 +2028,7 @@ Wish for something. Autocompletes. Debug mode only. Default key is `\texttt{\textasciicircum W}'. +Precede this command with the `\texttt{m}' prefix to show a wish history menu. %.lp \item[\#wmode] Show wall modes. @@ -4074,6 +4075,7 @@ Debug mode only: extra items to add to initial inventory. Value is the name of a text file containing a list of item names, one per line, up to a maximum of 128 lines. Each line is processed by the function that handles wishing. +Entries are added to the wish history; see the wizwish-command. %.lp "" Example: diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 0d85f432f..506dcdd5a 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1590,6 +1590,7 @@ change some command keys, 'v' is now chronicle, 'V' is versionshort, m-prefix 'V' is longer version, remove key binding of #history one orc-town shaman has a higher level, affecting spellcasting hero has a small chance of catching items thrown at them +wizard mode: history menu for #wizwish and WIZKIT Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/include/extern.h b/include/extern.h index 39e70fb0b..ac0992b05 100644 --- a/include/extern.h +++ b/include/extern.h @@ -3988,6 +3988,7 @@ extern boolean inventory_resistance_check(int); extern char *item_what(int); extern int destroy_items(struct monst *, int, int) NONNULLARG1; extern int resist(struct monst *, char, int, int) NONNULLARG1; +extern void wish_history_add(char *); extern void makewish(void); extern const char *flash_str(int, boolean) NONNULL; diff --git a/src/cmd.c b/src/cmd.c index a88b2d7a7..8b34060b6 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -1996,7 +1996,7 @@ struct ext_func_tab extcmdlist[] = { { '\0', "wizwhere", "show locations of special levels", wiz_where, IFBURIED | AUTOCOMPLETE | WIZMODECMD, NULL }, { C('w'), "wizwish", "wish for something", - wiz_wish, IFBURIED | WIZMODECMD, NULL }, + wiz_wish, IFBURIED | CMD_M_PREFIX | WIZMODECMD, NULL }, { '\0', "wmode", "show wall modes", wiz_show_wmodes, IFBURIED | AUTOCOMPLETE | WIZMODECMD, NULL }, { 'z', "zap", "zap a wand", diff --git a/src/files.c b/src/files.c index d2c81a4a9..ae45f6c07 100644 --- a/src/files.c +++ b/src/files.c @@ -2546,8 +2546,10 @@ proc_wizkit_line(char *buf) otmp = readobjnam(buf, (struct obj *) 0); if (otmp) { - if (otmp != &hands_obj) + if (otmp != &hands_obj) { + wish_history_add(buf); wizkit_addinv(otmp); + } } else { /* .60 limits output line width to 79 chars */ config_error_add("Bad wizkit item: \"%.60s\"", buf); diff --git a/src/zap.c b/src/zap.c index 12c482227..a1466e1d5 100644 --- a/src/zap.c +++ b/src/zap.c @@ -40,6 +40,7 @@ staticfn int maybe_destroy_item(struct monst *, struct obj *, int) NONNULLPTRS; staticfn boolean destroyable(struct obj *, int); staticfn void wishcmdassist(int); +staticfn void wish_history_menu(char *); #define ZT_MAGIC_MISSILE (AD_MAGM - 1) #define ZT_FIRE (AD_FIRE - 1) @@ -6210,6 +6211,81 @@ wishcmdassist(int triesleft) destroy_nhwindow(win); } +#define MAX_WISH_HISTORY 20 +static char *wish_history[MAX_WISH_HISTORY] = { NULL }; +static int wish_history_idx = 0; + +/* add string to wish history list */ +void +wish_history_add(char *buf) +{ +#ifdef DEBUG + int i; + + if (!wizard) + return; + + for (i = 0; i < MAX_WISH_HISTORY; i++) { + int idx = (wish_history_idx + i) % MAX_WISH_HISTORY; + + if (!wish_history[idx]) + continue; + if (!strncmpi(wish_history[idx], buf, strlen(wish_history[idx]))) + break; + + } + + if (i == MAX_WISH_HISTORY) { + int idx = (wish_history_idx + i) % MAX_WISH_HISTORY; + + if (wish_history[idx]) + free(wish_history[idx]); + wish_history[idx] = (char *)alloc(strlen(buf) + 1); + strcpy(wish_history[idx], buf); + wish_history_idx = (wish_history_idx + 1) % MAX_WISH_HISTORY; + } +#endif /* DEBUG */ +} + +/* shows menu of previous wishes, copies selected into buf, max BUFSZ len. + buf is not modified, if nothing was selected. */ +staticfn void +wish_history_menu(char *buf) +{ +#ifdef DEBUG + winid win; + anything any; + int i = 0, npick; + menu_item *picks = (menu_item *) 0; + int idx; + + win = create_nhwindow(NHW_MENU); + start_menu(win, MENU_BEHAVE_STANDARD); + any = cg.zeroany; + + for (i = MAX_WISH_HISTORY-1; i >= 0; i--) { + idx = (wish_history_idx + i) % MAX_WISH_HISTORY; + if (wish_history[idx]) { + any.a_int = (i + 1); + add_menu(win, &nul_glyphinfo, &any, '\0', 0, ATR_NONE, NO_COLOR, + wish_history[idx], MENU_ITEMFLAGS_NONE); + } + } + + end_menu(win, "Wish what?"); + npick = select_menu(win, PICK_ONE, &picks); + destroy_nhwindow(win); + if (npick > 0) { + i = picks->item.a_int; + i--; + idx = (wish_history_idx + i) % MAX_WISH_HISTORY; + + if (wish_history[idx]) + strcpy(buf, wish_history[idx]); + } +#endif /* DEBUG */ +} + RESTORE_WARNING_FORMAT_NONLITERAL void @@ -6232,7 +6308,11 @@ makewish(void) if (iflags.cmdassist && tries > 0) Strcat(promptbuf, " (enter 'help' for assistance)"); Strcat(promptbuf, "?"); - getlin(promptbuf, buf); + + if (iflags.menu_requested && wish_history[0] && (tries == 0)) + wish_history_menu(buf); + else + getlin(promptbuf, buf); if (iflags.term_gone) { if (!iflags.debug_fuzzer) @@ -6270,9 +6350,11 @@ makewish(void) livelog_printf(LL_WISH, "declined to make a wish"); return; } else if (otmp == &hands_obj) { + wish_history_add(bufcpy); /* wizard mode terrain wish: skip livelogging, etc */ return; } + wish_history_add(bufcpy); if (otmp->oartifact) { /* update artifact bookkeeping; doesn't produce a livelog event */ From 3c89dc23879218234cb786241fa4f7fb665f27d3 Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 25 Mar 2026 08:06:12 -0400 Subject: [PATCH 359/442] Guidebook date stamp to reflect most recent update --- doc/Guidebook.mn | 2 +- doc/Guidebook.tex | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index fd295e786..2ba4aafd1 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -60,7 +60,7 @@ .ds f0 \*(vr .ds f1 \" empty .\"DO NOT REMOVE NH_DATESUB .ds f2 Date(%B %-d, %Y) -.ds f2 March 21, 2026 +.ds f2 March 25, 2026 . .\" A note on some special characters: .\" \(lq = left double quote diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 7bfa18221..d5b6c72a1 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -25,7 +25,7 @@ \author{Original version - Eric S. Raymond\\ (Edited and expanded for 3.7.0 by Mike Stephenson and others)} %DO NOT REMOVE NH_DATESUB \date{Date(%B %-d, %Y)} -\date{March 21, 2026} +\date{March 25, 2026} \maketitle From b58d2d4096aefc0cc6efff934a72e83ef560a405 Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 25 Mar 2026 08:48:14 -0400 Subject: [PATCH 360/442] try to reduce some Amiga cross-compile warnings --- include/amiconf.h | 4 ++++ include/global.h | 6 +++++- sys/amiga/amidos.c | 6 +++++- sys/amiga/amidos.p | 4 ++-- sys/amiga/amiwind.c | 4 ++++ 5 files changed, 20 insertions(+), 4 deletions(-) diff --git a/include/amiconf.h b/include/amiconf.h index b607fd228..c729e6c30 100644 --- a/include/amiconf.h +++ b/include/amiconf.h @@ -17,6 +17,10 @@ #ifdef CROSS_TO_AMIGA #include #include +#include +#include +#include +#include #endif #ifdef __SASC_60 /* since SAS can prevent re-inclusion */ diff --git a/include/global.h b/include/global.h index 6d785ac9a..21b00fe46 100644 --- a/include/global.h +++ b/include/global.h @@ -110,10 +110,14 @@ enum optchoice { opt_in, opt_out}; typedef uchar nhsym; #ifndef STRNCMPI -#ifndef __SASC_60 /* SAS/C already shifts to stricmp */ +/* SAS/C already shifts to stricmp */ +#if !defined(__SASC_60) && !defined(CROSS_TO_AMIGA) #define strcmpi(a, b) strncmpi((a), (b), -1) #endif #endif +#ifdef CROSS_TO_AMIGA +#define strcmpi(a, b) stricmp(a, b) +#endif /* #define SPECIALIZATION */ /* do "specialized" version of new topology */ diff --git a/sys/amiga/amidos.c b/sys/amiga/amidos.c index 3d465ddd2..01d33343f 100644 --- a/sys/amiga/amidos.c +++ b/sys/amiga/amidos.c @@ -450,7 +450,11 @@ fopenp(const char *name, const char *mode) static BPTR OrgDirLock = NO_LOCK; int -chdir(char *dir) +chdir( +#ifdef CROSS_TO_AMIGA + const +#endif + char *dir) { extern char orgdir[]; diff --git a/sys/amiga/amidos.p b/sys/amiga/amidos.p index fc479f9d1..ed669f563 100644 --- a/sys/amiga/amidos.p +++ b/sys/amiga/amidos.p @@ -31,10 +31,10 @@ void getreturn(const char *); #ifndef msmsg void msmsg( const char *, ... ); #endif -#if !defined(__SASC_60) && !defined(_DCC) +#if !defined(__SASC_60) && !defined(_DCC) && !defined(CROSS_TO_AMIGA) int chdir(char *); #endif -#ifndef strcmpi +#ifndef strcmpi int strcmpi(char * , char *); #endif #if !defined(memcmp) && !defined(AZTEC_C) && !defined(_DCC) && !defined(__GNUC__) diff --git a/sys/amiga/amiwind.c b/sys/amiga/amiwind.c index e06630675..d931697b7 100644 --- a/sys/amiga/amiwind.c +++ b/sys/amiga/amiwind.c @@ -891,6 +891,10 @@ amii_loadlib(void) { } +#ifdef CROSS_TO_AMIGA +extern void Abort(long) NORETURN; +#endif + /* fatal error */ /*VARARGS1*/ void error From 7c9b4b9595530576171f1cb5a426565f0f3c504a Mon Sep 17 00:00:00 2001 From: nhw_cron Date: Wed, 25 Mar 2026 08:16:08 -0400 Subject: [PATCH 361/442] This is cron-daily v1-Jan-12-2026. 005guidebook updated: doc/Guidebook.txt --- doc/Guidebook.txt | 2192 ++++++++++++++++++++++----------------------- 1 file changed, 1096 insertions(+), 1096 deletions(-) diff --git a/doc/Guidebook.txt b/doc/Guidebook.txt index 6ac7457db..dad815917 100644 --- a/doc/Guidebook.txt +++ b/doc/Guidebook.txt @@ -15,7 +15,7 @@ Original version - Eric S. Raymond (Edited and expanded for NetHack 3.7.0 by Mike Stephenson and others) - March 21, 2026 + March 25, 2026 @@ -126,7 +126,7 @@ employ to great advantage. - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -192,7 +192,7 @@ NetHack continues this fine tradition. Unlike text adventure games - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -258,7 +258,7 @@ - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -324,7 +324,7 @@ - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -390,7 +390,7 @@ - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -456,7 +456,7 @@ The number of turns elapsed so far, displayed if you have the - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -522,7 +522,7 @@ - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -588,7 +588,7 @@ - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -654,7 +654,7 @@ - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -720,7 +720,7 @@ instead. Only these one-step movement commands cause you to - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -786,7 +786,7 @@ ing . ^ is used as shorthand elsewhere in the - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -852,7 +852,7 @@ - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -918,7 +918,7 @@ - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -984,7 +984,7 @@ - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -1050,7 +1050,7 @@ at an adjacent "remembered, unseen monster" marker. - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -1116,7 +1116,7 @@ - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -1182,7 +1182,7 @@ (R)UNIX is a registered trademark of The Open Group. - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -1248,7 +1248,7 @@ menu_next_page, and menu_last_page keys (`^', `<', `>', `|' by - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -1314,7 +1314,7 @@ doesn't and give that name to the result, while splitting (count - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -1380,7 +1380,7 @@ - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -1446,7 +1446,7 @@ extinct. - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -1512,7 +1512,7 @@ - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -1578,7 +1578,7 @@ - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -1644,7 +1644,7 @@ right away.) Since using this command by accident can cause - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -1710,7 +1710,7 @@ Default key is `M-R'. - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -1776,7 +1776,7 @@ (worn blindfold or towel or lenses, lit lamp(s) and/or candle(s), - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -1842,7 +1842,7 @@ - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -1908,7 +1908,7 @@ - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -1974,7 +1974,7 @@ - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -2040,7 +2040,7 @@ within a modest radius. No time elapses. Autocompletes. Debug - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -2100,13 +2100,13 @@ #wizwish Wish for something. Autocompletes. Debug mode only. Default - key is `^W'. - - #wmode - Show wall modes. Autocompletes. Debug mode only. + key is `^W'. Precede this command with the `m' prefix to show a + wish history menu. - NetHack 3.7.0 March 21, 2026 + + + NetHack 3.7.0 March 25, 2026 @@ -2116,6 +2116,9 @@ + #wmode + Show wall modes. Autocompletes. Debug mode only. + #zap Zap a wand. Default key is `z'. @@ -2124,23 +2127,23 @@ - If your keyboard has a meta key (which, when pressed in combina- - tion with another key, modifies it by setting the "meta" [8th, or - "high"] bit), you can invoke many extended commands by meta-ing the + If your keyboard has a meta key (which, when pressed in combina- + tion with another key, modifies it by setting the "meta" [8th, or + "high"] bit), you can invoke many extended commands by meta-ing the first letter of the command. On Windows and MS-DOS, the "Alt" key can be used in this fashion. - On other systems, if typing "Alt" plus another key transmits a two - character sequence consisting of an Escape followed by the other key, - you may set the altmeta option to have NetHack combine them into - meta+. (This combining action only takes place when NetHack is + On other systems, if typing "Alt" plus another key transmits a two + character sequence consisting of an Escape followed by the other key, + you may set the altmeta option to have NetHack combine them into + meta+. (This combining action only takes place when NetHack is expecting a command to execute, not when accepting input to name some- thing or to make a wish.) Unlike control characters, where ^x and ^X denote the same thing, - meta characters are case-sensitive: M-x and M-X represent different - things. Some commands which can be run via a meta character require - that the letter be capitalized because the lower-case equivalent is + meta characters are case-sensitive: M-x and M-X represent different + things. Some commands which can be run via a meta character require + that the letter be capitalized because the lower-case equivalent is used for another command, so the three key combination meta+Shift+ is needed. @@ -2169,10 +2172,7 @@ - - - - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -2238,7 +2238,7 @@ - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -2252,7 +2252,7 @@ - If the number_pad option is on, some additional letter commands + If the number_pad option is on, some additional letter commands are available: h #help @@ -2304,7 +2304,7 @@ - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -2316,61 +2316,61 @@ 5. Rooms and corridors - Rooms and corridors in the dungeon are either lit or dark. Any - lit areas within your line of sight will be displayed; dark areas are - only displayed if they are within one space of you. Walls and corri- + Rooms and corridors in the dungeon are either lit or dark. Any + lit areas within your line of sight will be displayed; dark areas are + only displayed if they are within one space of you. Walls and corri- dors remain on the map as you explore them. Secret corridors are hidden and appear to be solid rock. You can find them with the `s' (search) command when adjacent to them. Multi- - ple search attempts may be needed. When searching is successful, - secret corridors become ordinary open corridor locations. Mapping - magic reveals secret corridors, so converts them into ordinary corri- + ple search attempts may be needed. When searching is successful, + secret corridors become ordinary open corridor locations. Mapping + magic reveals secret corridors, so converts them into ordinary corri- dors and shows them as such. 5.1. Doorways - Doorways connect rooms and corridors. Some doorways have no - doors; you can walk right through. Others have doors in them, which - may be open, closed, or locked. To open a closed door, use the `o' - (open) command; to close it again, use the `c' (close) command. By - default the autoopen option is enabled, so simply attempting to walk - onto a closed door's location will attempt to open it without needing - `o'. Opening via autoopen will not work if you are confused or + Doorways connect rooms and corridors. Some doorways have no + doors; you can walk right through. Others have doors in them, which + may be open, closed, or locked. To open a closed door, use the `o' + (open) command; to close it again, use the `c' (close) command. By + default the autoopen option is enabled, so simply attempting to walk + onto a closed door's location will attempt to open it without needing + `o'. Opening via autoopen will not work if you are confused or stunned or suffer from the fumbling attribute. - Open doors cannot be entered diagonally; you must approach them - straight on, horizontally or vertically. Doorways without doors are + Open doors cannot be entered diagonally; you must approach them + straight on, horizontally or vertically. Doorways without doors are not restricted in this fashion except on one particular level (described by "#overview" as "a primitive area"). - Unlocking magic exists but usually won't be available early on. - You can get through a locked door without magic by first using an - unlocking tool with the `a' (apply) command, and then opening it. By - default the autounlock option is also enabled, so if you attempt to - open (via `o' or autoopen) a locked door while carrying an unlocking - tool, you'll be asked whether to use it on the door's lock. Alterna- - tively, you can break a closed door (whether locked or not) down by - kicking it via the `^D' (kick) command. Kicking down a door destroys + Unlocking magic exists but usually won't be available early on. + You can get through a locked door without magic by first using an + unlocking tool with the `a' (apply) command, and then opening it. By + default the autounlock option is also enabled, so if you attempt to + open (via `o' or autoopen) a locked door while carrying an unlocking + tool, you'll be asked whether to use it on the door's lock. Alterna- + tively, you can break a closed door (whether locked or not) down by + kicking it via the `^D' (kick) command. Kicking down a door destroys it and makes a lot of noise which might wake sleeping monsters. - Some closed doors are booby-trapped and will explode if an - attempt is made to open (when unlocked) or unlock (when locked) or - kick down. Like kicking, an explosion destroys the door and makes a - lot of noise. The "#untrap" command can be used to search a door for - traps but might take multiple attempts to find one. When one is - found, you'll be asked whether to try to disarm it. If you accede, - success will eliminate the trap but failure will set off the trap's - explosion. (If you decline, you effectively forget that a trap was + Some closed doors are booby-trapped and will explode if an + attempt is made to open (when unlocked) or unlock (when locked) or + kick down. Like kicking, an explosion destroys the door and makes a + lot of noise. The "#untrap" command can be used to search a door for + traps but might take multiple attempts to find one. When one is + found, you'll be asked whether to try to disarm it. If you accede, + success will eliminate the trap but failure will set off the trap's + explosion. (If you decline, you effectively forget that a trap was found there.) - Closed doors can be useful for shutting out monsters. Most mon- - sters cannot open closed doors, although a few don't need to (for - example, ghosts can walk through doors and fog clouds can flow under + Closed doors can be useful for shutting out monsters. Most mon- + sters cannot open closed doors, although a few don't need to (for + example, ghosts can walk through doors and fog clouds can flow under them). Some monsters who can open doors can also use unlocking tools. - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -2382,61 +2382,61 @@ And some (giants) can smash doors. - Secret doors are hidden and appear to be ordinary wall (from - inside a room) or solid rock (from outside). You can find them with - the `s' (search) command but it might take multiple tries (possibly - many tries if your luck is poor). Once found they are in all ways - equivalent to normal doors. Mapping magic does not reveal secret + Secret doors are hidden and appear to be ordinary wall (from + inside a room) or solid rock (from outside). You can find them with + the `s' (search) command but it might take multiple tries (possibly + many tries if your luck is poor). Once found they are in all ways + equivalent to normal doors. Mapping magic does not reveal secret doors. 5.2. Traps (`^') - There are traps throughout the dungeon to snare the unwary - intruder. For example, you may suddenly fall into a pit and be stuck + There are traps throughout the dungeon to snare the unwary + intruder. For example, you may suddenly fall into a pit and be stuck for a few turns trying to climb out (see below). A trap usually won't - appear on your map until you trigger it by moving onto it, you see + appear on your map until you trigger it by moving onto it, you see someone else trigger it, or you discover it with the `s' (search) com- - mand (multiple attempts are often needed; if your luck is poor, many - attempts might be needed). Wands of secret door detection and spell - of detect unseen also reveal traps within a modest radius but only if + mand (multiple attempts are often needed; if your luck is poor, many + attempts might be needed). Wands of secret door detection and spell + of detect unseen also reveal traps within a modest radius but only if the trap is also within line-of-sight (whether you can see at the time or not). There is also other magic which can reveal traps. - Monsters can fall prey to traps, too, which can potentially be - used as a defensive strategy. Unfortunately traps can be harmful to - your pet(s) as well. Monsters, including pets, usually will avoid + Monsters can fall prey to traps, too, which can potentially be + used as a defensive strategy. Unfortunately traps can be harmful to + your pet(s) as well. Monsters, including pets, usually will avoid moving onto a trap which is shown on your map if they have encountered that type of trap before. - Some traps such as pits, bear traps, and webs hold you in one - place. You can escape by simply trying to move to an adjacent spot + Some traps such as pits, bear traps, and webs hold you in one + place. You can escape by simply trying to move to an adjacent spot and repeat as needed; eventually you will get free. - Other traps can send you to different locations. Teleporters - send you elsewhere on the same dungeon level. Level teleporters send - you to a random dungeon level, the destination chosen from a few lev- - els lower all the way to the top. These traps choose a new destina- - tion each time they're activated. Trap doors and holes also send you - to another level, but one which is always below the current level. - Usually that will be the next level down but it can be farther. + Other traps can send you to different locations. Teleporters + send you elsewhere on the same dungeon level. Level teleporters send + you to a random dungeon level, the destination chosen from a few lev- + els lower all the way to the top. These traps choose a new destina- + tion each time they're activated. Trap doors and holes also send you + to another level, but one which is always below the current level. + Usually that will be the next level down but it can be farther. Unlike (level) teleporters, the destination level of a particular trap - door or hole is persistent, so falling into one will bring you to the - same level each time--though not necessarily the same spot on the + door or hole is persistent, so falling into one will bring you to the + same level each time--though not necessarily the same spot on the level. Magic portals behave similarly, but with some additional vari- - ation. Some portals are two-way and their remote destination is - always the same: another portal which can take you back. Others are + ation. Some portals are two-way and their remote destination is + always the same: another portal which can take you back. Others are one-way and send you to a specific destination level but not necessar- ily to a specific location there. - There is a special multi-level branch of the dungeon with pre- - mapped levels based on the classic computer game "Sokoban." In that - game, you operate as a warehouse worker who pushes crates around - obstacles to position them at designated locations. In NetHack, the + There is a special multi-level branch of the dungeon with pre- + mapped levels based on the classic computer game "Sokoban." In that + game, you operate as a warehouse worker who pushes crates around + obstacles to position them at designated locations. In NetHack, the goal is to push boulders into pits or holes until those traps have all - been nullified, giving access to whatever is beyond them. In the + been nullified, giving access to whatever is beyond them. In the - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -2446,63 +2446,63 @@ - Sokoban game, you can only move in the four cardinal compass direc- - tions, and a crate in its final destination blocks further access to - that spot. In the Sokoban levels of NetHack, you can move diagonally - (unless that would let you pass between two neighboring boulders) but - you can only push boulders in the four cardinal directions, and a - boulder which fills a pit or hole removes both the boulder and the - trap so opens up normal access to that spot. With careful foresight, - it is possible to complete all of the levels according to the tradi- - tional rules of Sokoban. (Hint: to solve Sokoban puzzles, you often - need to move things away from their eventual destinations in order to - open up more room to maneuver.) Since NetHack does not support an - undo capability, some allowances are permitted in case you get stuck. - For example, each level has at least one extra boulder. Also, it is - possible to drop everything in order to be able to squeeze into the - same location as a boulder (and then presumably move past it), or to - destroy a boulder with magic or tools, or to create new boulders with - a scroll of earth. However, doing such things will lower your luck - without any specific message given about that. See the Conduct sec- - tion for information about getting feedback for your actions in + Sokoban game, you can only move in the four cardinal compass direc- + tions, and a crate in its final destination blocks further access to + that spot. In the Sokoban levels of NetHack, you can move diagonally + (unless that would let you pass between two neighboring boulders) but + you can only push boulders in the four cardinal directions, and a + boulder which fills a pit or hole removes both the boulder and the + trap so opens up normal access to that spot. With careful foresight, + it is possible to complete all of the levels according to the tradi- + tional rules of Sokoban. (Hint: to solve Sokoban puzzles, you often + need to move things away from their eventual destinations in order to + open up more room to maneuver.) Since NetHack does not support an + undo capability, some allowances are permitted in case you get stuck. + For example, each level has at least one extra boulder. Also, it is + possible to drop everything in order to be able to squeeze into the + same location as a boulder (and then presumably move past it), or to + destroy a boulder with magic or tools, or to create new boulders with + a scroll of earth. However, doing such things will lower your luck + without any specific message given about that. See the Conduct sec- + tion for information about getting feedback for your actions in Sokoban. 5.3. Stairs and ladders (`<', `>') In general, each level in the dungeon will have a staircase going - up (`<') to the previous level and another going down (`>') to the - next level. There are some exceptions though. For instance, fairly - early in the dungeon you will find a level with two down staircases, - one continuing into the dungeon and the other branching into an area + up (`<') to the previous level and another going down (`>') to the + next level. There are some exceptions though. For instance, fairly + early in the dungeon you will find a level with two down staircases, + one continuing into the dungeon and the other branching into an area known as the Gnomish Mines. Those mines eventually hit a dead end, so - after exploring them (if you choose to do so), you'll need to climb + after exploring them (if you choose to do so), you'll need to climb back up to the main dungeon. - When you traverse a set of stairs, or trigger a trap which sends + When you traverse a set of stairs, or trigger a trap which sends you to another level, the level you're leaving will be deactivated and - stored in a file on disk. If you're moving to a previously visited - level, it will be loaded from its file on disk and reactivated. If - you're moving to a level which has not yet been visited, it will be + stored in a file on disk. If you're moving to a previously visited + level, it will be loaded from its file on disk and reactivated. If + you're moving to a level which has not yet been visited, it will be created (from scratch for most random levels, from a template for some - "special" levels, or loaded from the remains of an earlier game for a - "bones" level as briefly described below). Monsters are only active - on the current level; those on other levels are essentially placed + "special" levels, or loaded from the remains of an earlier game for a + "bones" level as briefly described below). Monsters are only active + on the current level; those on other levels are essentially placed into stasis. Ordinarily when you climb a set of stairs, you will arrive on the - corresponding staircase at your destination. However, pets (see - below) and some other monsters will follow along if they're close - enough when you travel up or down stairs, and occasionally one of + corresponding staircase at your destination. However, pets (see + below) and some other monsters will follow along if they're close + enough when you travel up or down stairs, and occasionally one of these creatures will displace you during the climb. When that occurs, the pet or other monster will arrive on the staircase and you will end up nearby. - Ladders serve the same purpose as staircases, and the two types - of inter-level connections are nearly indistinguishable during game + Ladders serve the same purpose as staircases, and the two types + of inter-level connections are nearly indistinguishable during game play. - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -2514,36 +2514,36 @@ 5.4. Shops and shopping - Occasionally you will run across a room with a shopkeeper near - the door and many items lying on the floor. You can buy items by + Occasionally you will run across a room with a shopkeeper near + the door and many items lying on the floor. You can buy items by picking them up and then using the `p' command. You can inquire about - the price of an item prior to picking it up by using the "#chat" com- - mand while standing on it. Using an item prior to paying for it will - incur a charge, and the shopkeeper won't allow you to leave the shop + the price of an item prior to picking it up by using the "#chat" com- + mand while standing on it. Using an item prior to paying for it will + incur a charge, and the shopkeeper won't allow you to leave the shop until you have paid any debt you owe. - You can sell items to a shopkeeper by dropping them to the floor + You can sell items to a shopkeeper by dropping them to the floor while inside a shop. You will either be offered an amount of gold and asked whether you're willing to sell, or you'll be told that the shop- - keeper isn't interested (generally, your item needs to be compatible + keeper isn't interested (generally, your item needs to be compatible with the type of merchandise carried by the shop). - If you drop something in a shop by accident, the shopkeeper will - usually claim ownership without offering any compensation. You'll + If you drop something in a shop by accident, the shopkeeper will + usually claim ownership without offering any compensation. You'll have to buy it back if you want to reclaim it. Shopkeepers sometime run out of money. When that happens, you'll - be offered credit instead of gold when you try to sell something. - Credit can be used to pay for purchases, but it is only good in the + be offered credit instead of gold when you try to sell something. + Credit can be used to pay for purchases, but it is only good in the shop where it was obtained; other shopkeepers won't honor it. (If you - happen to find a "credit card" in the dungeon, don't bother trying to + happen to find a "credit card" in the dungeon, don't bother trying to use it in shops; shopkeepers will not accept it.) - The `$' command, which reports the amount of gold you are carry- - ing, will also show current shop debt or credit, if any. The "Iu" - command lists unpaid items (those which still belong to the shop) if - you are carrying any. The "Ix" command shows an inventory-like dis- - play of any unpaid items which have been used up, along with other + The `$' command, which reports the amount of gold you are carry- + ing, will also show current shop debt or credit, if any. The "Iu" + command lists unpaid items (those which still belong to the shop) if + you are carrying any. The "Ix" command shows an inventory-like dis- + play of any unpaid items which have been used up, along with other shop fees, if any. 5.4.1. Shop idiosyncrasies @@ -2552,23 +2552,23 @@ * The price of a given item can vary due to a variety of factors. - * A shopkeeper treats the spot immediately inside the door as if it + * A shopkeeper treats the spot immediately inside the door as if it were outside the shop. - * While the shopkeeper watches you like a hawk, he or she will gener- + * While the shopkeeper watches you like a hawk, he or she will gener- ally ignore any other customers. - * If a shop is "closed for inventory," it will not open of its own + * If a shop is "closed for inventory," it will not open of its own accord. - * Shops do not get restocked with new items, regardless of inventory + * Shops do not get restocked with new items, regardless of inventory depletion. - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -2580,61 +2580,61 @@ 5.5. Movement feedback - Moving around the map usually provides no feedback--other than - drawing the hero at the new location--unless you step on an object or - pile of objects, or on a trap, or attempt to move onto a spot where a - monster is located. There are several options which can be used to + Moving around the map usually provides no feedback--other than + drawing the hero at the new location--unless you step on an object or + pile of objects, or on a trap, or attempt to move onto a spot where a + monster is located. There are several options which can be used to augment the normal feedback. - The pile_limit option controls how many objects can be in a - pile--sharing the same map location--for the game to state "there are - objects here" instead of listing them. The default is 5. Setting it - to 1 would always give that message instead of listing any objects. - Setting it to 0 is a special case which will always list all objects + The pile_limit option controls how many objects can be in a + pile--sharing the same map location--for the game to state "there are + objects here" instead of listing them. The default is 5. Setting it + to 1 would always give that message instead of listing any objects. + Setting it to 0 is a special case which will always list all objects no matter how big a pile is. Note that the number refers to the count of separate stacks of objects present rather than the sum of the quan- - tities of those stacks (so 7 arrows or 25 gold pieces will each count - as 1 rather than as 7 and 25, respectively, and total to 2 when both + tities of those stacks (so 7 arrows or 25 gold pieces will each count + as 1 rather than as 7 and 25, respectively, and total to 2 when both are at the same location). - The "nopickup" command prefix (default `m') can be used before a - movement direction to step on objects without attempting auto-pickup + The "nopickup" command prefix (default `m') can be used before a + movement direction to step on objects without attempting auto-pickup and without giving feedback about them. The mention_walls option controls whether you get feedback if you - try to walk into a wall or solid stone or off the edge of the map. - Normally nothing happens (unless the hero is blind and no wall is - shown, then the wall that is being bumped into will be drawn on the - map). This option also gives feedback when rushing or running stops + try to walk into a wall or solid stone or off the edge of the map. + Normally nothing happens (unless the hero is blind and no wall is + shown, then the wall that is being bumped into will be drawn on the + map). This option also gives feedback when rushing or running stops for some non-obvious reason. - The mention_decor option controls whether you get feedback when - walking on "furniture." Normally stepping onto stairs or a fountain - or an altar or various other things doesn't elicit anything unless it - is covered by one or more objects so is obscured on the map. Setting - this option to true will describe such things even when they aren't - obscured. Doorless doorways and open doors aren't considered worthy + The mention_decor option controls whether you get feedback when + walking on "furniture." Normally stepping onto stairs or a fountain + or an altar or various other things doesn't elicit anything unless it + is covered by one or more objects so is obscured on the map. Setting + this option to true will describe such things even when they aren't + obscured. Doorless doorways and open doors aren't considered worthy of mention; closed doors (if you can move onto their spots) and broken - doors are. Assuming that you're able to do so, moving onto water or - lava or ice will give feedback if not yet on that type of terrain but - not repeat it (unless there has been some intervening message) when - moving from water to another water spot, or lava to lava, or ice to - ice. Moving off of any of those back onto "normal" terrain will give - one message too, unless there is feedback about one or more objects, + doors are. Assuming that you're able to do so, moving onto water or + lava or ice will give feedback if not yet on that type of terrain but + not repeat it (unless there has been some intervening message) when + moving from water to another water spot, or lava to lava, or ice to + ice. Moving off of any of those back onto "normal" terrain will give + one message too, unless there is feedback about one or more objects, in which case the back on land circumstance is implied. - The confirm and safe_pet options control what happens when you + The confirm and safe_pet options control what happens when you try to move onto a peaceful monster's spot or a tame one's spot. - The "nopickup" command prefix (default `m') is also the move- + The "nopickup" command prefix (default `m') is also the move- without-attacking prefix and can be used to try to step onto a visible - monster's spot without the move being considered an attack (see the - Fighting subsection of Monsters below). The "fight" command prefix - (default `F'; also `-' if number_pad is on) can be used to force an - attack, when guessing where an unseen monster is or when deliberately + monster's spot without the move being considered an attack (see the + Fighting subsection of Monsters below). The "fight" command prefix + (default `F'; also `-' if number_pad is on) can be used to force an + attack, when guessing where an unseen monster is or when deliberately - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -2646,22 +2646,22 @@ attacking a peaceful or tame creature. - The run_mode option controls how frequently the map gets redrawn - when moving more than one step in a single command (so when rushing, + The run_mode option controls how frequently the map gets redrawn + when moving more than one step in a single command (so when rushing, running, or traveling). 5.6. Rogue level - One dungeon level (occurring in mid to late teens of the main + One dungeon level (occurring in mid to late teens of the main dungeon) is a tribute to the ancestor game hack's inspiration rogue. - It is usually displayed differently from other levels: possibly - in characters instead of tiles, or without line-drawing symbols if - already in characters; also, gold is shown as * rather than $ and - stairs are shown as % rather than < and >. There are some minor dif- - ferences in actual game play: doorways lack doors; a scroll, wand, or - spell of light used in a room lights up the whole room rather than - within a radius around your character. And monsters represented by + It is usually displayed differently from other levels: possibly + in characters instead of tiles, or without line-drawing symbols if + already in characters; also, gold is shown as * rather than $ and + stairs are shown as % rather than < and >. There are some minor dif- + ferences in actual game play: doorways lack doors; a scroll, wand, or + spell of light used in a room lights up the whole room rather than + within a radius around your character. And monsters represented by lower-case letters aren't randomly generated on the rogue level. The slight strangeness of this level is a feature, not a bug.... @@ -2669,38 +2669,38 @@ 6. Monsters Monsters you cannot see are not displayed on the screen. Beware! - You may suddenly come upon one in a dark place. Some magic items can - help you locate them before they locate you (which some monsters can + You may suddenly come upon one in a dark place. Some magic items can + help you locate them before they locate you (which some monsters can do very well). - The commands `/' and `;' may be used to obtain information about - those monsters who are displayed on the screen. The command "#name" - (by default bound to `C'), allows you to assign a name to a monster, + The commands `/' and `;' may be used to obtain information about + those monsters who are displayed on the screen. The command "#name" + (by default bound to `C'), allows you to assign a name to a monster, which may be useful to help distinguish one from another when multiple - monsters are present. Assigning a name which is just a space will + monsters are present. Assigning a name which is just a space will remove any prior name. - The extended command "#chat" can be used to interact with an - adjacent monster. There is no actual dialog (in other words, you - don't get to choose what you'll say), but chatting with some monsters - such as a shopkeeper or the Oracle of Delphi can produce useful + The extended command "#chat" can be used to interact with an + adjacent monster. There is no actual dialog (in other words, you + don't get to choose what you'll say), but chatting with some monsters + such as a shopkeeper or the Oracle of Delphi can produce useful results. 6.1. Fighting - If you see a monster and you wish to fight it, just attempt to - walk into it. Many monsters you find will mind their own business + If you see a monster and you wish to fight it, just attempt to + walk into it. Many monsters you find will mind their own business unless you attack them. Some of them are very dangerous when angered. Remember: discretion is the better part of valor. - In most circumstances, if you attempt to attack a peaceful mon- - ster by moving into its location, you'll be asked to confirm your - intent. By default an answer of `y' acknowledges that intent, which + In most circumstances, if you attempt to attack a peaceful mon- + ster by moving into its location, you'll be asked to confirm your + intent. By default an answer of `y' acknowledges that intent, which can be error prone if you're using `y' to move. You can set the para- - noid_confirmation:attack option to require a response of "yes" + noid_confirmation:attack option to require a response of "yes" - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -2712,61 +2712,61 @@ instead. - If you can't see a monster (if it is invisible, or if you are + If you can't see a monster (if it is invisible, or if you are blinded), the symbol `I' will be shown when you learn of its presence. - If you attempt to walk into it, you will try to fight it just like a - monster that you can see; of course, if the monster has moved, you - will attack empty air. If you guess that the monster has moved and - you don't wish to fight, you can use the `m' command to move without - fighting; likewise, if you don't remember a monster but want to try + If you attempt to walk into it, you will try to fight it just like a + monster that you can see; of course, if the monster has moved, you + will attack empty air. If you guess that the monster has moved and + you don't wish to fight, you can use the `m' command to move without + fighting; likewise, if you don't remember a monster but want to try fighting anyway, you can use the `F' command. 6.2. Your pet You start the game with a little dog (`d'), kitten (`f'), or pony - (`u'), which follows you about the dungeon and fights monsters with + (`u'), which follows you about the dungeon and fights monsters with you. Like you, your pet needs food to survive. Dogs and cats usually feed themselves on fresh carrion and other meats; horses need vegetar- ian food which is harder to come by. If you're worried about your pet - or want to train it, you can feed it, too, by throwing it food. A + or want to train it, you can feed it, too, by throwing it food. A properly trained pet can be very useful under certain circumstances. - Your pet also gains experience from killing monsters, and can - grow over time, gaining hit points and doing more damage. Initially, - your pet may even be better at killing things than you, which makes + Your pet also gains experience from killing monsters, and can + grow over time, gaining hit points and doing more damage. Initially, + your pet may even be better at killing things than you, which makes pets useful for low-level characters. - Your pet will follow you up and down staircases if it is next to + Your pet will follow you up and down staircases if it is next to you when you move. Otherwise your pet will be stranded and may become - wild. Similarly, when you trigger certain types of traps which alter - your location (for instance, a trap door which drops you to a lower - dungeon level), any adjacent pet will accompany you and any non-adja- + wild. Similarly, when you trigger certain types of traps which alter + your location (for instance, a trap door which drops you to a lower + dungeon level), any adjacent pet will accompany you and any non-adja- cent pet will be left behind. Your pet may trigger such traps itself; you will not be carried along with it even if adjacent at the time. 6.3. Steeds - Some types of creatures in the dungeon can actually be ridden if - you have the right equipment and skill. Convincing a wild beast to + Some types of creatures in the dungeon can actually be ridden if + you have the right equipment and skill. Convincing a wild beast to let you saddle it up is difficult to say the least. Many a dungeoneer - has had to resort to magic and wizardry in order to forge the - alliance. Once you do have the beast under your control however, you - can easily climb in and out of the saddle with the "#ride" command. - Lead the beast around the dungeon when riding, in the same manner as - you would move yourself. It is the beast that you will see displayed + has had to resort to magic and wizardry in order to forge the + alliance. Once you do have the beast under your control however, you + can easily climb in and out of the saddle with the "#ride" command. + Lead the beast around the dungeon when riding, in the same manner as + you would move yourself. It is the beast that you will see displayed on the map. - Riding skill is managed by the "#enhance" command. See the sec- + Riding skill is managed by the "#enhance" command. See the sec- tion on Weapon proficiency for more information about that. - Use the `a' (apply) command and pick a saddle in your inventory + Use the `a' (apply) command and pick a saddle in your inventory to attempt to put that saddle on an adjacent creature. If successful, it will be transferred to that creature's inventory. - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -2776,63 +2776,63 @@ - Use the "#loot" command while adjacent to a saddled creature to - try to remove the saddle from that creature. If successful, it will + Use the "#loot" command while adjacent to a saddled creature to + try to remove the saddle from that creature. If successful, it will be transferred to your inventory. 6.4. Bones levels You may encounter the shades and corpses of other adventurers (or - even former incarnations of yourself!) and their personal effects. - Ghosts are hard to kill, but easy to avoid, since they're slow and do + even former incarnations of yourself!) and their personal effects. + Ghosts are hard to kill, but easy to avoid, since they're slow and do little damage. You can plunder the deceased adventurer's possessions; - however, they are likely to be cursed. Beware of whatever killed the - former player; it is probably still lurking around, gloating over its + however, they are likely to be cursed. Beware of whatever killed the + former player; it is probably still lurking around, gloating over its last victory. 6.5. Persistence of Monsters - Monsters (a generic reference which also includes humans and + Monsters (a generic reference which also includes humans and pets) are only shown while they can be seen or otherwise sensed. Mov- ing to a location where you can't see or sense a monster any more will - result in it disappearing from your map, similarly if it is the one + result in it disappearing from your map, similarly if it is the one who moved rather than you. - However, if you encounter a monster which you can't see or + However, if you encounter a monster which you can't see or sense--perhaps it is invisible and has just tapped you on the noggin-- a special "remembered, unseen monster" marker will be displayed at the - location where you think it is. That will persist until you have - proven that there is no monster there, even if the unseen monster - moves to another location or you move to a spot where the marker's + location where you think it is. That will persist until you have + proven that there is no monster there, even if the unseen monster + moves to another location or you move to a spot where the marker's location ordinarily wouldn't be seen any more. 7. Objects - When you find something in the dungeon, it is common to want to - pick it up. In NetHack, this is accomplished by using the `,' com- - mand. If autopickup option is on, you will automatically pick up the + When you find something in the dungeon, it is common to want to + pick it up. In NetHack, this is accomplished by using the `,' com- + mand. If autopickup option is on, you will automatically pick up the object by walking over, unless you move with the `m' prefix. - If you're carrying too many items, NetHack will tell you so and - you won't be able to pick up anything more. Otherwise, it will add + If you're carrying too many items, NetHack will tell you so and + you won't be able to pick up anything more. Otherwise, it will add the object(s) to your pack and tell you what you just picked up. - As you add items to your inventory, you also add the weight of - that object to your load. The amount that you can carry depends on - your strength and your constitution. The stronger and sturdier you - are, the less the additional load will affect you. There comes a - point, though, when the weight of all of that stuff you are carrying + As you add items to your inventory, you also add the weight of + that object to your load. The amount that you can carry depends on + your strength and your constitution. The stronger and sturdier you + are, the less the additional load will affect you. There comes a + point, though, when the weight of all of that stuff you are carrying around with you through the dungeon will encumber you. Your reactions - will get slower and you'll burn calories faster, requiring food more - frequently to cope with it. Eventually, you'll be so overloaded that + will get slower and you'll burn calories faster, requiring food more + frequently to cope with it. Eventually, you'll be so overloaded that you'll either have to discard some of what you're carrying or collapse under its weight. NetHack will tell you how badly you have loaded yourself. If you - are encumbered, one of the conditions Burdened, Stressed, Strained, + are encumbered, one of the conditions Burdened, Stressed, Strained, - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -2842,63 +2842,63 @@ - Overtaxed, or Overloaded will be shown on the bottom line status dis- + Overtaxed, or Overloaded will be shown on the bottom line status dis- play. - When you pick up an object, it is assigned an inventory letter. - Many commands that operate on objects must ask you to find out which - object you want to use. When NetHack asks you to choose a particular - object you are carrying, you are usually presented with a list of + When you pick up an object, it is assigned an inventory letter. + Many commands that operate on objects must ask you to find out which + object you want to use. When NetHack asks you to choose a particular + object you are carrying, you are usually presented with a list of inventory letters to choose from (see Commands, above). - Some objects, such as weapons, are easily differentiated. Oth- - ers, like scrolls and potions, are given descriptions which vary - according to type. During a game, any two objects with the same - description are the same type. However, the descriptions will vary + Some objects, such as weapons, are easily differentiated. Oth- + ers, like scrolls and potions, are given descriptions which vary + according to type. During a game, any two objects with the same + description are the same type. However, the descriptions will vary from game to game. - When you use one of these objects, if its effect is obvious, - NetHack will remember what it is for you. If its effect isn't - extremely obvious, you will be asked what you want to call this type - of object so you will recognize it later. You can also use the + When you use one of these objects, if its effect is obvious, + NetHack will remember what it is for you. If its effect isn't + extremely obvious, you will be asked what you want to call this type + of object so you will recognize it later. You can also use the "#name" command, for the same purpose at any time, to name all objects - of a particular type or just an individual object. When you use - "#name" on an object which has already been named, specifying a space - as the value will remove the prior name instead of assigning a new + of a particular type or just an individual object. When you use + "#name" on an object which has already been named, specifying a space + as the value will remove the prior name instead of assigning a new one. 7.1. Curses and Blessings - Any object that you find may be cursed, even if the object is - otherwise helpful. The most common effect of a curse is being stuck - with (and to) the item. Cursed weapons weld themselves to your hand + Any object that you find may be cursed, even if the object is + otherwise helpful. The most common effect of a curse is being stuck + with (and to) the item. Cursed weapons weld themselves to your hand when wielded, so you cannot unwield them. Any cursed item you wear is - not removable by ordinary means. In addition, cursed arms and armor - usually, but not always, bear negative enchantments that make them - less effective in combat. Other cursed objects may act poorly or + not removable by ordinary means. In addition, cursed arms and armor + usually, but not always, bear negative enchantments that make them + less effective in combat. Other cursed objects may act poorly or detrimentally in other ways. - Objects can also be blessed instead. Blessed items usually work - better or more beneficially than normal uncursed items. For example, + Objects can also be blessed instead. Blessed items usually work + better or more beneficially than normal uncursed items. For example, a blessed weapon will do slightly more damage against demons. - Objects which are neither cursed nor blessed are referred to as + Objects which are neither cursed nor blessed are referred to as uncursed. They could just as easily have been described as unblessed, - but the uncursed designation is what you will see within the game. A + but the uncursed designation is what you will see within the game. A "glass half full versus glass half empty" situation; make of that what you will. - There are magical means of bestowing or removing curses upon - objects, so even if you are stuck with one, you can still have the - curse lifted and the item removed. Priests and Priestesses have an - innate sensitivity to this property in any object, so they can more - easily avoid cursed objects than other character roles. Dropping - objects onto an altar will reveal their bless or curse state provided + There are magical means of bestowing or removing curses upon + objects, so even if you are stuck with one, you can still have the + curse lifted and the item removed. Priests and Priestesses have an + innate sensitivity to this property in any object, so they can more + easily avoid cursed objects than other character roles. Dropping + objects onto an altar will reveal their bless or curse state provided that you can see them land. - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -2908,44 +2908,44 @@ - An item with unknown status will be reported in your inventory - with no prefix. An item which you know the state of will be distin- - guished in your inventory by the presence of the word cursed, - uncursed, or blessed in the description of the item. In some cases + An item with unknown status will be reported in your inventory + with no prefix. An item which you know the state of will be distin- + guished in your inventory by the presence of the word cursed, + uncursed, or blessed in the description of the item. In some cases uncursed will be omitted as being redundant when enough other informa- - tion is displayed. The implicit_uncursed option can be used to con- - trol this; toggle it off to have uncursed be displayed even when that + tion is displayed. The implicit_uncursed option can be used to con- + trol this; toggle it off to have uncursed be displayed even when that can be deduced from other attributes. - Sometimes the bless or curse state of objects is referred to as - their "BUC" attribute, for Blessed, Uncursed, or Cursed state, or + Sometimes the bless or curse state of objects is referred to as + their "BUC" attribute, for Blessed, Uncursed, or Cursed state, or "BUCX" for Blessed, Uncursed, Cursed, or unknown. (The term beatitude is occasionally used as well.) 7.2. Artifacts - Some objects have been imbued with special powers and are known - as Artifacts. They have specific types (such as long sword or orcish + Some objects have been imbued with special powers and are known + as Artifacts. They have specific types (such as long sword or orcish dagger) and distinct names such as Giantslayer or Grimtooth. Artifact - weapons typically do more damage than their ordinary counterparts. - Some do extra damage against all monsters, others only against spe- - cific types of monsters so aren't better than regular weapons against - other types. Some confer defensive capabilities when wielded or have + weapons typically do more damage than their ordinary counterparts. + Some do extra damage against all monsters, others only against spe- + cific types of monsters so aren't better than regular weapons against + other types. Some confer defensive capabilities when wielded or have other powers that aren't listed here. - You might find them simply lying on the floor, including but not - limited to inside shops, or be granted as a reward for "#offer" on an - altar to your patron deity. A few might be dropped by monsters, or - might be converted from an ordinary object of the same type via - assigning the right name. Or you might wish for them, if you happen + You might find them simply lying on the floor, including but not + limited to inside shops, or be granted as a reward for "#offer" on an + altar to your patron deity. A few might be dropped by monsters, or + might be converted from an ordinary object of the same type via + assigning the right name. Or you might wish for them, if you happen to be granted a wish, but such wishes can fail. - Some artifacts have a specific alignment, others don't. You - won't obtain aligned ones that have a different alignment from yours - via offering and might get a shock if you attempt to wish for any of + Some artifacts have a specific alignment, others don't. You + won't obtain aligned ones that have a different alignment from yours + via offering and might get a shock if you attempt to wish for any of those or find one and attempt to use it. - Each role has a distinct artifact that is contained in the Quest + Each role has a distinct artifact that is contained in the Quest dungeon branch. These are commonly known as quest artifacts. All are aligned and most are non-weapons. They won't be found randomly. @@ -2955,16 +2955,16 @@ 7.3. Relics There are three unique items that are named and have limited spe- - cial powers but aren't classified as artifacts. Each is guarded by a - particular monster and you'll need to collect all three for use late - in the game. They are the Bell of Opening, the Book of the Dead, and - the Candelabrum of Invocation. Their corresponding descriptions when - not yet identified are silver bell, papyrus spellbook, and cande- + cial powers but aren't classified as artifacts. Each is guarded by a + particular monster and you'll need to collect all three for use late + in the game. They are the Bell of Opening, the Book of the Dead, and + the Candelabrum of Invocation. Their corresponding descriptions when + not yet identified are silver bell, papyrus spellbook, and cande- labrum. - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -2976,61 +2976,61 @@ 7.4. Weapons (`)') - Given a chance, most monsters in the Mazes of Menace will gratu- - itously try to kill you. You need weapons for self-defense (killing - them first). Without a weapon, you do only 1-2 hit points of damage - (plus bonuses, if any). Monk characters are an exception; they nor- - mally do more damage with bare (or gloved) hands than they do with + Given a chance, most monsters in the Mazes of Menace will gratu- + itously try to kill you. You need weapons for self-defense (killing + them first). Without a weapon, you do only 1-2 hit points of damage + (plus bonuses, if any). Monk characters are an exception; they nor- + mally do more damage with bare (or gloved) hands than they do with weapons. - There are wielded weapons, like maces and swords, and thrown - weapons, like arrows and spears. To hit monsters with a weapon, you - must wield it and attack them, or throw it at them. You can simply - elect to throw a spear. To shoot an arrow, you should first wield a - bow, then throw the arrow. Crossbows shoot crossbow bolts. Slings + There are wielded weapons, like maces and swords, and thrown + weapons, like arrows and spears. To hit monsters with a weapon, you + must wield it and attack them, or throw it at them. You can simply + elect to throw a spear. To shoot an arrow, you should first wield a + bow, then throw the arrow. Crossbows shoot crossbow bolts. Slings hurl rocks and (other) stones (like gems). - Enchanted weapons have a "plus" (or "to hit enhancement" which - can be either positive or negative) that adds to your chance to hit - and the damage you do to a monster. The only way to determine a + Enchanted weapons have a "plus" (or "to hit enhancement" which + can be either positive or negative) that adds to your chance to hit + and the damage you do to a monster. The only way to determine a weapon's enchantment is to have it magically identified somehow. Most - weapons are subject to some type of damage like rust. Such "erosion" + weapons are subject to some type of damage like rust. Such "erosion" damage can be repaired. - The chance that an attack will successfully hit a monster, and - the amount of damage such a hit will do, depends upon many factors. - Among them are: type of weapon, quality of weapon (enchantment and/or + The chance that an attack will successfully hit a monster, and + the amount of damage such a hit will do, depends upon many factors. + Among them are: type of weapon, quality of weapon (enchantment and/or erosion), experience level, strength, dexterity, encumbrance, and pro- - ficiency (see below). The monster's armor class--a general defense - rating, not necessarily due to wearing of armor--is a factor too; - also, some monsters are particularly vulnerable to certain types of + ficiency (see below). The monster's armor class--a general defense + rating, not necessarily due to wearing of armor--is a factor too; + also, some monsters are particularly vulnerable to certain types of weapons. Many weapons can be wielded in one hand; some require both hands. When wielding a two-handed weapon, you can not wear a shield, and vice versa. When wielding a one-handed weapon, you can have another weapon - ready to use by setting things up with the `x' command, which - exchanges your primary (the one being wielded) and alternate weapons. - And if you have proficiency in the "two weapon combat" skill, you may - wield both weapons simultaneously as primary and secondary; use the - `X' command to engage or disengage that. Only some types of charac- - ters (barbarians, for instance) have the necessary skill available. - Even with that skill, using two weapons at once incurs a penalty in - the chance to hit your target compared to using just one weapon at a + ready to use by setting things up with the `x' command, which + exchanges your primary (the one being wielded) and alternate weapons. + And if you have proficiency in the "two weapon combat" skill, you may + wield both weapons simultaneously as primary and secondary; use the + `X' command to engage or disengage that. Only some types of charac- + ters (barbarians, for instance) have the necessary skill available. + Even with that skill, using two weapons at once incurs a penalty in + the chance to hit your target compared to using just one weapon at a time. - There might be times when you'd rather not wield any weapon at + There might be times when you'd rather not wield any weapon at all. To accomplish that, wield `-', or else use the `A' command which - allows you to unwield the current weapon in addition to taking off + allows you to unwield the current weapon in addition to taking off other worn items. - Those of you in the audience who are AD&D players, be aware that + Those of you in the audience who are AD&D players, be aware that each weapon which existed in AD&D does roughly the same damage to mon- - sters in NetHack. Some of the more obscure weapons (such as the + sters in NetHack. Some of the more obscure weapons (such as the aklys, lucern hammer, and bec-de-corbin) are defined in an appendix to - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -3042,61 +3042,61 @@ Unearthed Arcana, an AD&D supplement. - The commands to use weapons are `w' (wield), `t' (throw), `f' - (fire), `Q' (quiver), `x' (exchange), `X' (twoweapon), and "#enhance" + The commands to use weapons are `w' (wield), `t' (throw), `f' + (fire), `Q' (quiver), `x' (exchange), `X' (twoweapon), and "#enhance" (see below). 7.4.1. Throwing and shooting - You can throw just about anything via the `t' command. It will - prompt for the item to throw; picking `?' will list things in your - inventory which are considered likely to be thrown, or picking `*' - will list your entire inventory. After you've chosen what to throw, - you will be prompted for a direction rather than for a specific tar- - get. The distance something can be thrown depends mainly on the type + You can throw just about anything via the `t' command. It will + prompt for the item to throw; picking `?' will list things in your + inventory which are considered likely to be thrown, or picking `*' + will list your entire inventory. After you've chosen what to throw, + you will be prompted for a direction rather than for a specific tar- + get. The distance something can be thrown depends mainly on the type of object and your strength. Arrows can be thrown by hand, but can be - thrown much farther and will be more likely to hit when thrown while + thrown much farther and will be more likely to hit when thrown while you are wielding a bow. - Some weapons will return when thrown. A boomerang--provided it - fails to hit anything--is an obvious example. If an aklys (thonged - club) is thrown while it is wielded, it will return even when it hits - something. A sufficiently strong hero can throw the warhammer Mjoll- - nir; when thrown by a Valkyrie it will return too. However, aklyses - and Mjollnir occasionally fail to return. Returning thrown objects - occasionally fail to be caught, sometimes even hitting the thrower, + Some weapons will return when thrown. A boomerang--provided it + fails to hit anything--is an obvious example. If an aklys (thonged + club) is thrown while it is wielded, it will return even when it hits + something. A sufficiently strong hero can throw the warhammer Mjoll- + nir; when thrown by a Valkyrie it will return too. However, aklyses + and Mjollnir occasionally fail to return. Returning thrown objects + occasionally fail to be caught, sometimes even hitting the thrower, but when caught they become re-wielded. - You can simplify the throwing operation by using the `Q' command - to select your preferred "missile", then using the `f' command to - throw it. You'll be prompted for a direction as above, but you don't - have to specify which item to throw each time you use `f'. There is - also an option, autoquiver, which has NetHack choose another item to - automatically fill your quiver (or quiver sack, or have at the ready) - when the inventory slot used for `Q' runs out. If your quiver is - empty, autoquiver is false, and you are wielding a weapon which + You can simplify the throwing operation by using the `Q' command + to select your preferred "missile", then using the `f' command to + throw it. You'll be prompted for a direction as above, but you don't + have to specify which item to throw each time you use `f'. There is + also an option, autoquiver, which has NetHack choose another item to + automatically fill your quiver (or quiver sack, or have at the ready) + when the inventory slot used for `Q' runs out. If your quiver is + empty, autoquiver is false, and you are wielding a weapon which returns when thrown, you will throw that weapon instead of filling the - quiver. The fire command also has extra assistance, if fireassist is + quiver. The fire command also has extra assistance, if fireassist is on it will try to wield a launcher matching the ammo in the quiver. - Some characters have the ability to throw or shoot a volley of - multiple items (from the same stack) in a single action. Knowing how + Some characters have the ability to throw or shoot a volley of + multiple items (from the same stack) in a single action. Knowing how to load several rounds of ammunition at once--or hold several missiles in your hand--and still hit a target is not an easy task. Rangers are among those who are adept at this task, as are those with a high level - of proficiency in the relevant weapon skill (in bow skill if you're + of proficiency in the relevant weapon skill (in bow skill if you're wielding one to shoot arrows, in crossbow skill if you're wielding one - to shoot bolts, or in sling skill if you're wielding one to shoot - stones). The number of items that the character has a chance to fire - varies from turn to turn. You can explicitly limit the number of - shots by using a numeric prefix before the `t' or `f' command. For + to shoot bolts, or in sling skill if you're wielding one to shoot + stones). The number of items that the character has a chance to fire + varies from turn to turn. You can explicitly limit the number of + shots by using a numeric prefix before the `t' or `f' command. For example, "2f" (or "n2f" if using number_pad mode) would ensure that at most 2 arrows are shot even if you could have fired 3. If you specify - a larger number than would have been shot ("4f" in this example), - you'll just end up shooting the same number (3, here) as if no limit + a larger number than would have been shot ("4f" in this example), + you'll just end up shooting the same number (3, here) as if no limit - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -3106,63 +3106,63 @@ - had been specified. Once the volley is in motion, all of the items - will travel in the same direction; if the first ones kill a monster, + had been specified. Once the volley is in motion, all of the items + will travel in the same direction; if the first ones kill a monster, the others can still continue beyond that spot. 7.4.2. Weapon proficiency - You will have varying degrees of skill in the weapons available. + You will have varying degrees of skill in the weapons available. Weapon proficiency, or weapon skills, affect how well you can use par- ticular types of weapons, and you'll be able to improve your skills as - you progress through a game, depending on your role, your experience + you progress through a game, depending on your role, your experience level, and use of the weapons. - For the purposes of proficiency, weapons have been divided up - into various groups such as daggers, broadswords, and polearms. Each - role has a limit on what level of proficiency a character can achieve - for each group. For instance, wizards can become highly skilled in + For the purposes of proficiency, weapons have been divided up + into various groups such as daggers, broadswords, and polearms. Each + role has a limit on what level of proficiency a character can achieve + for each group. For instance, wizards can become highly skilled in daggers or staves but not in swords or bows. The "#enhance" extended command is used to review current weapons - proficiency (also spell proficiency) and to choose which skill(s) to - improve when you've used one or more skills enough to become eligible - to do so. The skill rankings are "none" (sometimes also referred to - as "restricted", because you won't be able to advance), "unskilled", - "basic", "skilled", and "expert". Restricted skills simply will not - appear in the list shown by "#enhance". (Divine intervention might - unrestrict a particular skill, in which case it will start at + proficiency (also spell proficiency) and to choose which skill(s) to + improve when you've used one or more skills enough to become eligible + to do so. The skill rankings are "none" (sometimes also referred to + as "restricted", because you won't be able to advance), "unskilled", + "basic", "skilled", and "expert". Restricted skills simply will not + appear in the list shown by "#enhance". (Divine intervention might + unrestrict a particular skill, in which case it will start at unskilled and be limited to basic.) Some characters can enhance their - barehanded combat or martial arts skill beyond expert to "master" or + barehanded combat or martial arts skill beyond expert to "master" or "grand master". - Use of a weapon in which you're restricted or unskilled will - incur a modest penalty in the chance to hit a monster and also in the - amount of damage done when you do hit; at basic level, there is no - penalty or bonus; at skilled level, you receive a modest bonus in the + Use of a weapon in which you're restricted or unskilled will + incur a modest penalty in the chance to hit a monster and also in the + amount of damage done when you do hit; at basic level, there is no + penalty or bonus; at skilled level, you receive a modest bonus in the chance to hit and amount of damage done; at expert level, the bonus is - higher. A successful hit has a chance to boost your training towards + higher. A successful hit has a chance to boost your training towards the next skill level (unless you've already reached the limit for this skill). Once such training reaches the threshold for that next level, - you'll be told that you feel more confident in your skills. At that - point you can use "#enhance" to increase one or more skills. Such - skills are not increased automatically because there is a limit to + you'll be told that you feel more confident in your skills. At that + point you can use "#enhance" to increase one or more skills. Such + skills are not increased automatically because there is a limit to your total overall skills, so you need to actively choose which skills to enhance and which to ignore. 7.4.3. Two-Weapon combat - Some characters can use two weapons at once. Setting things up - to do so can seem cumbersome but becomes second nature with use. To - wield two weapons, you need to use the "#twoweapon" command. But - first you need to have a weapon in each hand. (Note that your two - weapons are not fully equal; the one in the hand you normally wield - with is considered primary and the other one is considered secondary. + Some characters can use two weapons at once. Setting things up + to do so can seem cumbersome but becomes second nature with use. To + wield two weapons, you need to use the "#twoweapon" command. But + first you need to have a weapon in each hand. (Note that your two + weapons are not fully equal; the one in the hand you normally wield + with is considered primary and the other one is considered secondary. The most noticeable difference is after you stop--or before you begin, - for that matter--wielding two weapons at once. The primary is your + for that matter--wielding two weapons at once. The primary is your - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -3172,40 +3172,40 @@ - wielded weapon and the secondary is just an item in your inventory + wielded weapon and the secondary is just an item in your inventory that's been designated as alternate weapon.) - If your primary weapon is wielded but your off hand is empty or - has the wrong weapon, use the sequence `x', `w', `x' to first swap - your primary into your off hand, wield whatever you want as secondary - weapon, then swap them both back into the intended hands. If your - secondary or alternate weapon is correct but your primary one is not, - simply use `w' to wield the primary. Lastly, if neither hand holds + If your primary weapon is wielded but your off hand is empty or + has the wrong weapon, use the sequence `x', `w', `x' to first swap + your primary into your off hand, wield whatever you want as secondary + weapon, then swap them both back into the intended hands. If your + secondary or alternate weapon is correct but your primary one is not, + simply use `w' to wield the primary. Lastly, if neither hand holds the correct weapon, use `w', `x', `w' to first wield the intended sec- ondary, swap it to off hand, and then wield the primary. - The whole process can be simplified via use of the pushweapon - option. When it is enabled, then using `w' to wield something causes - the currently wielded weapon to become your alternate weapon. So the - sequence `w', `w' can be used to first wield the weapon you intend to - be secondary, and then wield the one you want as primary which will + The whole process can be simplified via use of the pushweapon + option. When it is enabled, then using `w' to wield something causes + the currently wielded weapon to become your alternate weapon. So the + sequence `w', `w' can be used to first wield the weapon you intend to + be secondary, and then wield the one you want as primary which will push the first into secondary position. - When in two-weapon combat mode, using the `X' command toggles - back to single-weapon mode. Throwing or dropping either of the weap- - ons or having one of them be stolen or destroyed will also make you + When in two-weapon combat mode, using the `X' command toggles + back to single-weapon mode. Throwing or dropping either of the weap- + ons or having one of them be stolen or destroyed will also make you revert to single-weapon combat. 7.5. Armor (`[') - Lots of unfriendly things lurk about; you need armor to protect - yourself from their blows. Some types of armor offer better protec- - tion than others. Your armor class is a measure of this protection. - Armor class (AC) is measured as in AD&D, with 10 being the equivalent - of no armor, and lower numbers meaning better armor. Each suit of + Lots of unfriendly things lurk about; you need armor to protect + yourself from their blows. Some types of armor offer better protec- + tion than others. Your armor class is a measure of this protection. + Armor class (AC) is measured as in AD&D, with 10 being the equivalent + of no armor, and lower numbers meaning better armor. Each suit of armor which exists in AD&D gives the same protection in NetHack. - Here is a list of the armor class values provided by suits of + Here is a list of the armor class values provided by suits of armor: Dragon scale mail 1 Plate mail, Crystal plate mail 3 @@ -3219,16 +3219,16 @@ Leather jacket 9 none 10 - You can also wear other pieces of armor (cloak over suit, shirt - under suit, helmet, gloves, boots, shield) to lower your armor class + You can also wear other pieces of armor (cloak over suit, shirt + under suit, helmet, gloves, boots, shield) to lower your armor class even further. Most of these provide a one or two point improvement to - AC (making the overall value smaller and eventually negative) but can - also be enchanted. Shirts are an exception; they don't provide any - protection unless enchanted. Some cloaks also don't improve AC when - unenchanted but all cloaks offer some protection against rust or cor- + AC (making the overall value smaller and eventually negative) but can + also be enchanted. Shirts are an exception; they don't provide any + protection unless enchanted. Some cloaks also don't improve AC when + unenchanted but all cloaks offer some protection against rust or cor- - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -3238,63 +3238,63 @@ - rosion to suits worn under them and against some monster touch + rosion to suits worn under them and against some monster touch attacks. - If a piece of armor is enchanted, its armor protection will be + If a piece of armor is enchanted, its armor protection will be better (or worse) than normal, and its "plus" (or minus) will subtract - from your armor class. For example, a +1 chain mail would give you - better protection than normal chain mail, lowering your armor class - one unit further to 4. When you put on a piece of armor, you immedi- - ately find out the armor class and any "plusses" it provides. Cursed - pieces of armor usually have negative enchantments (minuses) in addi- + from your armor class. For example, a +1 chain mail would give you + better protection than normal chain mail, lowering your armor class + one unit further to 4. When you put on a piece of armor, you immedi- + ately find out the armor class and any "plusses" it provides. Cursed + pieces of armor usually have negative enchantments (minuses) in addi- tion to being unremovable. Many types of armor are subject to some kind of damage like rust. - Such damage can be repaired. Some types of armor may inhibit spell + Such damage can be repaired. Some types of armor may inhibit spell casting. - The nudist option can be set (prior to game start) to attempt to - play the entire game without wearing any armor (a self-imposed chal- + The nudist option can be set (prior to game start) to attempt to + play the entire game without wearing any armor (a self-imposed chal- lenge which is extremely difficult to accomplish). The commands to use armor are `W' (wear) and `T' (take off). The `A' command can be used to take off armor as well as other worn items. Also, `P' (put on) and `R' (remove) which are normally for accessories - can be used for armor, but pieces of armor won't be shown as likely + can be used for armor, but pieces of armor won't be shown as likely candidates in a prompt for choosing what to put on or remove. 7.6. Food (`%') - Food is necessary to survive. If you go too long without eating - you will faint, and eventually die of starvation. Some types of food - will spoil, and become unhealthy to eat, if not protected. Food - stored in ice boxes or tins ("cans") will usually stay fresh, but ice + Food is necessary to survive. If you go too long without eating + you will faint, and eventually die of starvation. Some types of food + will spoil, and become unhealthy to eat, if not protected. Food + stored in ice boxes or tins ("cans") will usually stay fresh, but ice boxes are heavy, and tins take a while to open. When you kill monsters, they usually leave corpses which are also - "food." Many, but not all, of these are edible; some also give you - special powers when you eat them. A good rule of thumb is "you are + "food." Many, but not all, of these are edible; some also give you + special powers when you eat them. A good rule of thumb is "you are what you eat." - Some character roles and some monsters are vegetarian. Vegetar- + Some character roles and some monsters are vegetarian. Vegetar- ian monsters will typically never eat animal corpses, while vegetarian players can, but with some rather unpleasant side-effects. - You can name one food item after something you like to eat with + You can name one food item after something you like to eat with the fruit option. The command to eat food is `e'. 7.7. Scrolls (`?') - Scrolls are labeled with various titles, probably chosen by - ancient wizards for their amusement value (for example "READ ME," or - "THANX MAUD" backwards). Scrolls disappear after you read them + Scrolls are labeled with various titles, probably chosen by + ancient wizards for their amusement value (for example "READ ME," or + "THANX MAUD" backwards). Scrolls disappear after you read them (except for blank ones, without magic spells on them). - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -3304,28 +3304,28 @@ - One of the most useful of these is the scroll of identify, which - can be used to determine what another object is, whether it is cursed - or blessed, and how many uses it has left. Some objects of subtle + One of the most useful of these is the scroll of identify, which + can be used to determine what another object is, whether it is cursed + or blessed, and how many uses it has left. Some objects of subtle enchantment are difficult to identify without these. - A scroll whose label is known can be read even when the hero is - blind. If a scroll has been discovered, it will be listed in inven- + A scroll whose label is known can be read even when the hero is + blind. If a scroll has been discovered, it will be listed in inven- tory by type rather than by label, but the label is known in that sit- uation even though it isn't shown. - Many scrolls produce a different effect from usual if they are + Many scrolls produce a different effect from usual if they are blessed or cursed, or read while the hero is confused. - A mail daemon may run up and deliver mail to you as a scroll of + A mail daemon may run up and deliver mail to you as a scroll of mail (on versions compiled with this feature). To use this feature on - versions where NetHack mail delivery is triggered by electronic mail - appearing in your system mailbox, you must let NetHack know where to - look for new mail by setting the "MAIL" environment variable to the - file name of your mailbox. You may also want to set the "MAILREADER" - environment variable to the file name of your favorite reader, so - NetHack can shell to it when you read the scroll. On versions of - NetHack where mail is randomly generated internal to the game, these + versions where NetHack mail delivery is triggered by electronic mail + appearing in your system mailbox, you must let NetHack know where to + look for new mail by setting the "MAIL" environment variable to the + file name of your mailbox. You may also want to set the "MAILREADER" + environment variable to the file name of your favorite reader, so + NetHack can shell to it when you read the scroll. On versions of + NetHack where mail is randomly generated internal to the game, these environment variables are ignored. You can disable the mail daemon by turning off the mail option. @@ -3333,13 +3333,13 @@ 7.8. Potions (`!') - Potions are distinguished by the color of the liquid inside the + Potions are distinguished by the color of the liquid inside the flask. They disappear after you quaff them. - Clear potions are potions of water. Sometimes these are blessed - or cursed, resulting in holy or unholy water. Holy water is the bane + Clear potions are potions of water. Sometimes these are blessed + or cursed, resulting in holy or unholy water. Holy water is the bane of the undead, so potions of holy water are good things to throw (`t') - at them. It is also sometimes very useful to dip ("#dip") an object + at them. It is also sometimes very useful to dip ("#dip") an object into a potion. The command to drink a potion is `q' (quaff). @@ -3347,20 +3347,20 @@ 7.9. Wands (`/') Wands usually have multiple magical charges. Some types of wands - require a direction in which to zap them. You can also zap them at - yourself (just give a `.' or `s' for the direction). Be warned, how- - ever, for this is often unwise. Other types of wands don't require a + require a direction in which to zap them. You can also zap them at + yourself (just give a `.' or `s' for the direction). Be warned, how- + ever, for this is often unwise. Other types of wands don't require a direction. The number of charges in a wand is random and decreases by one whenever you use it. - When the number of charges left in a wand becomes zero, attempts - to use the wand will usually result in nothing happening. Occasion- - ally, however, it may be possible to squeeze the last few mana points - from an otherwise spent wand, destroying it in the process. A wand - may be recharged by using suitable magic, but doing so runs the risk + When the number of charges left in a wand becomes zero, attempts + to use the wand will usually result in nothing happening. Occasion- + ally, however, it may be possible to squeeze the last few mana points + from an otherwise spent wand, destroying it in the process. A wand + may be recharged by using suitable magic, but doing so runs the risk - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -3373,60 +3373,60 @@ of causing it to explode. The chance for such an explosion starts out very small and increases each time the wand is recharged. - In a truly desperate situation, when your back is up against the - wall, you might decide to go for broke and break your wand. This is - not for the faint of heart. Doing so will almost certainly cause a + In a truly desperate situation, when your back is up against the + wall, you might decide to go for broke and break your wand. This is + not for the faint of heart. Doing so will almost certainly cause a catastrophic release of magical energies. - When you have fully identified a particular wand, inventory dis- + When you have fully identified a particular wand, inventory dis- play will include additional information in parentheses: the number of - times it has been recharged followed by a colon and then by its cur- - rent number of charges. A current charge count of -1 is a special + times it has been recharged followed by a colon and then by its cur- + rent number of charges. A current charge count of -1 is a special case indicating that the wand has been cancelled. - The command to use a wand is `z' (zap). To break one, use the + The command to use a wand is `z' (zap). To break one, use the `a' (apply) command. 7.10. Rings (`=') - Rings are very useful items, since they are relatively permanent - magic, unlike the usually fleeting effects of potions, scrolls, and + Rings are very useful items, since they are relatively permanent + magic, unlike the usually fleeting effects of potions, scrolls, and wands. - Putting on a ring activates its magic. You can wear at most two + Putting on a ring activates its magic. You can wear at most two rings at any time, one on the ring finger of each hand. - Most worn rings also cause you to grow hungry more rapidly, the + Most worn rings also cause you to grow hungry more rapidly, the rate varying with the type of ring. - When wearing gloves, rings are worn underneath. If the gloves - are cursed, rings cannot be put on and any already being worn cannot - be removed. When worn gloves aren't cursed, you don't have to manu- - ally take them off before putting on or removing a ring and then re- + When wearing gloves, rings are worn underneath. If the gloves + are cursed, rings cannot be put on and any already being worn cannot + be removed. When worn gloves aren't cursed, you don't have to manu- + ally take them off before putting on or removing a ring and then re- wear them after. That's done implicitly to avoid unnecessary tedium. - The commands to use rings are `P' (put on) and `R' (remove). + The commands to use rings are `P' (put on) and `R' (remove). `A', `W', and `T' can also be used; see Amulets. 7.11. Spellbooks (`+') - Spellbooks are tomes of mighty magic. When studied with the `r' - (read) command, they transfer to the reader the knowledge of a spell + Spellbooks are tomes of mighty magic. When studied with the `r' + (read) command, they transfer to the reader the knowledge of a spell (and therefore eventually become unreadable)--unless the attempt back- - fires. Reading a cursed spellbook or one with mystic runes beyond + fires. Reading a cursed spellbook or one with mystic runes beyond your ken can be harmful to your health! - A spell (even when learned) can also backfire when you cast it. + A spell (even when learned) can also backfire when you cast it. If you attempt to cast a spell well above your experience level, or if you have little skill with the appropriate spell type, or cast it at a - time when your luck is particularly bad, you can end up wasting both + time when your luck is particularly bad, you can end up wasting both the energy and the time required in casting. - Casting a spell calls forth magical energies and focuses them - with your naked mind. Some of the magical energy released comes from + Casting a spell calls forth magical energies and focuses them + with your naked mind. Some of the magical energy released comes from - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -3437,62 +3437,62 @@ within you. Casting temporarily drains your magical power, which will - slowly be recovered, and causes you to need additional food. Casting - of spells also requires practice. With practice, your skill in each + slowly be recovered, and causes you to need additional food. Casting + of spells also requires practice. With practice, your skill in each category of spell casting will improve. Over time, however, your mem- ory of each spell will dim, and you will need to relearn it. Some spells require a direction in which to cast them, similar to wands. To cast one at yourself, just give a `.' or `s' for the direc- - tion. A few spells require you to pick a target location rather than - just specify a particular direction. Other spells don't require any + tion. A few spells require you to pick a target location rather than + just specify a particular direction. Other spells don't require any direction or target. - Just as weapons are divided into groups in which a character can - become proficient (to varying degrees), spells are similarly grouped. - Successfully casting a spell exercises its skill group; using the - "#enhance" command to advance a sufficiently exercised skill will - affect all spells within the group. Advanced skill may increase the - potency of spells, reduce their risk of failure during casting + Just as weapons are divided into groups in which a character can + become proficient (to varying degrees), spells are similarly grouped. + Successfully casting a spell exercises its skill group; using the + "#enhance" command to advance a sufficiently exercised skill will + affect all spells within the group. Advanced skill may increase the + potency of spells, reduce their risk of failure during casting attempts, and improve the accuracy of the estimate for how much longer - they will be retained in your memory. Skill slots are shared with + they will be retained in your memory. Skill slots are shared with weapons skills. (See also the section on "Weapon proficiency".) Casting a spell also requires flexible movement, and wearing var- ious types of armor may interfere with that. - The command to read a spellbook is the same as for scrolls, `r' - (read). The `+' command lists each spell you know along with its + The command to read a spellbook is the same as for scrolls, `r' + (read). The `+' command lists each spell you know along with its level, skill category, chance of failure when casting, and an estimate - of how strongly it is remembered. The `Z' (cast) command casts a + of how strongly it is remembered. The `Z' (cast) command casts a spell. 7.12. Tools (`(') - Tools are miscellaneous objects with various purposes. Some - tools have a limited number of uses, akin to wand charges. For exam- - ple, lamps burn out after a while. Other tools are containers, which + Tools are miscellaneous objects with various purposes. Some + tools have a limited number of uses, akin to wand charges. For exam- + ple, lamps burn out after a while. Other tools are containers, which objects can be placed into or taken out of. - Some tools (such as a blindfold) can be worn and can be put on - and removed like other accessories (rings, amulets); see Amulets. - Other tools (such as pick-axe) can be wielded as weapons in addition - to being applied for their usual purpose, and in some cases (again, + Some tools (such as a blindfold) can be worn and can be put on + and removed like other accessories (rings, amulets); see Amulets. + Other tools (such as pick-axe) can be wielded as weapons in addition + to being applied for their usual purpose, and in some cases (again, pick-axe) become wielded as a weapon even when applied. - The blind option can be set (prior to game start) to attempt to - play the entire game without being able to see (a self-imposed chal- + The blind option can be set (prior to game start) to attempt to + play the entire game without being able to see (a self-imposed chal- lenge which is very difficult to accomplish). The command to use a tool is `a' (apply). 7.12.1. Containers - You may encounter bags, boxes, and chests in your travels. A + You may encounter bags, boxes, and chests in your travels. A tool of this sort can be opened with the "#loot" extended command when - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -3502,55 +3502,55 @@ - you are standing on top of it (that is, on the same floor spot), or - with the `a' (apply) command when you are carrying it. However, - chests are often locked, and are in any case unwieldy objects. You - must set one down before unlocking it by using a key or lock-picking - tool with the `a' (apply) command, by kicking it with the `^D' com- - mand, or by using a weapon to force the lock with the "#force" + you are standing on top of it (that is, on the same floor spot), or + with the `a' (apply) command when you are carrying it. However, + chests are often locked, and are in any case unwieldy objects. You + must set one down before unlocking it by using a key or lock-picking + tool with the `a' (apply) command, by kicking it with the `^D' com- + mand, or by using a weapon to force the lock with the "#force" extended command. - Some chests are trapped, causing nasty things to happen when you - unlock or open them. You can check for and try to deactivate traps + Some chests are trapped, causing nasty things to happen when you + unlock or open them. You can check for and try to deactivate traps with the "#untrap" extended command. - When the contents of a container are known, that container will - be described as something like "a sack containing 3 items". In this - example, the 3 refers to number of stacks of compatible items, not to - the total number of individual items. So a sack holding 2 sky blue - potions, 7 arrows, and 350 gold pieces would be described as having 3 - items rather than 10 or 359. And you would need to have 3 unused - inventory slots available in order to take everything out (for the - case where the items you remove don't combine into bigger stacks with + When the contents of a container are known, that container will + be described as something like "a sack containing 3 items". In this + example, the 3 refers to number of stacks of compatible items, not to + the total number of individual items. So a sack holding 2 sky blue + potions, 7 arrows, and 350 gold pieces would be described as having 3 + items rather than 10 or 359. And you would need to have 3 unused + inventory slots available in order to take everything out (for the + case where the items you remove don't combine into bigger stacks with things you're already carrying). If a chest or large box is described as "broken", that means that - it can't be locked rather than that it no longer functions as a con- + it can't be locked rather than that it no longer functions as a con- tainer. - The apply and loot commands allow you to take out and/or put in - an arbitrary number of items in a single operation. If you want to - take everything out of a container, you can use the "#tip" command to - pour the contents onto the floor. This may be your only way to get - things out if your hands are stuck to a cursed two-handed weapon. - When your hands aren't stuck, you have the potential to pour the con- - tents into another container. (As of this writing, the other con- + The apply and loot commands allow you to take out and/or put in + an arbitrary number of items in a single operation. If you want to + take everything out of a container, you can use the "#tip" command to + pour the contents onto the floor. This may be your only way to get + things out if your hands are stuck to a cursed two-handed weapon. + When your hands aren't stuck, you have the potential to pour the con- + tents into another container. (As of this writing, the other con- tainer must be carried rather than on the floor.) 7.13. Amulets (`"') Amulets are very similar to rings, and often more powerful. Like - rings, amulets have various magical properties, some beneficial, some + rings, amulets have various magical properties, some beneficial, some harmful, which are activated by putting them on. - Only one amulet may be worn at a time, around your neck. Like - wearing rings, wearing an amulet affects your metabolism, causing you + Only one amulet may be worn at a time, around your neck. Like + wearing rings, wearing an amulet affects your metabolism, causing you to grow hungry more rapidly. - The commands to use amulets are the same as for rings, `P' (put - on) and `R' (remove). `A' can be used to remove various worn items + The commands to use amulets are the same as for rings, `P' (put + on) and `R' (remove). `A' can be used to remove various worn items including amulets. Also, `W' (wear) and `T' (take off) which are nor- - mally for armor can be used for amulets and other accessories (rings + mally for armor can be used for amulets and other accessories (rings and eyewear), but accessories won't be shown as likely candidates in a prompt for choosing what to wear or take off. @@ -3558,7 +3558,7 @@ - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -3570,53 +3570,53 @@ 7.14. Gems (`*') - Some gems are valuable, and can be sold for a lot of gold. They - are also a far more efficient way of carrying your riches. Valuable + Some gems are valuable, and can be sold for a lot of gold. They + are also a far more efficient way of carrying your riches. Valuable gems increase your score if you bring them with you when you exit. Other small rocks are also categorized as gems, but they are much - less valuable. All rocks, however, can be used as projectile weapons - (if you have a sling). In the most desperate of cases, you can still + less valuable. All rocks, however, can be used as projectile weapons + (if you have a sling). In the most desperate of cases, you can still throw them by hand. 7.15. Large rocks (``') - Statues and boulders are not particularly useful, and are gener- + Statues and boulders are not particularly useful, and are gener- ally heavy. It is rumored that some statues are not what they seem. - Boulders occasionally block your path. You can push one forward + Boulders occasionally block your path. You can push one forward (by attempting to walk onto its spot) when nothing blocks its path, or - you can smash it into a pile of small rocks with breaking magic or a + you can smash it into a pile of small rocks with breaking magic or a pick-axe. It is possible to move onto a boulder's location if certain conditions are met; ordinarily one of those conditions is that pushing - it any further be blocked. Using the move-without-picking-up prefix - (default key `m') prior to the direction of movement will attempt to - move to a boulder's location without pushing it in addition to the + it any further be blocked. Using the move-without-picking-up prefix + (default key `m') prior to the direction of movement will attempt to + move to a boulder's location without pushing it in addition to the prefix's usual action of suppressing auto-pickup at the destination. - Very large humanoids (giants and their ilk) have been known to + Very large humanoids (giants and their ilk) have been known to pick up boulders and use them as missile weapons. - Unlike boulders, statues can't be pushed, but don't need to be - because they don't block movement. They can be smashed into rocks + Unlike boulders, statues can't be pushed, but don't need to be + because they don't block movement. They can be smashed into rocks though. - For some configurations of the program, statues are no longer - shown as ``' but by the letter representing the monster they depict + For some configurations of the program, statues are no longer + shown as ``' but by the letter representing the monster they depict instead. 7.16. Gold (`$') Gold adds to your score, and you can buy things in shops with it. - There are a number of monsters in the dungeon that may be influenced + There are a number of monsters in the dungeon that may be influenced by the amount of gold you are carrying (shopkeepers aside). - Gold pieces are the only type of object where bless/curse state - does not apply. They're always uncursed but never described as - uncursed even if you turn off the implicit_uncursed option. You can - set the goldX option if you prefer to have gold pieces be treated as - bless/curse state unknown rather than as known to be uncursed. Only - matters when you're using an object selection prompt that can filter + Gold pieces are the only type of object where bless/curse state + does not apply. They're always uncursed but never described as + uncursed even if you turn off the implicit_uncursed option. You can + set the goldX option if you prefer to have gold pieces be treated as + bless/curse state unknown rather than as known to be uncursed. Only + matters when you're using an object selection prompt that can filter by "BUCX" state. @@ -3624,7 +3624,7 @@ - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -3637,60 +3637,60 @@ 7.17. Persistence of Objects Normally, if you have seen an object at a particular map location - and move to another location where you can't directly see that object - any more, it will continue to be displayed on your map. That remains - the case even if it is not actually there any more--perhaps a monster + and move to another location where you can't directly see that object + any more, it will continue to be displayed on your map. That remains + the case even if it is not actually there any more--perhaps a monster has picked it up or it has rotted away--until you can see or feel that location again. One notable exception is that if the object gets cov- - ered by the "remembered, unseen monster" marker. When that marker is + ered by the "remembered, unseen monster" marker. When that marker is later removed after you've verified that no monster is there, you will - have forgotten that there was any object there regardless of whether - the unseen monster actually took the object. If the object is still - there, then once you see or feel that location again you will re-dis- + have forgotten that there was any object there regardless of whether + the unseen monster actually took the object. If the object is still + there, then once you see or feel that location again you will re-dis- cover the object and resume remembering it. The situation is the same for a pile of objects, except that only - the top item of the pile is displayed. The hilite_pile option can be + the top item of the pile is displayed. The hilite_pile option can be enabled in order to show an item differently when it is the top one of a pile. 8. Conduct - As if winning NetHack were not difficult enough, certain players - seek to challenge themselves by imposing restrictions on the way they - play the game. The game automatically tracks some of these chal- - lenges, which can be checked at any time with the #conduct command or - at the end of the game. When you perform an action which breaks a - challenge, it will no longer be listed. This gives players extra - "bragging rights" for winning the game with these challenges. Note - that it is perfectly acceptable to win the game without resorting to - these restrictions and that it is unusual for players to adhere to + As if winning NetHack were not difficult enough, certain players + seek to challenge themselves by imposing restrictions on the way they + play the game. The game automatically tracks some of these chal- + lenges, which can be checked at any time with the #conduct command or + at the end of the game. When you perform an action which breaks a + challenge, it will no longer be listed. This gives players extra + "bragging rights" for winning the game with these challenges. Note + that it is perfectly acceptable to win the game without resorting to + these restrictions and that it is unusual for players to adhere to challenges the first time they win the game. - Several of the challenges are related to eating behavior. The + Several of the challenges are related to eating behavior. The most difficult of these is the foodless challenge. Although creatures - can survive long periods of time without food, there is a physiologi- - cal need for water; thus there is no restriction on drinking bever- - ages, even if they provide some minor food benefits. Calling upon + can survive long periods of time without food, there is a physiologi- + cal need for water; thus there is no restriction on drinking bever- + ages, even if they provide some minor food benefits. Calling upon your god for help with starvation does not violate any food challenges either. - A strict vegan diet is one which avoids any food derived from - animals. The primary source of nutrition is fruits and vegetables. - The corpses and tins of blobs (`b'), jellies (`j'), and fungi (`F') - are also considered to be vegetable matter. Certain human food is + A strict vegan diet is one which avoids any food derived from + animals. The primary source of nutrition is fruits and vegetables. + The corpses and tins of blobs (`b'), jellies (`j'), and fungi (`F') + are also considered to be vegetable matter. Certain human food is prepared without animal products; namely, lembas wafers, cram rations, - food rations (gunyoki), K-rations, and C-rations. Metal or another + food rations (gunyoki), K-rations, and C-rations. Metal or another normally indigestible material eaten while polymorphed into a creature - that can digest it is also considered vegan food. Note however that + that can digest it is also considered vegan food. Note however that eating such items still counts against foodless conduct. - Vegetarians do not eat animals; however, they are less selective - about eating animal byproducts than vegans. In addition to the vegan - items listed above, they may eat any kind of pudding (`P') other than + Vegetarians do not eat animals; however, they are less selective + about eating animal byproducts than vegans. In addition to the vegan + items listed above, they may eat any kind of pudding (`P') other than - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -3700,63 +3700,63 @@ - the black puddings, eggs and food made from eggs (fortune cookies and - pancakes), food made with milk (cream pies and candy bars), and lumps + the black puddings, eggs and food made from eggs (fortune cookies and + pancakes), food made with milk (cream pies and candy bars), and lumps of royal jelly. Monks are expected to observe a vegetarian diet. Eating any kind of meat violates the vegetarian, vegan, and food- - less conducts. This includes tripe rations, the corpses or tins of + less conducts. This includes tripe rations, the corpses or tins of any monsters not mentioned above, and the various other chunks of meat - found in the dungeon. Swallowing and digesting a monster while poly- - morphed is treated as if you ate the creature's corpse. Eating - leather, dragon hide, or bone items while polymorphed into a creature - that can digest it, or eating monster brains while polymorphed into a - mind flayer, is considered eating an animal, although wax is only an + found in the dungeon. Swallowing and digesting a monster while poly- + morphed is treated as if you ate the creature's corpse. Eating + leather, dragon hide, or bone items while polymorphed into a creature + that can digest it, or eating monster brains while polymorphed into a + mind flayer, is considered eating an animal, although wax is only an animal byproduct. - Regardless of conduct, there will be some items which are indi- + Regardless of conduct, there will be some items which are indi- gestible, and others which are hazardous to eat. Using a swallow-and- - digest attack against a monster is equivalent to eating the monster's - corpse. Please note that the term "vegan" is used here only in the - context of diet. You are still free to choose not to use or wear - items derived from animals (e.g. leather, dragon hide, bone, horns, - coral), but the game will not keep track of this for you. Also note - that "milky" potions may be a translucent white, but they do not con- - tain milk, so they are compatible with a vegan diet. Slime molds or - player-defined "fruits", although they could be anything from "cher- + digest attack against a monster is equivalent to eating the monster's + corpse. Please note that the term "vegan" is used here only in the + context of diet. You are still free to choose not to use or wear + items derived from animals (e.g. leather, dragon hide, bone, horns, + coral), but the game will not keep track of this for you. Also note + that "milky" potions may be a translucent white, but they do not con- + tain milk, so they are compatible with a vegan diet. Slime molds or + player-defined "fruits", although they could be anything from "cher- ries" to "pork chops", are also assumed to be vegan. An atheist is one who rejects religion. This means that you can- not #pray, #offer sacrifices to any god, #turn undead, or #chat with a priest. Particularly selective readers may argue that playing Monk or - Priest characters should violate this conduct; that is a choice left + Priest characters should violate this conduct; that is a choice left to the player. Offering the Amulet of Yendor to your god is necessary to win the game and is not counted against this conduct. You are also - not penalized for being spoken to by an angry god, priest(ess), or + not penalized for being spoken to by an angry god, priest(ess), or other religious figure; a true atheist would hear the words but attach no special meaning to them. - Most players fight with a wielded weapon (or tool intended to be - wielded as a weapon). Another challenge is to win the game without - using such a wielded weapon. You are still permitted to throw, fire, - and kick weapons; use a wand, spell, or other type of item; or fight + Most players fight with a wielded weapon (or tool intended to be + wielded as a weapon). Another challenge is to win the game without + using such a wielded weapon. You are still permitted to throw, fire, + and kick weapons; use a wand, spell, or other type of item; or fight with your hands and feet. - In NetHack, a pacifist refuses to cause the death of any other - monster (i.e. if you would get experience for the death). This is a - particularly difficult challenge, although it is still possible to + In NetHack, a pacifist refuses to cause the death of any other + monster (i.e. if you would get experience for the death). This is a + particularly difficult challenge, although it is still possible to gain experience by other means. - An illiterate character does not read or write. This includes + An illiterate character does not read or write. This includes reading a scroll, spellbook, fortune cookie message, or t-shirt; writ- - ing a scroll; or making an engraving of anything other than a single - "X" (the traditional signature of an illiterate person). Reading an - engraving, or any item that is absolutely necessary to win the game, - is not counted against this conduct. The identity of scrolls and - spellbooks (and knowledge of spells) in your starting inventory is + ing a scroll; or making an engraving of anything other than a single + "X" (the traditional signature of an illiterate person). Reading an + engraving, or any item that is absolutely necessary to win the game, + is not counted against this conduct. The identity of scrolls and + spellbooks (and knowledge of spells) in your starting inventory is - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -3766,55 +3766,55 @@ - assumed to be learned from your teachers prior to the start of the + assumed to be learned from your teachers prior to the start of the game and isn't counted. - There is a side-branch to the main dungeon called "Sokoban," - briefly described in the earlier section about Traps. As mentioned - there, the goal is to push boulders into pits and/or holes to plug - those in order to both get the boulders out of the way and be able to - go past the traps. There are some special "rules" that are active - when in that branch of the dungeon. Some rules can't be bypassed, - such as being unable to push a boulder diagonally. Other rules can, + There is a side-branch to the main dungeon called "Sokoban," + briefly described in the earlier section about Traps. As mentioned + there, the goal is to push boulders into pits and/or holes to plug + those in order to both get the boulders out of the way and be able to + go past the traps. There are some special "rules" that are active + when in that branch of the dungeon. Some rules can't be bypassed, + such as being unable to push a boulder diagonally. Other rules can, such as not smashing boulders with magic or tools, but doing so causes - you to receive a luck penalty. No message about that is given at the + you to receive a luck penalty. No message about that is given at the time, but it is tracked as a conduct. The #conduct command and end of - game disclosure will report whether you have abided by the special - rules of Sokoban, and if not, how many times you violated them, pro- + game disclosure will report whether you have abided by the special + rules of Sokoban, and if not, how many times you violated them, pro- viding you with a way to discover which actions incur bad luck so that - you can be better informed about whether or not to avoid repeating + you can be better informed about whether or not to avoid repeating those actions in the future. (Note: the Sokoban conduct will only be displayed if you have entered the Sokoban branch of the dungeon during - the current game. Once that has happened, it becomes part of dis- - closed conduct even if you haven't done anything interesting there. - Ending the game with "never broke the Sokoban rules" conduct is most - meaningful if you also manage to perform the "obtained the Sokoban + the current game. Once that has happened, it becomes part of dis- + closed conduct even if you haven't done anything interesting there. + Ending the game with "never broke the Sokoban rules" conduct is most + meaningful if you also manage to perform the "obtained the Sokoban prize" achievement (see Achievements below).) - There are several other challenges tracked by the game. It is - possible to eliminate one or more species of monsters by genocide; + There are several other challenges tracked by the game. It is + possible to eliminate one or more species of monsters by genocide; playing without this feature is considered a challenge. When the game - offers you an opportunity to genocide monsters, you may respond with - the monster type "none" if you want to decline. You can change the - form of an item into another item of the same type ("polypiling") or - the form of your own body into another creature ("polyself") by wand, + offers you an opportunity to genocide monsters, you may respond with + the monster type "none" if you want to decline. You can change the + form of an item into another item of the same type ("polypiling") or + the form of your own body into another creature ("polyself") by wand, spell, or potion of polymorph; avoiding these effects are each consid- - ered challenges. Polymorphing monsters, including pets, does not - break either of these challenges. Finally, you may sometimes receive - wishes; a game without an attempt to wish for any items is a chal- + ered challenges. Polymorphing monsters, including pets, does not + break either of these challenges. Finally, you may sometimes receive + wishes; a game without an attempt to wish for any items is a chal- lenge, as is a game without wishing for an artifact (even if the arti- fact immediately disappears). When the game offers you an opportunity - to make a wish for an item, you may choose "nothing" if you want to + to make a wish for an item, you may choose "nothing" if you want to decline. 8.1. Achievements - End of game disclosure will also display various achievements - representing progress toward ultimate ascension, if any have been - attained. They aren't directly related to conduct but are grouped - with it because they fall into the same category of "bragging rights" - and to limit the number of questions during disclosure. Listed here - roughly in order of difficulty and not necessarily in the order in + End of game disclosure will also display various achievements + representing progress toward ultimate ascension, if any have been + attained. They aren't directly related to conduct but are grouped + with it because they fall into the same category of "bragging rights" + and to limit the number of questions during disclosure. Listed here + roughly in order of difficulty and not necessarily in the order in which you might accomplish them. Rank - Attained rank title Rank. @@ -3822,7 +3822,7 @@ - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -3839,12 +3839,12 @@ Novel - Read a passage from a Discworld Novel. Sokoban - Entered Sokoban. Big Room - Entered the Big Room. - Soko-Prize - Explored to the top of Sokoban and found a + Soko-Prize - Explored to the top of Sokoban and found a special item there. - Mines' End - Explored to the bottom of the Gnomish Mines + Mines' End - Explored to the bottom of the Gnomish Mines and found a special item there. Medusa - Defeated Medusa. - Tune - Discovered the tune that can be used to open + Tune - Discovered the tune that can be used to open and close the drawbridge on the Castle level. Bell - Acquired the Bell of Opening. Gehennom - Entered Gehennom. @@ -3864,31 +3864,31 @@ Notes: - Achievements are recorded and subsequently reported in the order - in which they happen during your current game rather than the order + Achievements are recorded and subsequently reported in the order + in which they happen during your current game rather than the order listed here. - There are nine titles for each role, bestowed at experi- - ence levels 1, 3, 6, 10, 14, 18, 22, 26, and 30. The one for experi- - ence level 1 is not recorded as an achievement. Losing enough levels + There are nine titles for each role, bestowed at experi- + ence levels 1, 3, 6, 10, 14, 18, 22, 26, and 30. The one for experi- + ence level 1 is not recorded as an achievement. Losing enough levels to revert to lower rank(s) does not discard the corresponding achieve- ment(s). - There's no guaranteed Novel so the achievement to read one might - not always be attainable (except perhaps by wishing). Similarly, the - Big Room level is not always present. Unlike with the Novel, there's + There's no guaranteed Novel so the achievement to read one might + not always be attainable (except perhaps by wishing). Similarly, the + Big Room level is not always present. Unlike with the Novel, there's no way to wish for this opportunity. - The "special items" hidden in Mines' End and Sokoban are not - unique but are considered to be prizes or rewards for exploring those - levels since doing so is not necessary to complete the game. Finding - other instances of the same objects doesn't record the corresponding + The "special items" hidden in Mines' End and Sokoban are not + unique but are considered to be prizes or rewards for exploring those + levels since doing so is not necessary to complete the game. Finding + other instances of the same objects doesn't record the corresponding achievement. - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -3898,63 +3898,63 @@ - The Medusa achievement is recorded if she dies for any reason, + The Medusa achievement is recorded if she dies for any reason, even if you are not directly responsible, and only if she dies. The 5-note tune can be learned via trial and error with a musical - instrument played closely enough--but not too close!--to the Castle + instrument played closely enough--but not too close!--to the Castle level's drawbridge or can be given to you via prayer boon. - Blind, Deaf, Nudist, and Pauper are also conducts, and they can - only be enabled by setting the correspondingly named option in + Blind, Deaf, Nudist, and Pauper are also conducts, and they can + only be enabled by setting the correspondingly named option in NETHACKOPTIONS or run-time configuration file prior to game start. In - the case of Blind and Deaf, the option also enforces the conduct. - They aren't really significant accomplishments unless/until you make + the case of Blind and Deaf, the option also enforces the conduct. + They aren't really significant accomplishments unless/until you make substantial progress into the dungeon. 9. Options - Due to variations in personal tastes and conceptions of how - NetHack should do things, there are options you can set to change how + Due to variations in personal tastes and conceptions of how + NetHack should do things, there are options you can set to change how NetHack behaves. 9.1. Setting the options Options may be set in a number of ways. Within the game, the `O' - command allows you to view all options and change most of them. You - can also set options automatically by placing them in a configuration + command allows you to view all options and change most of them. You + can also set options automatically by placing them in a configuration file, or in the NETHACKOPTIONS environment variable. Some versions of - NetHack also have front-end programs that allow you to set options + NetHack also have front-end programs that allow you to set options before starting the game or a global configuration for system adminis- trators. 9.2. Using a configuration file - The default name of the configuration file varies on different + The default name of the configuration file varies on different operating systems. - On UNIX, Linux, and macOS it is ".nethackrc" in the user's home + On UNIX, Linux, and macOS it is ".nethackrc" in the user's home directory. The file may not exist, but it is a normal ASCII text file and can be created with any text editor. - On Windows, the name is ".nethackrc" located in the folder - "%USERPROFILE%\NetHack\". The file may not exist, but it is a normal - ASCII text file can can be created with any text editor. After run- - ning NetHack for the first time, you should find a default template - for the configuration file named ".nethackrc.template" in - "%USERPROFILE%\NetHack\". If you have not created the configuration + On Windows, the name is ".nethackrc" located in the folder + "%USERPROFILE%\NetHack\". The file may not exist, but it is a normal + ASCII text file can can be created with any text editor. After run- + ning NetHack for the first time, you should find a default template + for the configuration file named ".nethackrc.template" in + "%USERPROFILE%\NetHack\". If you have not created the configuration file, NetHack will create one for you using the default template file. On MS-DOS, it is "defaults.nh" in the same folder as nethack.exe. - Any line in the configuration file starting with `#' is treated + Any line in the configuration file starting with `#' is treated as a comment and ignored. Empty lines are ignored. Any line beginning with `[' and ending in `]' is a section marker - (the closing `]' can be followed by whitespace and then an arbitrary + (the closing `]' can be followed by whitespace and then an arbitrary - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -3964,29 +3964,29 @@ - comment beginning with `#'). The text between the square brackets is - the section name. Section markers are only valid after a CHOOSE + comment beginning with `#'). The text between the square brackets is + the section name. Section markers are only valid after a CHOOSE directive and their names are case-insensitive. Lines after a section - marker belong to that section up until another section starts or a - marker without a name is encountered or the file ends. Lines within - sections are ignored unless a CHOOSE directive has selected that sec- + marker belong to that section up until another section starts or a + marker without a name is encountered or the file ends. Lines within + sections are ignored unless a CHOOSE directive has selected that sec- tion. - You can use different configuration directives in the file, some - of which can be used multiple times. In general, the directives are - written in capital letters, followed by an equals sign, followed by + You can use different configuration directives in the file, some + of which can be used multiple times. In general, the directives are + written in capital letters, followed by an equals sign, followed by settings particular to that directive. Here is a list of allowed directives: OPTIONS There are two types of options, boolean and compound options. Bool- - ean options toggle a setting on or off, while compound options take - more diverse values. Prefix a boolean option with "no" or `!' to - turn it off. For compound options, the option name and value are - separated by a colon. Some options are persistent, and apply only + ean options toggle a setting on or off, while compound options take + more diverse values. Prefix a boolean option with "no" or `!' to + turn it off. For compound options, the option name and value are + separated by a colon. Some options are persistent, and apply only to new games. You can specify multiple OPTIONS directives, and mul- - tiple options separated by commas in a single OPTIONS directive. + tiple options separated by commas in a single OPTIONS directive. (Comma separated options are processed from right to left.) Example: @@ -3996,15 +3996,15 @@ HACKDIR Default location of files NetHack needs. On Windows HACKDIR defaults - to the location of the NetHack.exe or NetHackw.exe file so setting + to the location of the NetHack.exe or NetHackw.exe file so setting HACKDIR to override that is not usually necessary or recommended. LEVELDIR - The location that in-progress level files are stored. Defaults to + The location that in-progress level files are stored. Defaults to HACKDIR, must be writable. SAVEDIR - The location where saved games are kept. Defaults to HACKDIR, must + The location where saved games are kept. Defaults to HACKDIR, must be writable. BONESDIR @@ -4016,11 +4016,11 @@ HACKDIR, must be writable. TROUBLEDIR - The location that a record of game aborts and self-diagnosed game + The location that a record of game aborts and self-diagnosed game problems is kept. Defaults to HACKDIR, must be writable. - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -4031,9 +4031,9 @@ AUTOCOMPLETE - Enable or disable an extended command autocompletion. Autocomple- + Enable or disable an extended command autocompletion. Autocomple- tion has no effect for the X11 windowport. You can specify multiple - autocompletions. To enable autocompletion, list the extended com- + autocompletions. To enable autocompletion, list the extended com- mand. Prefix the command with "!" to disable the autocompletion for that command. @@ -4042,13 +4042,13 @@ AUTOCOMPLETE=zap,!annotate AUTOPICKUP_EXCEPTION - Set exceptions to the pickup_types option. See the "Configuring + Set exceptions to the pickup_types option. See the "Configuring Autopickup Exceptions" section. BINDINGS - Change the key bindings of some special keys, menu accelerators, + Change the key bindings of some special keys, menu accelerators, extended commands, or mouse buttons. You can specify multiple bind- - ings. Format is key followed by the command, separated by a colon. + ings. Format is key followed by the command, separated by a colon. See the "Changing Key Bindings" section for more information. Example: @@ -4071,22 +4071,22 @@ OPTIONS=!rest_on_space If [] is present, the preceding section is closed and no new section - begins; whatever follows will be common to all sections. Otherwise + begins; whatever follows will be common to all sections. Otherwise the last section extends to the end of the options file. MENUCOLOR - Highlight menu lines with different colors. See the "Configuring + Highlight menu lines with different colors. See the "Configuring Menu Colors" section. MSGTYPE - Change the way messages are shown in the top status line. See the + Change the way messages are shown in the top status line. See the "Configuring Message Types" section. ROGUESYMBOLS Custom symbols for the rogue level's symbol set. See SYMBOLS below. - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -4100,12 +4100,12 @@ Define a sound mapping. See the "Configuring User Sounds" section. SOUNDDIR - Define the directory that contains the sound files. See the "Con- + Define the directory that contains the sound files. See the "Con- figuring User Sounds" section. SYMBOLS - Override one or more symbols in the symbol set used for all dungeon - levels except for the special rogue level. See the "Modifying + Override one or more symbols in the symbol set used for all dungeon + levels except for the special rogue level. See the "Modifying NetHack Symbols" section. Example: @@ -4115,9 +4115,10 @@ WIZKIT Debug mode only: extra items to add to initial inventory. Value is - the name of a text file containing a list of item names, one per - line, up to a maximum of 128 lines. Each line is processed by the - function that handles wishing. + the name of a text file containing a list of item names, one per + line, up to a maximum of 128 lines. Each line is processed by the + function that handles wishing. Entries are added to the wish his- + tory; see the wizwish-command. Example: @@ -4146,13 +4147,12 @@ - 9.3. Using the NETHACKOPTIONS environment variable - - The NETHACKOPTIONS variable is a comma-separated list of initial - values for the various options. Some can only be turned on or off. - NetHack 3.7.0 March 21, 2026 + + + + NetHack 3.7.0 March 25, 2026 @@ -4162,6 +4162,10 @@ + 9.3. Using the NETHACKOPTIONS environment variable + + The NETHACKOPTIONS variable is a comma-separated list of initial + values for the various options. Some can only be turned on or off. You turn one of these on by adding the name of the option to the list, and turn it off by typing a `!' or "no" before the name. Others take a character string as a value. You can set string options by typing @@ -4213,12 +4217,8 @@ that this has nothing to do with your computer's audio capabilities. Persistent. - alignment - Your starting alignment (align:lawful, align:neutral, or - align:chaotic). You may specify just the first letter. Many roles - - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -4228,6 +4228,9 @@ + alignment + Your starting alignment (align:lawful, align:neutral, or + align:chaotic). You may specify just the first letter. Many roles and the non-human races restrict which alignments are allowed. See role for a description of how to use negation to exclude choices. @@ -4279,12 +4282,9 @@ if you decline, your character will forget that the door or box is trapped; Apply-Key - if carrying a key or other unlocking tool, prompt about - using it; - Kick - kick the door (if you omit untrap or decline to attempt - untrap and you omit apply-key or you lack a key or you - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -4294,6 +4294,9 @@ + using it; + Kick - kick the door (if you omit untrap or decline to attempt + untrap and you omit apply-key or you lack a key or you decline to use the key; has no effect on containers); Force - try to force a container's lid with your currently wielded weapon (if you omit untrap or decline to attempt @@ -4346,11 +4349,8 @@ dark_room Show out-of-sight areas of lit rooms (default on). Persistent. - deaf - Start the character permanently deaf (default false). Persistent. - - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -4360,6 +4360,9 @@ + deaf + Start the character permanently deaf (default false). Persistent. + dropped_nopick If this option is on, items you dropped will not be automatically picked up, even if autopickup is also on and they are in @@ -4411,12 +4414,9 @@ Note that the vanquished monsters list includes all monsters killed by traps and each other as well as by you. And the dungeon overview - shows all levels you had visited but does not reveal things about - them that you hadn't discovered. - - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -4426,6 +4426,9 @@ + shows all levels you had visited but does not reveal things about + them that you hadn't discovered. + dogname Name your starting dog (for example "dogname:Fang"). Cannot be set with the `O' command. @@ -4477,12 +4480,9 @@ If gender is not specified, there is no default value; player will be prompted unless role and/or race forces a choice for gender. - Cannot be set with the `O' command. Persistent. - - - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -4492,6 +4492,8 @@ + Cannot be set with the `O' command. Persistent. + goldX When filtering objects based on bless/curse state (BUCX), whether to treat gold pieces as X (unknown bless/curse state, when "on") or U @@ -4544,11 +4546,9 @@ also be used if the current hitpoint value is at or below the criti- cal HP threshold. - The "Qt" interface also supports hitpointbar, by drawing a solid bar - above the name and title with a hard-coded color scheme. (As of - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -4558,6 +4558,8 @@ + The "Qt" interface also supports hitpointbar, by drawing a solid bar + above the name and title with a hard-coded color scheme. (As of this writing, having the bar enabled unintentionally inhibits resiz- ing the status panel. To resize that, use the #optionsfull command to toggle the hitpointbar option off, perform the resize while it's @@ -4610,11 +4612,9 @@ Give feedback when walking against a wall (default off). Persis- tent. - menucolors - Enable coloring menu lines (default off). See "Configuring Menu - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -4624,6 +4624,8 @@ + menucolors + Enable coloring menu lines (default off). See "Configuring Menu Colors" on how to configure the colors. menustyle @@ -4676,11 +4678,9 @@ Inventory and other object menus are normally separated by object class (weapons, armor, and so forth), with a menu header line at the beginning of each group. You can have menus add the display symbol - for the class of objects for each header line. You can also add the - display symbol for the individual item in each menu entry. For a - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -4690,6 +4690,8 @@ + for the class of objects for each header line. You can also add the + display symbol for the individual item in each menu entry. For a tiles map, that would be a small rendition of an object's tile. For a text map, it is the same character as is used for the object's class, which would be most useful when there are no headers separat- @@ -4742,11 +4744,9 @@ monpolycontrol Prompt for new form whenever any monster changes shape (default - off). Debug mode only. - - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -4756,6 +4756,8 @@ + off). Debug mode only. + montelecontrol Prompt for destination whenever any monster gets teleported (default off). Debug mode only. @@ -4808,11 +4810,9 @@ nudist Start the character with no armor (default false). Persistent. - null - Send padding nulls to the terminal (default on). Persistent. - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -4822,6 +4822,9 @@ + null + Send padding nulls to the terminal (default on). Persistent. + number_pad Use digit keys instead of letters to move (default 0 or off). Valid settings are: @@ -4873,12 +4876,9 @@ Were-change - require "yes" rather than `y' to confirm changing form due to lycanthropy when hero has polymorph control; pray - require `y' to confirm an attempt to pray rather than - immediately praying; on by default; (to require "yes" - rather than just `y', set Confirm too); - trap - require `y' to confirm an attempt to move into or onto - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -4888,6 +4888,9 @@ + immediately praying; on by default; (to require "yes" + rather than just `y', set Confirm too); + trap - require `y' to confirm an attempt to move into or onto a known trap, unless doing so is considered to be harmless; when enabled, this confirmation is also used for moving into visible gas cloud regions; (to require @@ -4939,12 +4942,9 @@ none - behave as if perm_invent is false; all - show all inventory except for gold; - full - show full inventory including gold; - in-use - only show items which are in use (worn, wielded, lit lamp). - - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -4954,6 +4954,9 @@ + full - show full inventory including gold; + in-use - only show items which are in use (worn, wielded, lit lamp). + Default is none but if perm_invent gets set to true while it is none it will be changed to all. @@ -5005,12 +5008,9 @@ empty value reverts to "all".) If you want to avoid automatically picking up any types of items but do want to have autopickup on in order to have autopickup_exception settings control what you do and - don't pick up, you can set pickup_types to `.'. That is the type - symbol for venom and you won't come across any venom items so won't - unintentionally pick such up. - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -5020,6 +5020,10 @@ + don't pick up, you can set pickup_types to `.'. That is the type + symbol for venom and you won't come across any venom items so won't + unintentionally pick such up. + pile_limit When walking across a pile of objects on the floor, threshold at which the message "there are few/several/many objects here" is given @@ -5070,13 +5074,9 @@ human races are allowed. See role for a description of how to use negation to exclude choices. - If race is not specified, there is no default value; player will be - prompted unless role forces a choice for race. Cannot be set with - the `O' command. Persistent. - - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -5086,6 +5086,10 @@ + If race is not specified, there is no default value; player will be + prompted unless role forces a choice for race. Cannot be set with + the `O' command. Persistent. + reroll Allows rerolling your character's starting inventory and attributes (default false). Persistent. @@ -5136,13 +5140,9 @@ Controls the amount of screen updating for the map window when engaged in multi-turn movement (running via shift+direction or con- trol+direction and so forth, or via the travel command or mouse - click). The possible values are: - - teleport - update the map after movement has finished; - run - update the map after every seven or so steps; - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -5152,6 +5152,10 @@ + click). The possible values are: + + teleport - update the map after movement has finished; + run - update the map after every seven or so steps; walk - update the map after each step; crawl - like walk, but pause briefly after each step. @@ -5202,13 +5206,9 @@ Include the game's version number on the status lines (default off). Potentially useful if you switch between different versions or vari- ants, or you are making screenshots or streaming video. Using the - statuslines:3 option is recommended so that there will be more room - available for status information, unless you're using NetHack's Qt - interface or your terminal emulator window displays fewer than 25 - lines. Persistent. - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -5218,6 +5218,11 @@ + statuslines:3 option is recommended so that there will be more room + available for status information, unless you're using NetHack's Qt + interface or your terminal emulator window displays fewer than 25 + lines. Persistent. + silent Suppress terminal beeps (default on). Persistent. @@ -5267,14 +5272,9 @@ index; a - order alphabetically, first any unique monsters then all the others; - c - order by monster class, by low to high level within each class; - n - order by count, high to low; ties are broken by internal monster - index; - z - order by count, low to high; ties broken by internal index. - - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -5284,6 +5284,11 @@ + c - order by monster class, by low to high level within each class; + n - order by count, high to low; ties are broken by internal monster + index; + z - order by count, low to high; ties broken by internal index. + Can be interactively set via the `m O' command or via using the `m' prefix before either the #vanquished command or the #genocided com- mand. @@ -5332,15 +5337,10 @@ "X11" interface always uses a timer-based delay. The default is on if configured into the program.) Persistent. - tips - Show some helpful tips during gameplay (default on). Persistent. - - tombstone - Draw a tombstone graphic upon your death (default on). Persistent. - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -5350,6 +5350,12 @@ + tips + Show some helpful tips during gameplay (default on). Persistent. + + tombstone + Draw a tombstone graphic upon your death (default on). Persistent. + toptenwin Put the ending display in a NetHack window instead of on stdout (default off). Setting this option makes the score list visible @@ -5398,15 +5404,9 @@ The area-filter tries to be slightly predictive--if you're standing on a doorway, it will consider the area on the side of the door you - were last moving towards. - - Filtering can also be changed when getting a location with the "get- - pos.filter" key. - - - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -5416,6 +5416,11 @@ + were last moving towards. + + Filtering can also be changed when getting a location with the "get- + pos.filter" key. + whatis_menu When getting a location on the map, and using a key to cycle through next and previous targets, use a menu instead to pick a target. @@ -5466,13 +5471,8 @@ Where to align or place the message window (top, bottom, left, or right) - align_status - Where to align or place the status window (top, bottom, left, or - right). - - - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -5482,6 +5482,10 @@ + align_status + Where to align or place the status window (top, bottom, left, or + right). + ascii_map If NetHack can, it should display the map using simple characters (letters and punctuation) rather than tiles graphics. In some @@ -5531,14 +5535,10 @@ font_size_status If NetHack can, it should use this size font for the status window. - font_size_text - If NetHack can, it should use this size font for text windows. - - fullscreen - If NetHack can, it should try to display on the entire screen rather - NetHack 3.7.0 March 21, 2026 + + NetHack 3.7.0 March 25, 2026 @@ -5548,6 +5548,11 @@ + font_size_text + If NetHack can, it should use this size font for text windows. + + fullscreen + If NetHack can, it should try to display on the entire screen rather than in a window. guicolor @@ -5596,15 +5601,10 @@ If NetHack can, it should display an opening splash screen when it starts up (default yes). - statuslines - Number of lines for traditional below-the-map status display. - Acceptable values are 2 and 3 (default is 2). - - When set to 3, the tty interface moves some fields around and mainly - shows status conditions on their own line. A display capable of - NetHack 3.7.0 March 21, 2026 + + NetHack 3.7.0 March 25, 2026 @@ -5614,6 +5614,12 @@ + statuslines + Number of lines for traditional below-the-map status display. + Acceptable values are 2 and 3 (default is 2). + + When set to 3, the tty interface moves some fields around and mainly + shows status conditions on their own line. A display capable of showing at least 25 lines is recommended. The value can be toggled back and forth during the game with the `O' command. @@ -5661,16 +5667,10 @@ use_darkgray Use bold black instead of blue for black glyphs (TTY only). - use_inverse - If NetHack can, it should display inverse when the game specifies - it. - - vary_msgcount - If NetHack can, it should display this number of messages at a time - in the message window. - NetHack 3.7.0 March 21, 2026 + + NetHack 3.7.0 March 25, 2026 @@ -5680,6 +5680,14 @@ + use_inverse + If NetHack can, it should display inverse when the game specifies + it. + + vary_msgcount + If NetHack can, it should display this number of messages at a time + in the message window. + windowborders Whether to draw boxes around the map, status area, message area, and persistent inventory window if enabled. Curses interface only. @@ -5726,17 +5734,9 @@ If NetHack can, it should wrap long lines of text if they don't fit in the visible area of the window. - 9.6. Crash Report Options - - Please note that NetHack does not send any information off your - computer unless you manually click submit on a form. - - OPTION=crash_email:email_address - - - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -5746,6 +5746,13 @@ + 9.6. Crash Report Options + + Please note that NetHack does not send any information off your + computer unless you manually click submit on a form. + + OPTION=crash_email:email_address + OPTION=crash_name:your_name These options are used only to save you some typing on the crash report and #bugreport forms. @@ -5793,16 +5800,9 @@ subkeyvalue (Win32 tty NetHack only). May be used to alter the value of key- strokes that the operating system returns to NetHack to help compen- - sate for international keyboard issues. OPTIONS=subkeyvalue:171/92 - will return 92 to NetHack, if 171 was originally going to be - returned. You can use multiple subkeyvalue assignments in the con- - figuration file if needed. Cannot be set with the `O' command. - - video - Set the video mode used (PC NetHack only). Values are "autodetect", - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -5812,6 +5812,13 @@ + sate for international keyboard issues. OPTIONS=subkeyvalue:171/92 + will return 92 to NetHack, if 171 was originally going to be + returned. You can use multiple subkeyvalue assignments in the con- + figuration file if needed. Cannot be set with the `O' command. + + video + Set the video mode used (PC NetHack only). Values are "autodetect", "default", "vga", or "vesa". Setting "vesa" will cause the game to display tiles, using the full capability of the VGA hardware. Set- ting "vga" will cause the game to display tiles, fixed at 640x480 in @@ -5858,17 +5865,10 @@ you can define patterns to be checked when the game is about to autopickup something. - autopickup_exception - Sets an exception to the pickup_types option. The autopickup_excep- - tion option should be followed by a regular expression to be used as - a pattern to match against the singular form of the description of - an object at your location. - - In addition, some characters are treated specially if they occur as - the first character in the pattern, specifically: - NetHack 3.7.0 March 21, 2026 + + NetHack 3.7.0 March 25, 2026 @@ -5878,6 +5878,15 @@ + autopickup_exception + Sets an exception to the pickup_types option. The autopickup_excep- + tion option should be followed by a regular expression to be used as + a pattern to match against the singular form of the description of + an object at your location. + + In addition, some characters are treated specially if they occur as + the first character in the pattern, specifically: + < - always pickup an object that matches rest of pattern; > - never pickup an object that matches rest of pattern. @@ -5923,18 +5932,9 @@ Menu accelerator keys The menu control or accelerator keys can also be rebound via OPTIONS - lines in the configuration file. You cannot bind object symbols or - selection letters into menu accelerators. Some interfaces only sup- - port some of the menu accelerators. - - Mouse buttons - You can bind "mouse1" or "mouse2" to "nothing", "therecmdmenu", - "clicklook", or "mouseaction". - - - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -5944,6 +5944,14 @@ + lines in the configuration file. You cannot bind object symbols or + selection letters into menu accelerators. Some interfaces only sup- + port some of the menu accelerators. + + Mouse buttons + You can bind "mouse1" or "mouse2" to "nothing", "therecmdmenu", + "clicklook", or "mouseaction". + Special command keys Below are the special commands you can rebind. Some of them can be bound to same keys with no problems, others are in the same "con- @@ -5990,17 +5998,9 @@ When asked for a location, the key to go to next closest door or doorway. Default is `d'. - getpos.door.prev - When asked for a location, the key to go to previous closest door or - doorway. Default is `D'. - - getpos.help - When asked for a location, the key to show help. Default is `?'. - - - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -6010,6 +6010,13 @@ + getpos.door.prev + When asked for a location, the key to go to previous closest door or + doorway. Default is `D'. + + getpos.help + When asked for a location, the key to show help. Default is `?'. + getpos.mon.next When asked for a location, the key to go to next closest monster. Default is `m'. @@ -6057,16 +6064,9 @@ getpos.pick.quick When asked for a location, the key to choose the location, skip ask- ing for more info, and exit the location asking loop. Default is - `;'. - - getpos.pick.verbose - When asked for a location, the key to choose the location, and show - more info without asking. Default is `:'. - - - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -6076,6 +6076,12 @@ + `;'. + + getpos.pick.verbose + When asked for a location, the key to choose the location, and show + more info without asking. Default is `:'. + getpos.self When asked for a location, the key to go to your location. Default is `@'. @@ -6124,15 +6130,9 @@ Here's an example of message types using NetHack's internal pattern matching facility: - MSGTYPE=stop "You feel hungry." - MSGTYPE=hide "You displaced *." - - specifies that whenever a message "You feel hungry" is shown, the - user is prompted with more-prompt, and a message matching "You dis- - placed ." is not shown at all. - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -6142,6 +6142,13 @@ + MSGTYPE=stop "You feel hungry." + MSGTYPE=hide "You displaced *." + + specifies that whenever a message "You feel hungry" is shown, the + user is prompted with more-prompt, and a message matching "You dis- + placed ." is not shown at all. + The order of the defined MSGTYPE lines is important; the last match- ing rule is used. Put the general case first, exceptions below them. @@ -6188,17 +6195,10 @@ ple MENUCOLOR entries in your configuration file, and the last MENU- COLOR line that matches a menu line will be used for the line. - Note that if you intend to have one or more color specifications - match " uncursed ", you will probably want to turn the - implicit_uncursed option off so that all items known to be uncursed - are actually displayed with the "uncursed" description. - - - - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -6208,6 +6208,11 @@ + Note that if you intend to have one or more color specifications + match " uncursed ", you will probably want to turn the + implicit_uncursed option off so that all items known to be uncursed + are actually displayed with the "uncursed" description. + 9.13. Configuring User Sounds Some platforms allow you to define sound files to be played when @@ -6257,14 +6262,9 @@ For example, the following line in your configuration file will cause the hitpoints field to display in the color red if your hit- - points drop to or below a threshold of 30%: - - OPTION=hilite_status:hitpoints/<=30%/red/normal - - (That example is actually specifying red&normal for <=30% and no- - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -6274,6 +6274,11 @@ + points drop to or below a threshold of 30%: + + OPTION=hilite_status:hitpoints/<=30%/red/normal + + (That example is actually specifying red&normal for <=30% and no- color&normal for >30%.) For another example, the following line in your configuration @@ -6322,15 +6327,10 @@ "experience", "time", and "score" are conditionally displayed depending upon your other option settings. - Instead of a behavior, "condition" takes the following condition - flags: stone, slime, strngl, foodpois, termill, blind, deaf, stun, - conf, hallu, lev, fly, and ride. You can use "major_troubles" as an - alias for stone through termill, "minor_troubles" for blind through - hallu, "movement" for lev, fly, and ride, and "all" for every condi- - tion. - NetHack 3.7.0 March 21, 2026 + + NetHack 3.7.0 March 25, 2026 @@ -6340,6 +6340,13 @@ + Instead of a behavior, "condition" takes the following condition + flags: stone, slime, strngl, foodpois, termill, blind, deaf, stun, + conf, hallu, lev, fly, and ride. You can use "major_troubles" as an + alias for stone through termill, "minor_troubles" for blind through + hallu, "movement" for lev, fly, and ride, and "all" for every condi- + tion. + Allowed behaviors are "always", "up", "down", "changed", a percent- age or absolute number threshold, or text to match against. For the hitpoints field, the additional behavior "criticalhp" is available. @@ -6387,16 +6394,9 @@ instead, it also matches when value is below or above. If the prefix is `<' or `>', only match when strictly above or below. - * criticalhp only applies to the hitpoints field and only when - current hit points are below a threshold (which varies by maxi- - mum hit points and experience level). When the threshold is - met, a criticalhp rule takes precedence over all other hit- - points rules. - - - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -6406,6 +6406,12 @@ + * criticalhp only applies to the hitpoints field and only when + current hit points are below a threshold (which varies by maxi- + mum hit points and experience level). When the threshold is + met, a criticalhp rule takes precedence over all other hit- + points rules. + * text match sets the attribute when the field value matches the text. Text matches can only be used for "alignment", "carry- ing-capacity", "hunger", "dungeon-level", and "title". For @@ -6454,15 +6460,9 @@ following character literally. Thus \ needs to be represented as \\. The special prefix form \m switches on the meta bit in the symbol value, and the ^ prefix causes the following character to be treated - as a control character. - - NetHack Symbols - Symbol Name Description - ----------------------------------------------------------------- - - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -6472,6 +6472,11 @@ + as a control character. + + NetHack Symbols + Symbol Name Description + ----------------------------------------------------------------- S_air (air) _ S_altar (altar) " S_amulet (amulet) @@ -6520,15 +6525,10 @@ \ S_expl_tr (explosion top right) | S_expl_ml (explosion middle left) S_expl_mc (explosion middle center) - | S_expl_mr (explosion middle right) - \ S_expl_bl (explosion bottom left) - - S_expl_bc (explosion bottom center) - / S_expl_br (explosion bottom right) - e S_eye (eye or sphere) - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -6538,6 +6538,11 @@ + | S_expl_mr (explosion middle right) + \ S_expl_bl (explosion bottom left) + - S_expl_bc (explosion bottom center) + / S_expl_br (explosion bottom right) + e S_eye (eye or sphere) ^ S_falling_rock_trap (falling rock trap) f S_feline (cat or other feline) ^ S_fire_trap (fire trap) @@ -6586,15 +6591,10 @@ N S_naga (naga) . S_ndoor (doorway without door) n S_nymph (nymph) - O S_ogre (ogre) - o S_orc (orc) - p S_piercer (piercer) - ^ S_pit (pit) - # S_poisoncloud (poison cloud) - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -6604,6 +6604,11 @@ + O S_ogre (ogre) + o S_orc (orc) + p S_piercer (piercer) + ^ S_pit (pit) + # S_poisoncloud (poison cloud) ^ S_polymorph_trap (polymorph trap) } S_pool (water) ! S_potion (potion) @@ -6652,15 +6657,10 @@ # S_tree (tree) T S_troll (troll) | S_trwall (wall) - - S_tuwall (wall) - U S_umber (umber hulk) - S_unexplored (unexplored terrain) - u S_unicorn (unicorn or horse) - < S_upladder (ladder up) - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -6670,6 +6670,11 @@ + - S_tuwall (wall) + U S_umber (umber hulk) + S_unexplored (unexplored terrain) + u S_unicorn (unicorn or horse) + < S_upladder (ladder up) < S_upstair (staircase up) V S_vampire (vampire) | S_vbeam (vertical beam [zap animation]) @@ -6718,15 +6723,10 @@ has a UTF8 handler within the symbols file such as the enhanced1 set, or individually within your nethack.rc file. - The format for defining a glyph representation is: - - OPTIONS=glyph:glyphid/U+nnnn/R-G-B - - The window port that is active needs to provide support for dis- - playing UTF-8 character sequences and explicit red-green-blue colors - NetHack 3.7.0 March 21, 2026 + + NetHack 3.7.0 March 25, 2026 @@ -6736,6 +6736,12 @@ + The format for defining a glyph representation is: + + OPTIONS=glyph:glyphid/U+nnnn/R-G-B + + The window port that is active needs to provide support for dis- + playing UTF-8 character sequences and explicit red-green-blue colors in order for the glyph representation to be visible. For example, the following line in your configuration file will cause the glyph repre- sentation for glyphid G_pool to use Unicode codepoint U+224B and the @@ -6783,16 +6789,10 @@ The most crucial settings to make the game more accessible are: - symset:plain - Load a symbol set appropriate for use by blind players. - - menustyle:traditional - This will assist in the interface to speech synthesizers. - - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -6802,6 +6802,12 @@ + symset:plain + Load a symbol set appropriate for use by blind players. + + menustyle:traditional + This will assist in the interface to speech synthesizers. + nomenu_overlay Show menus on a cleared screen and aligned to the left edge. @@ -6851,14 +6857,8 @@ your screen-reader reads those lines. The same information can be seen via the "#attributes" command. - showdamage - Give a message of damage taken and how many hit points are left. - - - - - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -6868,6 +6868,9 @@ + showdamage + Give a message of damage taken and how many hit points are left. + 9.18. Global Configuration for System Administrators If NetHack is compiled with the SYSCF option, a system adminis- @@ -6919,12 +6922,9 @@ ENTRYMAX = Maximum number of entries in the score file. - POINTSMIN = Minimum number of points to get an entry in the score - file. - - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -6934,6 +6934,9 @@ + POINTSMIN = Minimum number of points to get an entry in the score + file. + PERS_IS_UID = 0 or 1 to use user names or numeric userids, respec- tively, to identify unique people for the score file. @@ -6984,13 +6987,10 @@ internally inconsistent state, or if the #bugreport command is invoked. - 10. Scoring - - NetHack maintains a list of the top scores or scorers on your - machine, depending on how it is set up. In the latter case, each - NetHack 3.7.0 March 21, 2026 + + NetHack 3.7.0 March 25, 2026 @@ -7000,6 +7000,10 @@ + 10. Scoring + + NetHack maintains a list of the top scores or scorers on your + machine, depending on how it is set up. In the latter case, each account on the machine can post only one non-winning score on this list. If you score higher than someone else on this list, or better your previous score, you will be inserted in the proper place under @@ -7050,13 +7054,9 @@ user name to be allowed to use debug mode; for others, the hero must be given a particular character name (but may be any role; there's no connection between "wizard mode" and the Wizard role). Attempting to - start a game in debug mode when not allowed or not available will - result in falling back to explore mode instead. - - - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -7066,6 +7066,9 @@ + start a game in debug mode when not allowed or not available will + result in falling back to explore mode instead. + 12. Credits The original hack game was modeled on the Berkeley UNIX rogue @@ -7117,12 +7120,9 @@ Kevin Darcy later joined the main NetHack Development Team to produce subsequent revisions of 3.0. - Olaf Seibert ported NetHack 2.3 and 3.0 to the Amiga. Norm - Meluch, Stephen Spackman and Pierre Martineau designed overlay code - for PC NetHack 3.0. Johnny Lee ported NetHack 3.0 to the Macintosh. - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -7132,6 +7132,9 @@ + Olaf Seibert ported NetHack 2.3 and 3.0 to the Amiga. Norm + Meluch, Stephen Spackman and Pierre Martineau designed overlay code + for PC NetHack 3.0. Johnny Lee ported NetHack 3.0 to the Macintosh. Along with various other Dungeoneers, they continued to enhance the PC, Macintosh, and Amiga ports through the later revisions of 3.0. @@ -7183,12 +7186,9 @@ Warwick Allison wrote a graphically displayed version of NetHack for the Atari where the tiny pictures were described as "icons" and were distinct for specific types of monsters and objects rather than - just their classes. He contributed them to the NetHack Development - Team which rechristened them "tiles", original usage which has subse- - quently been picked up by various other games. NetHack's tiles sup- - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -7198,6 +7198,9 @@ + just their classes. He contributed them to the NetHack Development + Team which rechristened them "tiles", original usage which has subse- + quently been picked up by various other games. NetHack's tiles sup- port was then implemented on other platforms (initially MS-DOS but eventually Windows, Qt, and X11 too). @@ -7249,12 +7252,9 @@ 1999's year 99 was followed by 2000's year 100. That got written out successfully but it unintentionally introduced an extra column in the file layout which prevented score entries from being read back in cor- - rectly, interfering with insertion of new high scores and with - retrieval of old character names to use for random ghost and statue - names in the current game.) - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -7264,6 +7264,10 @@ + rectly, interfering with insertion of new high scores and with + retrieval of old character names to use for random ghost and statue + names in the current game.) + The 3.3 NetHack Development Team, consisting of Michael Allison, Ken Arromdee, David Cohrs, Jessie Collet, Steve Creps, Kevin Darcy, Timo Hakulinen, Kevin Hugo, Steve Linhart, Ken Lorber, Dean Luick, Pat @@ -7314,13 +7318,9 @@ Janne Salmijarvi and Teemu Suikki maintained and enhanced the Amiga port of 3.4 after Janne Salmijarvi resurrected it for 3.3.1. - Christian "Marvin" Bressler maintained 3.4 for the Atari after he - resurrected it for 3.3.1. - - - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -7330,6 +7330,9 @@ + Christian "Marvin" Bressler maintained 3.4 for the Atari after he + resurrected it for 3.3.1. + The release of NetHack 3.4.3 in December 2003 marked the begin- ning of a long release hiatus. 3.4.3 proved to be a remarkably stable version that provided continued enjoyment by the community for more @@ -7381,12 +7384,9 @@ Michael Allison, David Cohrs, Bart House, Pasi Kallinen, Alex Kompel, Dion Nicolaas, Derek S. Ray and Yitzhak Sapir maintained the - port of NetHack 3.6 for Microsoft Windows. - - - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -7396,6 +7396,8 @@ + port of NetHack 3.6 for Microsoft Windows. + Pat Rankin attempted to keep the VMS port running for NetHack 3.6, hindered by limited access. Kevin Smolkowski has updated and tested it for the most recent version of OpenVMS (V8.4 as of this @@ -7450,9 +7452,7 @@ - - - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -7518,7 +7518,7 @@ sends a particularly intriguing modification to help out with the - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -7584,7 +7584,7 @@ - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -7650,7 +7650,7 @@ - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 @@ -7716,7 +7716,7 @@ - NetHack 3.7.0 March 21, 2026 + NetHack 3.7.0 March 25, 2026 From 11f27226cb44b5b9b3f08e405c39c291a2853186 Mon Sep 17 00:00:00 2001 From: PatR Date: Wed, 25 Mar 2026 11:18:15 -0700 Subject: [PATCH 362/442] 'pauper' documentation The LaTeX Guidebook has been updated but not tested. It's unlikely that this small change has introduced any problems though. --- dat/opthelp | 1 + doc/Guidebook.mn | 15 ++++++++++++++- doc/Guidebook.tex | 17 ++++++++++++++++- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/dat/opthelp b/dat/opthelp index a5c7d9111..68eb53215 100644 --- a/dat/opthelp +++ b/dat/opthelp @@ -48,6 +48,7 @@ nudist start your character without armor [False] null allow nulls to be sent to your terminal [True] try turning this option off (forcing NetHack to use its own delay code) if moving objects seem to teleport across rooms +pauper start your character with no possessions [False] perm_invent keep inventory in a permanent window [False] pickup_stolen override pickup_types for stolen objects [True] pickup_thrown override pickup_types for thrown objects [True] diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 2ba4aafd1..03aefb6f9 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -3399,6 +3399,14 @@ not penalized for being spoken to by an angry god, priest(ess), or other religious figure; a true atheist would hear the words but attach no special meaning to them. .pg +A pauper starts the game with no possessions, no spells, and no weapon or +spell skills (and if playing as a knight, your pony will not have a saddle). +Can only be initiated by starting a new game with \f(CROPTIONS=pauper\fP +set in your run-time configurtion file or \f(CRNETHACKOPTIONS\fP environment +variable. +Once the game is underway, you can acquire and use items, spells, and skills +in the usual way. +.pg Most players fight with a wielded weapon (or tool intended to be wielded as a weapon). Another challenge is to win the game without using such a wielded weapon. You are still permitted to throw, @@ -3555,7 +3563,8 @@ the Castle level's drawbridge or can be given to you via prayer boon. and \fIPauper\fP are also conducts, and they can only be enabled by setting the correspondingly named option in NETHACKOPTIONS or run-time configuration file prior to game start. -In the case of \fIBlind\fP and \fIDeaf\fP, the option also enforces the conduct. +In the case of \fIBlind\fP and \fIDeaf\fP, the option also enforces the +conduct. They aren't really significant accomplishments unless/until you make substantial progress into the dungeon. . @@ -4488,6 +4497,10 @@ The positive (no \(oq!\(cq) and negative (with \(oq!\(cq) entries can be intermixed. .lp pauper Start the character with no possessions (default false). Persistent. +.lp "" +Also start with no spells or skills, which are tied to starting equipment. +Does not inhibit acquiring and using items, spells, and skills once play +has started. .lp perm_invent If true, always display your current inventory in a window (default false). .lp "" diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index d5b6c72a1..ec7c5d80e 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -3654,6 +3654,15 @@ not penalized for being spoken to by an angry god, priest(ess), or other religious figure; a true atheist would hear the words but attach no special meaning to them. +%.pg +A pauper starts the game with no possessions, no spells, and no weapon or +spell skills (and if playing as a knight, your pony will not have a saddle). +Can only be initiated by starting a new game with \texttt{OPTIONS=pauper} +set in your run-time configurtion file or \texttt{NETHACKOPTIONS} environment +variable. +Once the game is underway, you can acquire and use items, spells, and skills +in the usual way. + %.pg Most players fight with a wielded weapon (or tool intended to be wielded as a weapon). Another challenge is to win the game without @@ -3842,7 +3851,8 @@ the Castle level's drawbridge or can be given to you via prayer boon. \textit{Blind}, \textit{Deaf}, \textit{Nudist}, and \textit{Pauper} are also conducts, and they can only be enabled by setting the correspondingly named option in \texttt{NETHACKOPTIONS} or run-time configuration file prior to game start. -In the case of \textit{Blind} and \textit{Deaf}, the option also enforces the conduct. +In the case of \textit{Blind} and \textit{Deaf}, the option also enforces the +conduct. They aren't really significant accomplishments unless/until you make substantial progress into the dungeon. @@ -4900,6 +4910,11 @@ can be intermixed. %.lp \item[pauper] Start the character with no possessions (default false). Persistent. +%.lp "" +\\ +Also start with no spells or skills, which are tied to starting equipment. +Does not inhibit acquiring and using items, spells, and skills once play +has started. %.lp \item[perm\textunderscore invent] If true, always display your current inventory in a window (default is false). From d939238ea6f9c9789655e0746e42c252d1ff311d Mon Sep 17 00:00:00 2001 From: nhw_cron Date: Wed, 25 Mar 2026 15:16:08 -0400 Subject: [PATCH 363/442] This is cron-daily v1-Jan-12-2026. 005guidebook updated: doc/Guidebook.txt --- doc/Guidebook.txt | 3496 ++++++++++++++++++++++----------------------- 1 file changed, 1748 insertions(+), 1748 deletions(-) diff --git a/doc/Guidebook.txt b/doc/Guidebook.txt index dad815917..b652a455e 100644 --- a/doc/Guidebook.txt +++ b/doc/Guidebook.txt @@ -3736,24 +3736,24 @@ other religious figure; a true atheist would hear the words but attach no special meaning to them. - Most players fight with a wielded weapon (or tool intended to be - wielded as a weapon). Another challenge is to win the game without - using such a wielded weapon. You are still permitted to throw, fire, - and kick weapons; use a wand, spell, or other type of item; or fight + A pauper starts the game with no possessions, no spells, and no + weapon or spell skills (and if playing as a knight, your pony will not + have a saddle). Can only be initiated by starting a new game with + OPTIONS=pauper set in your run-time configurtion file or NETHACKOP- + TIONS environment variable. Once the game is underway, you can + acquire and use items, spells, and skills in the usual way. + + Most players fight with a wielded weapon (or tool intended to be + wielded as a weapon). Another challenge is to win the game without + using such a wielded weapon. You are still permitted to throw, fire, + and kick weapons; use a wand, spell, or other type of item; or fight with your hands and feet. - In NetHack, a pacifist refuses to cause the death of any other - monster (i.e. if you would get experience for the death). This is a - particularly difficult challenge, although it is still possible to + In NetHack, a pacifist refuses to cause the death of any other + monster (i.e. if you would get experience for the death). This is a + particularly difficult challenge, although it is still possible to gain experience by other means. - An illiterate character does not read or write. This includes - reading a scroll, spellbook, fortune cookie message, or t-shirt; writ- - ing a scroll; or making an engraving of anything other than a single - "X" (the traditional signature of an illiterate person). Reading an - engraving, or any item that is absolutely necessary to win the game, - is not counted against this conduct. The identity of scrolls and - spellbooks (and knowledge of spells) in your starting inventory is NetHack 3.7.0 March 25, 2026 @@ -3766,60 +3766,60 @@ - assumed to be learned from your teachers prior to the start of the + An illiterate character does not read or write. This includes + reading a scroll, spellbook, fortune cookie message, or t-shirt; writ- + ing a scroll; or making an engraving of anything other than a single + "X" (the traditional signature of an illiterate person). Reading an + engraving, or any item that is absolutely necessary to win the game, + is not counted against this conduct. The identity of scrolls and + spellbooks (and knowledge of spells) in your starting inventory is + assumed to be learned from your teachers prior to the start of the game and isn't counted. - There is a side-branch to the main dungeon called "Sokoban," - briefly described in the earlier section about Traps. As mentioned - there, the goal is to push boulders into pits and/or holes to plug - those in order to both get the boulders out of the way and be able to - go past the traps. There are some special "rules" that are active - when in that branch of the dungeon. Some rules can't be bypassed, - such as being unable to push a boulder diagonally. Other rules can, + There is a side-branch to the main dungeon called "Sokoban," + briefly described in the earlier section about Traps. As mentioned + there, the goal is to push boulders into pits and/or holes to plug + those in order to both get the boulders out of the way and be able to + go past the traps. There are some special "rules" that are active + when in that branch of the dungeon. Some rules can't be bypassed, + such as being unable to push a boulder diagonally. Other rules can, such as not smashing boulders with magic or tools, but doing so causes - you to receive a luck penalty. No message about that is given at the + you to receive a luck penalty. No message about that is given at the time, but it is tracked as a conduct. The #conduct command and end of - game disclosure will report whether you have abided by the special - rules of Sokoban, and if not, how many times you violated them, pro- + game disclosure will report whether you have abided by the special + rules of Sokoban, and if not, how many times you violated them, pro- viding you with a way to discover which actions incur bad luck so that - you can be better informed about whether or not to avoid repeating + you can be better informed about whether or not to avoid repeating those actions in the future. (Note: the Sokoban conduct will only be displayed if you have entered the Sokoban branch of the dungeon during - the current game. Once that has happened, it becomes part of dis- - closed conduct even if you haven't done anything interesting there. - Ending the game with "never broke the Sokoban rules" conduct is most - meaningful if you also manage to perform the "obtained the Sokoban + the current game. Once that has happened, it becomes part of dis- + closed conduct even if you haven't done anything interesting there. + Ending the game with "never broke the Sokoban rules" conduct is most + meaningful if you also manage to perform the "obtained the Sokoban prize" achievement (see Achievements below).) - There are several other challenges tracked by the game. It is - possible to eliminate one or more species of monsters by genocide; + There are several other challenges tracked by the game. It is + possible to eliminate one or more species of monsters by genocide; playing without this feature is considered a challenge. When the game - offers you an opportunity to genocide monsters, you may respond with - the monster type "none" if you want to decline. You can change the - form of an item into another item of the same type ("polypiling") or - the form of your own body into another creature ("polyself") by wand, + offers you an opportunity to genocide monsters, you may respond with + the monster type "none" if you want to decline. You can change the + form of an item into another item of the same type ("polypiling") or + the form of your own body into another creature ("polyself") by wand, spell, or potion of polymorph; avoiding these effects are each consid- - ered challenges. Polymorphing monsters, including pets, does not - break either of these challenges. Finally, you may sometimes receive - wishes; a game without an attempt to wish for any items is a chal- + ered challenges. Polymorphing monsters, including pets, does not + break either of these challenges. Finally, you may sometimes receive + wishes; a game without an attempt to wish for any items is a chal- lenge, as is a game without wishing for an artifact (even if the arti- fact immediately disappears). When the game offers you an opportunity - to make a wish for an item, you may choose "nothing" if you want to + to make a wish for an item, you may choose "nothing" if you want to decline. 8.1. Achievements - End of game disclosure will also display various achievements - representing progress toward ultimate ascension, if any have been - attained. They aren't directly related to conduct but are grouped - with it because they fall into the same category of "bragging rights" - and to limit the number of questions during disclosure. Listed here - roughly in order of difficulty and not necessarily in the order in - which you might accomplish them. - - Rank - Attained rank title Rank. - Shop - Entered a shop. - + End of game disclosure will also display various achievements + representing progress toward ultimate ascension, if any have been + attained. They aren't directly related to conduct but are grouped + with it because they fall into the same category of "bragging rights" NetHack 3.7.0 March 25, 2026 @@ -3832,6 +3832,12 @@ + and to limit the number of questions during disclosure. Listed here + roughly in order of difficulty and not necessarily in the order in + which you might accomplish them. + + Rank - Attained rank title Rank. + Shop - Entered a shop. Temple - Entered a temple. Mines - Entered the Gnomish Mines. Town - Entered Mine Town. @@ -3839,12 +3845,12 @@ Novel - Read a passage from a Discworld Novel. Sokoban - Entered Sokoban. Big Room - Entered the Big Room. - Soko-Prize - Explored to the top of Sokoban and found a + Soko-Prize - Explored to the top of Sokoban and found a special item there. - Mines' End - Explored to the bottom of the Gnomish Mines + Mines' End - Explored to the bottom of the Gnomish Mines and found a special item there. Medusa - Defeated Medusa. - Tune - Discovered the tune that can be used to open + Tune - Discovered the tune that can be used to open and close the drawbridge on the Castle level. Bell - Acquired the Bell of Opening. Gehennom - Entered Gehennom. @@ -3864,27 +3870,21 @@ Notes: - Achievements are recorded and subsequently reported in the order - in which they happen during your current game rather than the order + Achievements are recorded and subsequently reported in the order + in which they happen during your current game rather than the order listed here. - There are nine titles for each role, bestowed at experi- - ence levels 1, 3, 6, 10, 14, 18, 22, 26, and 30. The one for experi- - ence level 1 is not recorded as an achievement. Losing enough levels + There are nine titles for each role, bestowed at experi- + ence levels 1, 3, 6, 10, 14, 18, 22, 26, and 30. The one for experi- + ence level 1 is not recorded as an achievement. Losing enough levels to revert to lower rank(s) does not discard the corresponding achieve- ment(s). - There's no guaranteed Novel so the achievement to read one might - not always be attainable (except perhaps by wishing). Similarly, the - Big Room level is not always present. Unlike with the Novel, there's + There's no guaranteed Novel so the achievement to read one might + not always be attainable (except perhaps by wishing). Similarly, the + Big Room level is not always present. Unlike with the Novel, there's no way to wish for this opportunity. - The "special items" hidden in Mines' End and Sokoban are not - unique but are considered to be prizes or rewards for exploring those - levels since doing so is not necessary to complete the game. Finding - other instances of the same objects doesn't record the corresponding - achievement. - @@ -3898,60 +3898,60 @@ - The Medusa achievement is recorded if she dies for any reason, + The "special items" hidden in Mines' End and Sokoban are not + unique but are considered to be prizes or rewards for exploring those + levels since doing so is not necessary to complete the game. Finding + other instances of the same objects doesn't record the corresponding + achievement. + + The Medusa achievement is recorded if she dies for any reason, even if you are not directly responsible, and only if she dies. The 5-note tune can be learned via trial and error with a musical - instrument played closely enough--but not too close!--to the Castle + instrument played closely enough--but not too close!--to the Castle level's drawbridge or can be given to you via prayer boon. - Blind, Deaf, Nudist, and Pauper are also conducts, and they can - only be enabled by setting the correspondingly named option in + Blind, Deaf, Nudist, and Pauper are also conducts, and they can + only be enabled by setting the correspondingly named option in NETHACKOPTIONS or run-time configuration file prior to game start. In - the case of Blind and Deaf, the option also enforces the conduct. - They aren't really significant accomplishments unless/until you make + the case of Blind and Deaf, the option also enforces the conduct. + They aren't really significant accomplishments unless/until you make substantial progress into the dungeon. 9. Options - Due to variations in personal tastes and conceptions of how - NetHack should do things, there are options you can set to change how + Due to variations in personal tastes and conceptions of how + NetHack should do things, there are options you can set to change how NetHack behaves. 9.1. Setting the options Options may be set in a number of ways. Within the game, the `O' - command allows you to view all options and change most of them. You - can also set options automatically by placing them in a configuration + command allows you to view all options and change most of them. You + can also set options automatically by placing them in a configuration file, or in the NETHACKOPTIONS environment variable. Some versions of - NetHack also have front-end programs that allow you to set options + NetHack also have front-end programs that allow you to set options before starting the game or a global configuration for system adminis- trators. 9.2. Using a configuration file - The default name of the configuration file varies on different + The default name of the configuration file varies on different operating systems. - On UNIX, Linux, and macOS it is ".nethackrc" in the user's home + On UNIX, Linux, and macOS it is ".nethackrc" in the user's home directory. The file may not exist, but it is a normal ASCII text file and can be created with any text editor. - On Windows, the name is ".nethackrc" located in the folder - "%USERPROFILE%\NetHack\". The file may not exist, but it is a normal - ASCII text file can can be created with any text editor. After run- - ning NetHack for the first time, you should find a default template - for the configuration file named ".nethackrc.template" in - "%USERPROFILE%\NetHack\". If you have not created the configuration + On Windows, the name is ".nethackrc" located in the folder + "%USERPROFILE%\NetHack\". The file may not exist, but it is a normal + ASCII text file can can be created with any text editor. After run- + ning NetHack for the first time, you should find a default template + for the configuration file named ".nethackrc.template" in + "%USERPROFILE%\NetHack\". If you have not created the configuration file, NetHack will create one for you using the default template file. - On MS-DOS, it is "defaults.nh" in the same folder as nethack.exe. - Any line in the configuration file starting with `#' is treated - as a comment and ignored. Empty lines are ignored. - - Any line beginning with `[' and ending in `]' is a section marker - (the closing `]' can be followed by whitespace and then an arbitrary NetHack 3.7.0 March 25, 2026 @@ -3964,29 +3964,36 @@ - comment beginning with `#'). The text between the square brackets is - the section name. Section markers are only valid after a CHOOSE + On MS-DOS, it is "defaults.nh" in the same folder as nethack.exe. + + Any line in the configuration file starting with `#' is treated + as a comment and ignored. Empty lines are ignored. + + Any line beginning with `[' and ending in `]' is a section marker + (the closing `]' can be followed by whitespace and then an arbitrary + comment beginning with `#'). The text between the square brackets is + the section name. Section markers are only valid after a CHOOSE directive and their names are case-insensitive. Lines after a section - marker belong to that section up until another section starts or a - marker without a name is encountered or the file ends. Lines within - sections are ignored unless a CHOOSE directive has selected that sec- + marker belong to that section up until another section starts or a + marker without a name is encountered or the file ends. Lines within + sections are ignored unless a CHOOSE directive has selected that sec- tion. - You can use different configuration directives in the file, some - of which can be used multiple times. In general, the directives are - written in capital letters, followed by an equals sign, followed by + You can use different configuration directives in the file, some + of which can be used multiple times. In general, the directives are + written in capital letters, followed by an equals sign, followed by settings particular to that directive. Here is a list of allowed directives: OPTIONS There are two types of options, boolean and compound options. Bool- - ean options toggle a setting on or off, while compound options take - more diverse values. Prefix a boolean option with "no" or `!' to - turn it off. For compound options, the option name and value are - separated by a colon. Some options are persistent, and apply only + ean options toggle a setting on or off, while compound options take + more diverse values. Prefix a boolean option with "no" or `!' to + turn it off. For compound options, the option name and value are + separated by a colon. Some options are persistent, and apply only to new games. You can specify multiple OPTIONS directives, and mul- - tiple options separated by commas in a single OPTIONS directive. + tiple options separated by commas in a single OPTIONS directive. (Comma separated options are processed from right to left.) Example: @@ -3996,28 +4003,21 @@ HACKDIR Default location of files NetHack needs. On Windows HACKDIR defaults - to the location of the NetHack.exe or NetHackw.exe file so setting + to the location of the NetHack.exe or NetHackw.exe file so setting HACKDIR to override that is not usually necessary or recommended. LEVELDIR - The location that in-progress level files are stored. Defaults to + The location that in-progress level files are stored. Defaults to HACKDIR, must be writable. SAVEDIR - The location where saved games are kept. Defaults to HACKDIR, must + The location where saved games are kept. Defaults to HACKDIR, must be writable. BONESDIR The location that bones files are kept. Defaults to HACKDIR, must be writable. - LOCKDIR - The location that file synchronization locks are stored. Defaults to - HACKDIR, must be writable. - - TROUBLEDIR - The location that a record of game aborts and self-diagnosed game - problems is kept. Defaults to HACKDIR, must be writable. NetHack 3.7.0 March 25, 2026 @@ -4030,10 +4030,18 @@ + LOCKDIR + The location that file synchronization locks are stored. Defaults to + HACKDIR, must be writable. + + TROUBLEDIR + The location that a record of game aborts and self-diagnosed game + problems is kept. Defaults to HACKDIR, must be writable. + AUTOCOMPLETE - Enable or disable an extended command autocompletion. Autocomple- + Enable or disable an extended command autocompletion. Autocomple- tion has no effect for the X11 windowport. You can specify multiple - autocompletions. To enable autocompletion, list the extended com- + autocompletions. To enable autocompletion, list the extended com- mand. Prefix the command with "!" to disable the autocompletion for that command. @@ -4042,13 +4050,13 @@ AUTOCOMPLETE=zap,!annotate AUTOPICKUP_EXCEPTION - Set exceptions to the pickup_types option. See the "Configuring + Set exceptions to the pickup_types option. See the "Configuring Autopickup Exceptions" section. BINDINGS - Change the key bindings of some special keys, menu accelerators, + Change the key bindings of some special keys, menu accelerators, extended commands, or mouse buttons. You can specify multiple bind- - ings. Format is key followed by the command, separated by a colon. + ings. Format is key followed by the command, separated by a colon. See the "Changing Key Bindings" section for more information. Example: @@ -4071,19 +4079,11 @@ OPTIONS=!rest_on_space If [] is present, the preceding section is closed and no new section - begins; whatever follows will be common to all sections. Otherwise + begins; whatever follows will be common to all sections. Otherwise the last section extends to the end of the options file. MENUCOLOR - Highlight menu lines with different colors. See the "Configuring - Menu Colors" section. - - MSGTYPE - Change the way messages are shown in the top status line. See the - "Configuring Message Types" section. - - ROGUESYMBOLS - Custom symbols for the rogue level's symbol set. See SYMBOLS below. + Highlight menu lines with different colors. See the "Configuring NetHack 3.7.0 March 25, 2026 @@ -4096,16 +4096,25 @@ + Menu Colors" section. + + MSGTYPE + Change the way messages are shown in the top status line. See the + "Configuring Message Types" section. + + ROGUESYMBOLS + Custom symbols for the rogue level's symbol set. See SYMBOLS below. + SOUND Define a sound mapping. See the "Configuring User Sounds" section. SOUNDDIR - Define the directory that contains the sound files. See the "Con- + Define the directory that contains the sound files. See the "Con- figuring User Sounds" section. SYMBOLS - Override one or more symbols in the symbol set used for all dungeon - levels except for the special rogue level. See the "Modifying + Override one or more symbols in the symbol set used for all dungeon + levels except for the special rogue level. See the "Modifying NetHack Symbols" section. Example: @@ -4115,9 +4124,9 @@ WIZKIT Debug mode only: extra items to add to initial inventory. Value is - the name of a text file containing a list of item names, one per - line, up to a maximum of 128 lines. Each line is processed by the - function that handles wishing. Entries are added to the wish his- + the name of a text file containing a list of item names, one per + line, up to a maximum of 128 lines. Each line is processed by the + function that handles wishing. Entries are added to the wish his- tory; see the wizwish-command. Example: @@ -4128,6 +4137,31 @@ Here is an example of configuration file contents: + + + + + + + + + + + + + + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 64 + + + # Set your character's role, race, gender, and alignment. OPTIONS=role:Valkyrie, race:Human, gender:female, align:lawful # @@ -4147,39 +4181,24 @@ - - - - - - NetHack 3.7.0 March 25, 2026 - - - - - - NetHack Guidebook 64 - - - 9.3. Using the NETHACKOPTIONS environment variable - The NETHACKOPTIONS variable is a comma-separated list of initial - values for the various options. Some can only be turned on or off. + The NETHACKOPTIONS variable is a comma-separated list of initial + values for the various options. Some can only be turned on or off. You turn one of these on by adding the name of the option to the list, - and turn it off by typing a `!' or "no" before the name. Others take - a character string as a value. You can set string options by typing - the option name, a colon or equals sign, and then the value of the - string. The value is terminated by the next comma or the end of + and turn it off by typing a `!' or "no" before the name. Others take + a character string as a value. You can set string options by typing + the option name, a colon or equals sign, and then the value of the + string. The value is terminated by the next comma or the end of string. - For example, to set up an environment variable so that color is - on, legacy is off, character name is set to "Blue Meanie", and named + For example, to set up an environment variable so that color is + on, legacy is off, character name is set to "Blue Meanie", and named fruit is set to "lime", you would enter the command % setenv NETHACKOPTIONS "color,\!leg,name:Blue Meanie,fruit:lime" - in csh (note the need to escape the `!' since it's special to that + in csh (note the need to escape the `!' since it's special to that shell), or the pair of commands $ NETHACKOPTIONS="color,!leg,name:Blue Meanie,fruit:lime" @@ -4187,35 +4206,16 @@ in sh, ksh, or bash. - The NETHACKOPTIONS value is effectively the same as a single - OPTIONS directive in a configuration file. The "OPTIONS=" prefix is - implied and comma separated options are processed from right to left. - Other types of configuration directives such as BIND or MSGTYPE are + The NETHACKOPTIONS value is effectively the same as a single + OPTIONS directive in a configuration file. The "OPTIONS=" prefix is + implied and comma separated options are processed from right to left. + Other types of configuration directives such as BIND or MSGTYPE are not allowed. - Instead of a comma-separated list of options, NETHACKOPTIONS can - be set to the full name of a configuration file you want to use. If - that full name doesn't start with a slash, precede it with `@' (at- - sign) to let NetHack know that the rest is intended as a file name. - If it does start with `/', the at-sign is optional. - - 9.4. Customization options - - Here are explanations of what the various options do. Character - strings that are too long may be truncated. Some of the options - listed may be inactive in your dungeon. - - Some options are persistent, and are saved and reloaded along - with the game. Changing a persistent option in the configuration file - applies only to new games. - - accessiblemsg - Add location or direction information to messages (default is off). - - acoustics - Enable messages about what your character hears (default on). Note - that this has nothing to do with your computer's audio capabilities. - Persistent. + Instead of a comma-separated list of options, NETHACKOPTIONS can + be set to the full name of a configuration file you want to use. If + that full name doesn't start with a slash, precede it with `@' (at- + sign) to let NetHack know that the rest is intended as a file name. NetHack 3.7.0 March 25, 2026 @@ -4228,19 +4228,39 @@ + If it does start with `/', the at-sign is optional. + + 9.4. Customization options + + Here are explanations of what the various options do. Character + strings that are too long may be truncated. Some of the options + listed may be inactive in your dungeon. + + Some options are persistent, and are saved and reloaded along + with the game. Changing a persistent option in the configuration file + applies only to new games. + + accessiblemsg + Add location or direction information to messages (default is off). + + acoustics + Enable messages about what your character hears (default on). Note + that this has nothing to do with your computer's audio capabilities. + Persistent. + alignment - Your starting alignment (align:lawful, align:neutral, or - align:chaotic). You may specify just the first letter. Many roles - and the non-human races restrict which alignments are allowed. See + Your starting alignment (align:lawful, align:neutral, or + align:chaotic). You may specify just the first letter. Many roles + and the non-human races restrict which alignments are allowed. See role for a description of how to use negation to exclude choices. If align is not specified, there is no default value; player will be - prompted unless role and/or race forces a choice for alignment. + prompted unless role and/or race forces a choice for alignment. Cannot be set with the `O' command. Persistent. autodescribe - Automatically describe the terrain under cursor when asked to get a - location on the map (default true). The whatis_coord option con- + Automatically describe the terrain under cursor when asked to get a + location on the map (default true). The whatis_coord option con- trols whether the description includes map coordinates. autodig @@ -4252,36 +4272,16 @@ sistent. autopickup - Automatically pick up things onto which you move (default off). + Automatically pick up things onto which you move (default off). Persistent. - See pickup_types and also autopickup_exception for ways to refine + See pickup_types and also autopickup_exception for ways to refine the behavior. Note: prior to version 3.7.0, the default for autopickup was on. autoquiver - This option controls what happens when you attempt the `f' (fire) - command when nothing is quivered or readied (default false). When - true, the computer will fill your quiver or quiver sack or make - ready some suitable weapon. Note that it will not take into account - the blessed/cursed status, enchantment, damage, or quality of the - weapon; you are free to manually fill your quiver or quiver sack or - make ready with the `Q' command instead. If no weapon is found or - the option is false, the `t' (throw) command is executed instead. - Persistent. - - autounlock - Controls what action to take when attempting to walk into a locked - door or to loot a locked container. Takes a plus-sign separated - list of values: - - Untrap - prompt about whether to attempt to find a trap; it might - fail to find one even when present; if it does find one, - it will ask whether you want to try to disarm the trap; - if you decline, your character will forget that the door - or box is trapped; - Apply-Key - if carrying a key or other unlocking tool, prompt about + This option controls what happens when you attempt the `f' (fire) NetHack 3.7.0 March 25, 2026 @@ -4294,24 +4294,44 @@ + command when nothing is quivered or readied (default false). When + true, the computer will fill your quiver or quiver sack or make + ready some suitable weapon. Note that it will not take into account + the blessed/cursed status, enchantment, damage, or quality of the + weapon; you are free to manually fill your quiver or quiver sack or + make ready with the `Q' command instead. If no weapon is found or + the option is false, the `t' (throw) command is executed instead. + Persistent. + + autounlock + Controls what action to take when attempting to walk into a locked + door or to loot a locked container. Takes a plus-sign separated + list of values: + + Untrap - prompt about whether to attempt to find a trap; it might + fail to find one even when present; if it does find one, + it will ask whether you want to try to disarm the trap; + if you decline, your character will forget that the door + or box is trapped; + Apply-Key - if carrying a key or other unlocking tool, prompt about using it; - Kick - kick the door (if you omit untrap or decline to attempt - untrap and you omit apply-key or you lack a key or you - decline to use the key; has no effect on containers); - Force - try to force a container's lid with your currently - wielded weapon (if you omit untrap or decline to attempt + Kick - kick the door (if you omit untrap or decline to attempt untrap and you omit apply-key or you lack a key or you + decline to use the key; has no effect on containers); + Force - try to force a container's lid with your currently + wielded weapon (if you omit untrap or decline to attempt + untrap and you omit apply-key or you lack a key or you decline to use the key; has no effect on doors); - None - none of the above; can't be combined with the other + None - none of the above; can't be combined with the other choices. Omitting the value is treated as if autounlock:apply-key. Preceding autounlock with `!' or "no" is treated as autounlock:none. - Applying a key might set off a trap if the door or container is - trapped. Successfully kicking a door will break it and wake up - nearby monsters. Successfully forcing a container open will break - its lock and might also destroy some of its contents or damage your + Applying a key might set off a trap if the door or container is + trapped. Successfully kicking a door will break it and wake up + nearby monsters. Successfully forcing a container open will break + its lock and might also destroy some of its contents or damage your weapon or both. The default is Apply-Key. Persistent. @@ -4323,31 +4343,11 @@ Allow saving and loading bones files (default true). Persistent. boulder - Set the character used to display boulders (default is the "large + Set the character used to display boulders (default is the "large rock" class symbol, ``'). catname - Name your starting cat (for example "catname:Morris"). Cannot be - set with the `O' command. - - character - Synonym for "role" to pick the type of your character (for example - "character:Monk"). See role for more details. - - checkpoint - Save game state after each level change, for possible recovery after - program crash (default on). Persistent. - - cmdassist - Have the game provide some additional command assistance for new - players if it detects some anticipated mistakes (default on). - - confirm - Have user confirm attacks on pets, shopkeepers, and other peaceable - creatures (default on). Persistent. - - dark_room - Show out-of-sight areas of lit rooms (default on). Persistent. + Name your starting cat (for example "catname:Morris"). Cannot be NetHack 3.7.0 March 25, 2026 @@ -4360,17 +4360,38 @@ + set with the `O' command. + + character + Synonym for "role" to pick the type of your character (for example + "character:Monk"). See role for more details. + + checkpoint + Save game state after each level change, for possible recovery after + program crash (default on). Persistent. + + cmdassist + Have the game provide some additional command assistance for new + players if it detects some anticipated mistakes (default on). + + confirm + Have user confirm attacks on pets, shopkeepers, and other peaceable + creatures (default on). Persistent. + + dark_room + Show out-of-sight areas of lit rooms (default on). Persistent. + deaf Start the character permanently deaf (default false). Persistent. dropped_nopick - If this option is on, items you dropped will not be automatically - picked up, even if autopickup is also on and they are in - pickup_types or match a positive autopickup exception (default on). + If this option is on, items you dropped will not be automatically + picked up, even if autopickup is also on and they are in + pickup_types or match a positive autopickup exception (default on). Persistent. disclose - Controls what information the program reveals when the game ends. + Controls what information the program reveals when the game ends. Value is a space separated list of prompting/category pairs (default is "ni na nv ng nc no", prompt with default response of `n' for each candidate). Persistent. The possibilities are: @@ -4382,7 +4403,7 @@ c - display your conduct; also achievements, if any; o - display dungeon overview. - Each disclosure possibility can optionally be preceded by a prefix + Each disclosure possibility can optionally be preceded by a prefix which lets you refine how it behaves. Here are the valid prefixes: y - prompt you and default to yes on the prompt; @@ -4390,30 +4411,9 @@ + - disclose it without prompting; - - do not disclose it and do not prompt. - The listings of vanquished monsters and of genocided types can be + The listings of vanquished monsters and of genocided types can be sorted, so there are two additional choices for `v' and `g': - ? - prompt you and default to ask on the prompt; - # - disclose it without prompting, ask for sort order. - - Asking refers to picking one of the orderings from a menu. The `+' - disclose without prompting choice, or being prompted and answering - `y' rather than `a', will default to showing monsters in the order - specified by the sortvanquished option. - - Omitted categories are implicitly added with `n' prefix. Specified - categories with omitted prefix implicitly use `+' prefix. Order of - the disclosure categories does not matter, program display for end- - of-game disclosure follows a set sequence. - - (for example "disclose:yi na +v -g o") The example sets inventory to - prompt and default to yes, attributes to prompt and default to no, - vanquished to disclose without prompting, genocided to not disclose - and not prompt, conduct to implicitly prompt and default to no, and - overview to disclose without prompting. - - Note that the vanquished monsters list includes all monsters killed - by traps and each other as well as by you. And the dungeon overview NetHack 3.7.0 March 25, 2026 @@ -4426,22 +4426,43 @@ - shows all levels you had visited but does not reveal things about + ? - prompt you and default to ask on the prompt; + # - disclose it without prompting, ask for sort order. + + Asking refers to picking one of the orderings from a menu. The `+' + disclose without prompting choice, or being prompted and answering + `y' rather than `a', will default to showing monsters in the order + specified by the sortvanquished option. + + Omitted categories are implicitly added with `n' prefix. Specified + categories with omitted prefix implicitly use `+' prefix. Order of + the disclosure categories does not matter, program display for end- + of-game disclosure follows a set sequence. + + (for example "disclose:yi na +v -g o") The example sets inventory to + prompt and default to yes, attributes to prompt and default to no, + vanquished to disclose without prompting, genocided to not disclose + and not prompt, conduct to implicitly prompt and default to no, and + overview to disclose without prompting. + + Note that the vanquished monsters list includes all monsters killed + by traps and each other as well as by you. And the dungeon overview + shows all levels you had visited but does not reveal things about them that you hadn't discovered. dogname - Name your starting dog (for example "dogname:Fang"). Cannot be set + Name your starting dog (for example "dogname:Fang"). Cannot be set with the `O' command. extmenu - Changes the extended commands interface to pop-up a menu of avail- - able commands. It is keystroke compatible with the traditional + Changes the extended commands interface to pop-up a menu of avail- + able commands. It is keystroke compatible with the traditional interface except that it does not require that you hit Enter. It is implemented for the tty interface (default off). - For the X11 interface, which always uses a menu for choosing an - extended command, it controls whether the menu shows all available - commands (on) or just the subset of commands which have tradition- + For the X11 interface, which always uses a menu for choosing an + extended command, it controls whether the menu shows all available + commands (on) or just the subset of commands which have tradition- ally been considered extended ones (off). female @@ -4449,37 +4470,16 @@ command. fireassist - This option controls what happens when you attempt the `f' (fire) - and don't have an appropriate launcher, such as a bow or a sling, - wielded. If on, you will automatically wield the launcher. Default + This option controls what happens when you attempt the `f' (fire) + and don't have an appropriate launcher, such as a bow or a sling, + wielded. If on, you will automatically wield the launcher. Default is on. fixinv An object's inventory letter sticks to it when it's dropped (default - on). If this is off, dropping an object shifts all the remaining + on). If this is off, dropping an object shifts all the remaining inventory letters. Persistent. - force_invmenu - Commands asking for an inventory item show a menu instead of a text - query with possible menu letters. Default is off. - - fruit - Name a fruit after something you enjoy eating (for example - "fruit:mango") (default "slime mold"). Basically a nostalgic whimsy - that NetHack uses from time to time. You should set this to some- - thing you find more appetizing than slime mold. Apples, oranges, - pears, bananas, and melons already exist in NetHack, so don't use - those. - - gender - Your starting gender (gender:male or gender:female). You may spec- - ify just the first letter. Although you can still denote your gen- - der using either of the deprecated male and female options, if the - gender option is also present it will take precedence. See role for - a description of how to use negation to exclude choices. - - If gender is not specified, there is no default value; player will - be prompted unless role and/or race forces a choice for gender. NetHack 3.7.0 March 25, 2026 @@ -4492,59 +4492,59 @@ + force_invmenu + Commands asking for an inventory item show a menu instead of a text + query with possible menu letters. Default is off. + + fruit + Name a fruit after something you enjoy eating (for example + "fruit:mango") (default "slime mold"). Basically a nostalgic whimsy + that NetHack uses from time to time. You should set this to some- + thing you find more appetizing than slime mold. Apples, oranges, + pears, bananas, and melons already exist in NetHack, so don't use + those. + + gender + Your starting gender (gender:male or gender:female). You may spec- + ify just the first letter. Although you can still denote your gen- + der using either of the deprecated male and female options, if the + gender option is also present it will take precedence. See role for + a description of how to use negation to exclude choices. + + If gender is not specified, there is no default value; player will + be prompted unless role and/or race forces a choice for gender. Cannot be set with the `O' command. Persistent. goldX When filtering objects based on bless/curse state (BUCX), whether to - treat gold pieces as X (unknown bless/curse state, when "on") or U - (known to be uncursed, when "off", the default). Gold is never - blessed or cursed, but it is not described as "uncursed" even when + treat gold pieces as X (unknown bless/curse state, when "on") or U + (known to be uncursed, when "off", the default). Gold is never + blessed or cursed, but it is not described as "uncursed" even when the implicit_uncursed option is "off". help - If more information is available for an object looked at with the - `/' command, ask if you want to see it (default on). Turning help - off makes just looking at things faster, since you aren't inter- - rupted with the "More info?" prompt, but it also means that you - might miss some interesting and/or important information. Persis- + If more information is available for an object looked at with the + `/' command, ask if you want to see it (default on). Turning help + off makes just looking at things faster, since you aren't inter- + rupted with the "More info?" prompt, but it also means that you + might miss some interesting and/or important information. Persis- tent. herecmd_menu When using a windowport that supports mouse and clicking on yourself - or next to you, show a menu of possible actions for the location. + or next to you, show a menu of possible actions for the location. Same as "#herecmdmenu" and "#therecmdmenu" commands. hilite_pet - Visually distinguish pets from similar animals (default off). The - behavior of this option depends on the type of windowing you use. + Visually distinguish pets from similar animals (default off). The + behavior of this option depends on the type of windowing you use. In text windowing, text highlighting or inverse video is often used; with tiles, generally displays a heart symbol near pets. With the tty or curses interface, the petattr option controls how to - highlight pets and setting it will turn the hilite_pet option on or + highlight pets and setting it will turn the hilite_pet option on or off as warranted. - hilite_pile - Visually distinguish piles of objects from individual objects - (default off). The behavior of this option depends on the type of - windowing you use. In text windowing, text highlighting or inverse - video is often used; with tiles, generally displays a small plus- - symbol beside the object on the top of the pile. - - hitpointbar - Show a hit point bar graph behind your name and title in the status - display (default off). - - The "curses" interface supports it even if the status highlighting - feature has been disabled when building the program. The "tty" and - "mswin" (aka "Windows GUI") interfaces support it only if status - highlighting is left enabled when building. You don't need to set - up any highlighting rules in order to display the bar. If there is - one for hitpoints in effect and it specifies color, that color will - be used for the bar. However if it specifies video attributes, they - will be ignored in favor of inverse. For tty and curses, blink will - also be used if the current hitpoint value is at or below the criti- - cal HP threshold. @@ -4558,19 +4558,41 @@ + hilite_pile + Visually distinguish piles of objects from individual objects + (default off). The behavior of this option depends on the type of + windowing you use. In text windowing, text highlighting or inverse + video is often used; with tiles, generally displays a small plus- + symbol beside the object on the top of the pile. + + hitpointbar + Show a hit point bar graph behind your name and title in the status + display (default off). + + The "curses" interface supports it even if the status highlighting + feature has been disabled when building the program. The "tty" and + "mswin" (aka "Windows GUI") interfaces support it only if status + highlighting is left enabled when building. You don't need to set + up any highlighting rules in order to display the bar. If there is + one for hitpoints in effect and it specifies color, that color will + be used for the bar. However if it specifies video attributes, they + will be ignored in favor of inverse. For tty and curses, blink will + also be used if the current hitpoint value is at or below the criti- + cal HP threshold. + The "Qt" interface also supports hitpointbar, by drawing a solid bar - above the name and title with a hard-coded color scheme. (As of + above the name and title with a hard-coded color scheme. (As of this writing, having the bar enabled unintentionally inhibits resiz- - ing the status panel. To resize that, use the #optionsfull command - to toggle the hitpointbar option off, perform the resize while it's + ing the status panel. To resize that, use the #optionsfull command + to toggle the hitpointbar option off, perform the resize while it's off, then use the same command to toggle it back on.) horsename - Name your starting horse (for example "horsename:Trigger"). Cannot + Name your starting horse (for example "horsename:Trigger"). Cannot be set with the `O' command. ignintr - Ignore interrupt signals, including breaks (default off). Persis- + Ignore interrupt signals, including breaks (default off). Persis- tent. implicit_uncursed @@ -4588,30 +4610,8 @@ your character as lit (default off). Persistent. lootabc - When using a menu to interact with a container, use the old `a', - `b', and `c' keyboard shortcuts rather than the mnemonics `o', `i', - and `b' (default off). Persistent. - - mail - Enable mail delivery during the game (default on). Persistent. - - male - An obsolete synonym for "gender:male". Cannot be set with the `O' - command. - - mention_decor - Give feedback when walking onto various dungeon features such as - stairs, fountains, or altars which are ordinarily only described - when covered by one or more objects (default off). Cannot be set - with the `O' command. Persistent. - - mention_map - Give feedback when interesting map locations change (default off). - - mention_walls - Give feedback when walking against a wall (default off). Persis- - tent. - + When using a menu to interact with a container, use the old `a', + `b', and `c' keyboard shortcuts rather than the mnemonics `o', `i', NetHack 3.7.0 March 25, 2026 @@ -4624,27 +4624,49 @@ + and `b' (default off). Persistent. + + mail + Enable mail delivery during the game (default on). Persistent. + + male + An obsolete synonym for "gender:male". Cannot be set with the `O' + command. + + mention_decor + Give feedback when walking onto various dungeon features such as + stairs, fountains, or altars which are ordinarily only described + when covered by one or more objects (default off). Cannot be set + with the `O' command. Persistent. + + mention_map + Give feedback when interesting map locations change (default off). + + mention_walls + Give feedback when walking against a wall (default off). Persis- + tent. + menucolors - Enable coloring menu lines (default off). See "Configuring Menu + Enable coloring menu lines (default off). See "Configuring Menu Colors" on how to configure the colors. menustyle Controls the method used when you need to choose various objects (in - response to the Drop (aka droptype) command, for instance). The - value specified should be the first letter of one of the following: - traditional, combination, full, or partial. Default is full. Per- + response to the Drop (aka droptype) command, for instance). The + value specified should be the first letter of one of the following: + traditional, combination, full, or partial. Default is full. Per- sistent. - Traditional was the only method available for very early versions; - it consists of a prompt for object class characters, followed by an - object-by-object prompt for all items matching the selected object + Traditional was the only method available for very early versions; + it consists of a prompt for object class characters, followed by an + object-by-object prompt for all items matching the selected object class(es). Combination starts with a prompt for object class(es) of - interest, but then displays a menu of matching objects rather than + interest, but then displays a menu of matching objects rather than prompting one-by-one. Full displays a menu of object classes rather - than a character prompt, and then a menu of matching objects for + than a character prompt, and then a menu of matching objects for selection. (Choosing its `A' (Autoselect-All) choice skips the sec- - ond menu. To avoid choosing that by accident, set paranoid_con- - firm:AutoAll to require confirmation.) Partial skips the object + ond menu. To avoid choosing that by accident, set paranoid_con- + firm:AutoAll to require confirmation.) Partial skips the object class filtering and immediately displays a menu of all objects. menu_deselect_all @@ -4656,10 +4678,22 @@ menu_first_page Key to jump to the first page in a menu. Default `^'. + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 72 + + + menu_headings - Controls how the headings in a menu are highlighted. Takes a text - attribute, or text color and attribute separated by ampersand. For - allowed attributes and colors, see "Configuring Menu Colors". Not + Controls how the headings in a menu are highlighted. Takes a text + attribute, or text color and attribute separated by ampersand. For + allowed attributes and colors, see "Configuring Menu Colors". Not all ports can actually display all types. menu_invert_all @@ -4675,25 +4709,13 @@ Key to go to the next menu page. Default `>'. menu_objsyms - Inventory and other object menus are normally separated by object + Inventory and other object menus are normally separated by object class (weapons, armor, and so forth), with a menu header line at the - beginning of each group. You can have menus add the display symbol - - - NetHack 3.7.0 March 25, 2026 - - - - - - NetHack Guidebook 72 - - - + beginning of each group. You can have menus add the display symbol for the class of objects for each header line. You can also add the - display symbol for the individual item in each menu entry. For a + display symbol for the individual item in each menu entry. For a tiles map, that would be a small rendition of an object's tile. For - a text map, it is the same character as is used for the object's + a text map, it is the same character as is used for the object's class, which would be most useful when there are no headers separat- ing objects among classes. Possible values are @@ -4702,13 +4724,13 @@ 1 - Headers - show symbols on header lines but not entries; 2 - Entries - show symbols on menu entry lines but not headers; 3 - Both - show symbols on headers and entries; - 4 - Conditional - only show symbols for entries if there are no + 4 - Conditional - only show symbols for entries if there are no headers; - 5 - One-or-other - show symbols on headers, or on entries if no + 5 - One-or-other - show symbols on headers, or on entries if no headers. - Supported by tty and curses. When setting the value, it can be - specified by digit or keyword. The default value is Conditional + Supported by tty and curses. When setting the value, it can be + specified by digit or keyword. The default value is Conditional (4). menu_overlay @@ -4719,31 +4741,9 @@ Key to go to the previous menu page. Default `<'. menu_search - Key to search for some text and toggle selection state of matching + Key to search for some text and toggle selection state of matching menu items. Default `:'. - menu_select_all - Key to select all items in a menu. Default `.'. - - menu_select_page - Key to select all items on this page of a menu. Default `,'. - - menu_shift_left - Key to scroll a menu--one which has been scrolled right--back to the - left. Implemented for perm_invent only by curses and X11. Default - `{'. - - menu_shift_right - Key to scroll a menu which has text beyond the right edge to the - right. Implemented for perm_invent only by curses and X11. Default - `}'. - - mon_movement - Show a message when hero notices a monster movement (default is - off). - - monpolycontrol - Prompt for new form whenever any monster changes shape (default NetHack 3.7.0 March 25, 2026 @@ -4756,6 +4756,28 @@ + menu_select_all + Key to select all items in a menu. Default `.'. + + menu_select_page + Key to select all items on this page of a menu. Default `,'. + + menu_shift_left + Key to scroll a menu--one which has been scrolled right--back to the + left. Implemented for perm_invent only by curses and X11. Default + `{'. + + menu_shift_right + Key to scroll a menu which has text beyond the right edge to the + right. Implemented for perm_invent only by curses and X11. Default + `}'. + + mon_movement + Show a message when hero notices a monster movement (default is + off). + + monpolycontrol + Prompt for new form whenever any monster changes shape (default off). Debug mode only. montelecontrol @@ -4773,12 +4795,12 @@ port is the same as specifying 0. msghistory - The number of top line messages to keep (and be able to recall with + The number of top line messages to keep (and be able to recall with `^P') (default 20). Cannot be set with the `O' command. msg_window - Allows you to change the way recalled messages are displayed. Cur- - rently it is only supported for tty (all four choices) and for + Allows you to change the way recalled messages are displayed. Cur- + rently it is only supported for tty (all four choices) and for curses (`f' and `r' choices, default `r'). The possible values are: s - single message (default; only choice prior to 3.4.0); @@ -4786,30 +4808,8 @@ f - full window, oldest message first; r - full window reversed, newest message first. - For backward compatibility, no value needs to be specified (which - defaults to "full"), or it can be negated (which defaults to "sin- - gle"). - - name - Set your character's name (defaults to your user name). You can - also set your character's role by appending a dash and one or more - letters of the role (that is, by suffixing one of -A -B -C -H -K -M - -P -Ra -Ro -S -T -V -W). If -@ is used for the role, then a random - one will be automatically chosen. - - On some systems, the default is the player's user name; on others, - there is no default and the player will be prompted. The former can - made to behave like the latter by specifying a generic name such as - ``player''. Cannot be set with the `O' command. - - news - Read the NetHack news file, if present (default on). Since the news - is shown at the beginning of the game, there's no point in setting - this with the `O' command. - - nudist - Start the character with no armor (default false). Persistent. - + For backward compatibility, no value needs to be specified (which + defaults to "full"), or it can be negated (which defaults to "sin- NetHack 3.7.0 March 25, 2026 @@ -4822,6 +4822,28 @@ + gle"). + + name + Set your character's name (defaults to your user name). You can + also set your character's role by appending a dash and one or more + letters of the role (that is, by suffixing one of -A -B -C -H -K -M + -P -Ra -Ro -S -T -V -W). If -@ is used for the role, then a random + one will be automatically chosen. + + On some systems, the default is the player's user name; on others, + there is no default and the player will be prompted. The former can + made to behave like the latter by specifying a generic name such as + ``player''. Cannot be set with the `O' command. + + news + Read the NetHack news file, if present (default on). Since the news + is shown at the beginning of the game, there's no point in setting + this with the `O' command. + + nudist + Start the character with no armor (default false). Persistent. + null Send padding nulls to the terminal (default on). Persistent. @@ -4837,45 +4859,23 @@ -1 - by letters but use `z' to go northwest, `y' to zap wands For backward compatibility, omitting a value is the same as specify- - ing 1 and negating number_pad is the same as specifying 0. (Set- - tings 2 and 4 are for compatibility with MS-DOS or old PC Hack; in - addition to the different behavior for `5', `Alt-5' acts as `G' and - `Alt-0' acts as `I'. Setting -1 is to accommodate some QWERTZ key- - boards which have the location of the `y' and `z' keys swapped.) - When moving by numbers, to enter a count prefix for those commands - which accept one (such as "12s" to search twelve times), precede it + ing 1 and negating number_pad is the same as specifying 0. (Set- + tings 2 and 4 are for compatibility with MS-DOS or old PC Hack; in + addition to the different behavior for `5', `Alt-5' acts as `G' and + `Alt-0' acts as `I'. Setting -1 is to accommodate some QWERTZ key- + boards which have the location of the `y' and `z' keys swapped.) + When moving by numbers, to enter a count prefix for those commands + which accept one (such as "12s" to search twelve times), precede it with the letter `n' ("n12s"). packorder - Specify the order to list object types in (default + Specify the order to list object types in (default "")[%?+!=/(*`0_"). The value of this option should be a string con- taining the symbols for the various object types. Any omitted types are filled in at the end from the previous order. paranoid_confirmation - A space separated list of specific situations where alternate - prompting is desired. The default is "paranoid_confirmation:pray - swim trap". - - Confirm - for any prompts which are set to require "yes" rather - than `y', also require "no" to reject instead of - accepting any non-yes response as no; changes pray and - AutoAll to require "yes" or `no' too; - quit - require "yes" rather than `y' to confirm quitting the - game or switching into non-scoring explore mode; - die - require "yes" rather than `y' to confirm dying (not - useful in normal play; applies to explore mode); - bones - require "yes" rather than `y' to confirm saving bones - data when dying in debug mode; - attack - require "yes" rather than `y' to confirm attacking a - peaceful monster; - wand-break - require "yes" rather than `y' to confirm breaking a - wand with the apply command; - eating - require "yes" rather than `y' to confirm whether to - continue eating; - Were-change - require "yes" rather than `y' to confirm changing form - due to lycanthropy when hero has polymorph control; - pray - require `y' to confirm an attempt to pray rather than + A space separated list of specific situations where alternate NetHack 3.7.0 March 25, 2026 @@ -4888,60 +4888,60 @@ - immediately praying; on by default; (to require "yes" + prompting is desired. The default is "paranoid_confirmation:pray + swim trap". + + Confirm - for any prompts which are set to require "yes" rather + than `y', also require "no" to reject instead of + accepting any non-yes response as no; changes pray and + AutoAll to require "yes" or `no' too; + quit - require "yes" rather than `y' to confirm quitting the + game or switching into non-scoring explore mode; + die - require "yes" rather than `y' to confirm dying (not + useful in normal play; applies to explore mode); + bones - require "yes" rather than `y' to confirm saving bones + data when dying in debug mode; + attack - require "yes" rather than `y' to confirm attacking a + peaceful monster; + wand-break - require "yes" rather than `y' to confirm breaking a + wand with the apply command; + eating - require "yes" rather than `y' to confirm whether to + continue eating; + Were-change - require "yes" rather than `y' to confirm changing form + due to lycanthropy when hero has polymorph control; + pray - require `y' to confirm an attempt to pray rather than + immediately praying; on by default; (to require "yes" rather than just `y', set Confirm too); trap - require `y' to confirm an attempt to move into or onto - a known trap, unless doing so is considered to be + a known trap, unless doing so is considered to be harmless; when enabled, this confirmation is also used for moving into visible gas cloud regions; (to require - "yes" rather than just `y', set Confirm too); confir- - mation can be skipped by using the `m' movement pre- + "yes" rather than just `y', set Confirm too); confir- + mation can be skipped by using the `m' movement pre- fix; swim - prevent walking into water or lava; on by default; (to - deliberately step onto/into such terrain when this is + deliberately step onto/into such terrain when this is set, use the `m' movement prefix when adjacent); - AutoAll - require confirmation when the `A' (Autoselect-All) + AutoAll - require confirmation when the `A' (Autoselect-All) choice is selected in object class filtering menus for - menustyle:Full; (to require "yes" rather than just + menustyle:Full; (to require "yes" rather than just `y', set Confirm too); - Remove - require selection from inventory for `R' and `T' com- + Remove - require selection from inventory for `R' and `T' com- mands even when wearing just one applicable item; all - turn on all of the above. By default, the pray, swim, and trap choices are enabled, the others disabled. To disable them without setting any of the other choices, - use paranoid_confirmation:none. To keep them enabled while setting - any of the others, you can include them in the new list, such as + use paranoid_confirmation:none. To keep them enabled while setting + any of the others, you can include them in the new list, such as paranoid_confirmation:attack pray swim Remove or you can precede the - first entry in the list with a plus sign, paranoid_confirma- - tion:+attack Remove. To remove an entry that has been previously - set without removing others, precede the first entry in the list - with a minus sign, paranoid_confirmation:-swim. To both add some - new entries and remove some old ones, you can use multiple para- - noid_confirmation option settings, or you can use the `+' form and - list entries to be added by their name and entries to be removed by - `!' and name. The positive (no `!') and negative (with `!') entries - can be intermixed. - - pauper - Start the character with no possessions (default false). Persis- - tent. - - perm_invent - If true, always display your current inventory in a window (default - false). - - This only makes sense for windowing system interfaces that implement - this feature. For those that do, the perminv_mode option can be - used to refine what gets displayed for perm_invent. Setting that to - a value other than none while perm_invent is false will change it to - true. - - perminv_mode - Augments the perm_invent option. Value is one of - - none - behave as if perm_invent is false; - all - show all inventory except for gold; + first entry in the list with a plus sign, paranoid_confirma- + tion:+attack Remove. To remove an entry that has been previously + set without removing others, precede the first entry in the list + with a minus sign, paranoid_confirmation:-swim. To both add some + new entries and remove some old ones, you can use multiple para- + noid_confirmation option settings, or you can use the `+' form and + list entries to be added by their name and entries to be removed by NetHack 3.7.0 March 25, 2026 @@ -4954,6 +4954,32 @@ + `!' and name. The positive (no `!') and negative (with `!') entries + can be intermixed. + + pauper + Start the character with no possessions (default false). Persis- + tent. + + Also start with no spells or skills, which are tied to starting + equipment. Does not inhibit acquiring and using items, spells, and + skills once play has started. + + perm_invent + If true, always display your current inventory in a window (default + false). + + This only makes sense for windowing system interfaces that implement + this feature. For those that do, the perminv_mode option can be + used to refine what gets displayed for perm_invent. Setting that to + a value other than none while perm_invent is false will change it to + true. + + perminv_mode + Augments the perm_invent option. Value is one of + + none - behave as if perm_invent is false; + all - show all inventory except for gold; full - show full inventory including gold; in-use - only show items which are in use (worn, wielded, lit lamp). @@ -4966,48 +4992,22 @@ petattr Specifies one or more text highlighting attributes to use when show- ing pets on the map. Effectively a superset of the hilite_pet bool- - ean option. Curses or tty interface only; value is one of none, - bold, dim, underline, italic, blink, and inverse. Some of those + ean option. Curses or tty interface only; value is one of none, + bold, dim, underline, italic, blink, and inverse. Some of those choices might not work, depending upon terminal hardware or terminal emulation software. pettype Specify the type of your initial pet, if you are playing a character class that uses multiple types of pets; or choose to have no initial - pet at all. Possible values are "cat", "dog", "horse", and "none". + pet at all. Possible values are "cat", "dog", "horse", and "none". If the choice is not allowed for the role you are currently playing, it will be silently ignored. For example, "horse" will only be hon- ored when playing a knight. Cannot be set with the `O' command. pickup_burden - When you pick up an item that would exceed this encumbrance level - (Unencumbered, Burdened, streSsed, straiNed, overTaxed, or over- - Loaded), you will be asked if you want to continue. (Default `S'). - Persistent. - - pickup_stolen - If this option is on and autopickup is also on, try to pick up - things that a monster stole from you, even if they aren't in - pickup_types or match an autopickup exception. Default is on. Per- - sistent. - - pickup_thrown - If this option is on and autopickup is also on, try to pick up - things that you threw, even if they aren't in pickup_types or match - an autopickup exception. Default is on. Persistent. - - pickup_types - Specify the object types to be picked up when autopickup is on. - Default is all types. Persistent. - - The value is a list of object symbols, such as pickup_types:$?! to - pick up gold, scrolls, and potions. You can use autopickup_excep- - tion configuration file lines to further refine autopickup behavior. - - There is no way to set pickup_types to "none". (Setting it to an - empty value reverts to "all".) If you want to avoid automatically - picking up any types of items but do want to have autopickup on in - order to have autopickup_exception settings control what you do and + When you pick up an item that would exceed this encumbrance level + (Unencumbered, Burdened, streSsed, straiNed, overTaxed, or over- NetHack 3.7.0 March 25, 2026 @@ -5020,60 +5020,60 @@ - don't pick up, you can set pickup_types to `.'. That is the type - symbol for venom and you won't come across any venom items so won't + Loaded), you will be asked if you want to continue. (Default `S'). + Persistent. + + pickup_stolen + If this option is on and autopickup is also on, try to pick up + things that a monster stole from you, even if they aren't in + pickup_types or match an autopickup exception. Default is on. Per- + sistent. + + pickup_thrown + If this option is on and autopickup is also on, try to pick up + things that you threw, even if they aren't in pickup_types or match + an autopickup exception. Default is on. Persistent. + + pickup_types + Specify the object types to be picked up when autopickup is on. + Default is all types. Persistent. + + The value is a list of object symbols, such as pickup_types:$?! to + pick up gold, scrolls, and potions. You can use autopickup_excep- + tion configuration file lines to further refine autopickup behavior. + + There is no way to set pickup_types to "none". (Setting it to an + empty value reverts to "all".) If you want to avoid automatically + picking up any types of items but do want to have autopickup on in + order to have autopickup_exception settings control what you do and + don't pick up, you can set pickup_types to `.'. That is the type + symbol for venom and you won't come across any venom items so won't unintentionally pick such up. pile_limit - When walking across a pile of objects on the floor, threshold at + When walking across a pile of objects on the floor, threshold at which the message "there are few/several/many objects here" is given - instead of showing a popup list of those objects. A value of 0 + instead of showing a popup list of those objects. A value of 0 means "no limit" (always list the objects); a value of 1 effectively means "never show the objects" since the pile size will always be at least that big; default value is 5. Persistent. playmode - Values are "normal", "explore", or "debug". Allows selection of - explore mode (also known as discovery mode) or debug mode (also + Values are "normal", "explore", or "debug". Allows selection of + explore mode (also known as discovery mode) or debug mode (also known as wizard mode) instead of normal play. Debug mode might only - be allowed for someone logged in under a particular user name (on - multi-user systems) or specifying a particular character name (on - single-user systems) or it might be disabled entirely. Requesting + be allowed for someone logged in under a particular user name (on + multi-user systems) or specifying a particular character name (on + single-user systems) or it might be disabled entirely. Requesting it when not allowed or not possible results in explore mode instead. Default is normal play. price_quotes Whenever the game mentions the name of an object you haven't identi- fied yet, it also mentions the range of buy and sell prices you have - seen for that item (to help narrow down what it could be). The - price shown is the unit price for one item (even when you are look- - ing at a stack of multiple items). Many players may want to turn - this on while identifying objects, and then turn it back off again - for general play. Default is off. - - pushweapon - Using the `w' (wield) command when already wielding something pushes - the old item into your alternate weapon slot (default off). Like- - wise for the `a' (apply) command if it causes the applied item to - become wielded. Persistent. - - query_menu - Use a menu when asked specific yes/no queries, instead of a prompt. - - quick_farsight - When set, usually prevents the "you sense your surroundings" message - where play pauses to allow you to browse the map whenever clairvoy- - ance randomly activates. Some situations, such as being underwater - or engulfed, ignore this option. It does not affect the clairvoy- - ance spell where pausing to examine revealed objects or monsters is - less intrusive. Default is off. Persistent. - - race - Selects your race (for example, race:human). Choices are human, - dwarf, elf, gnome, and orc but most roles restrict which of the non- - human races are allowed. See role for a description of how to use - negation to exclude choices. - + seen for that item (to help narrow down what it could be). The + price shown is the unit price for one item (even when you are look- + ing at a stack of multiple items). Many players may want to turn NetHack 3.7.0 March 25, 2026 @@ -5086,60 +5086,60 @@ - If race is not specified, there is no default value; player will be - prompted unless role forces a choice for race. Cannot be set with + this on while identifying objects, and then turn it back off again + for general play. Default is off. + + pushweapon + Using the `w' (wield) command when already wielding something pushes + the old item into your alternate weapon slot (default off). Like- + wise for the `a' (apply) command if it causes the applied item to + become wielded. Persistent. + + query_menu + Use a menu when asked specific yes/no queries, instead of a prompt. + + quick_farsight + When set, usually prevents the "you sense your surroundings" message + where play pauses to allow you to browse the map whenever clairvoy- + ance randomly activates. Some situations, such as being underwater + or engulfed, ignore this option. It does not affect the clairvoy- + ance spell where pausing to examine revealed objects or monsters is + less intrusive. Default is off. Persistent. + + race + Selects your race (for example, race:human). Choices are human, + dwarf, elf, gnome, and orc but most roles restrict which of the non- + human races are allowed. See role for a description of how to use + negation to exclude choices. + + If race is not specified, there is no default value; player will be + prompted unless role forces a choice for race. Cannot be set with the `O' command. Persistent. reroll - Allows rerolling your character's starting inventory and attributes + Allows rerolling your character's starting inventory and attributes (default false). Persistent. - Note that rerolling your character is not a recommended way to play - if aiming merely to win (a lucky start has a much smaller influence - on whether or not you win the game than your actions later in the - game). This option exists partly as an acknowledgement that some + Note that rerolling your character is not a recommended way to play + if aiming merely to win (a lucky start has a much smaller influence + on whether or not you win the game than your actions later in the + game). This option exists partly as an acknowledgement that some players will insist on doing so anyway, and partly because rerolling may be necessary for certain types of challenge games. rest_on_space - Make the space bar a synonym for the `.' (#wait) command (default + Make the space bar a synonym for the `.' (#wait) command (default off). Persistent. role Pick your type of character (for example, role:Samurai); synonym for - character. See name for an alternate method of specifying your + character. See name for an alternate method of specifying your role. - This option can also be used to limit selection when role is chosen - randomly. Use a space-separated list of roles and either negate - each one or negate the option itself instead. Negation is accom- + This option can also be used to limit selection when role is chosen + randomly. Use a space-separated list of roles and either negate + each one or negate the option itself instead. Negation is accom- plished in the same manner as with boolean options, by prefixing the - option or its value(s) with `!' or "no". - Examples: - - OPTIONS=role:!arc !bar !kni - OPTIONS=!role:arc bar kni - - There can be multiple instances of the role option if they're all - negations. - - If role is not specified, there is no default value; player will be - prompted. Cannot be set with the `O' command. Persistent. - - roguesymset - This option may be used to select one of the named symbol sets found - within "symbols" to alter the symbols displayed on the screen on the - rogue level. - - rlecomp - When writing out a save file, perform run length compression of the - map. Not all ports support run length compression. It has no effect - on reading an existing save file. - - runmode - Controls the amount of screen updating for the map window when - engaged in multi-turn movement (running via shift+direction or con- - trol+direction and so forth, or via the travel command or mouse NetHack 3.7.0 March 25, 2026 @@ -5152,6 +5152,32 @@ + option or its value(s) with `!' or "no". + Examples: + + OPTIONS=role:!arc !bar !kni + OPTIONS=!role:arc bar kni + + There can be multiple instances of the role option if they're all + negations. + + If role is not specified, there is no default value; player will be + prompted. Cannot be set with the `O' command. Persistent. + + roguesymset + This option may be used to select one of the named symbol sets found + within "symbols" to alter the symbols displayed on the screen on the + rogue level. + + rlecomp + When writing out a save file, perform run length compression of the + map. Not all ports support run length compression. It has no effect + on reading an existing save file. + + runmode + Controls the amount of screen updating for the map window when + engaged in multi-turn movement (running via shift+direction or con- + trol+direction and so forth, or via the travel command or mouse click). The possible values are: teleport - update the map after movement has finished; @@ -5159,10 +5185,10 @@ walk - update the map after each step; crawl - like walk, but pause briefly after each step. - This option only affects the game's screen display, not the actual - results of moving. The default is "run"; versions prior to 3.4.1 - used "teleport" only. Whether or not the effect is noticeable will - depend upon the window port used or on the type of terminal. Per- + This option only affects the game's screen display, not the actual + results of moving. The default is "run"; versions prior to 3.4.1 + used "teleport" only. Whether or not the effect is noticeable will + depend upon the window port used or on the type of terminal. Per- sistent. safe_pet @@ -5170,42 +5196,16 @@ sistent. safe_wait - Prevents you from waiting or searching when next to a hostile mon- + Prevents you from waiting or searching when next to a hostile mon- ster (default on). Persistent. sanity_check - Evaluate monsters, objects, and map prior to each turn (default + Evaluate monsters, objects, and map prior to each turn (default off). Debug mode only. scores - Control what parts of the score list you are shown at the end (for - example "scores:5 top scores/4 around my score/own scores"). Only - the first letter of each category (`t', `a', or `o') is necessary. - Persistent. - - showdamage - Whenever your character takes damage, show a message of the damage - taken, and the amount of hit points left. - - showexp - Show your accumulated experience points on bottom line (default - off). Persistent. - - showrace - Display yourself as the glyph for your race, rather than the glyph - for your role (default off). Note that this setting affects only - the appearance of the display, not the way the game treats you. - Persistent. - - showscore - Show your approximate accumulated score on bottom line (default - off). By default, this feature is suppressed when building the pro- - gram. Persistent. - - showvers - Include the game's version number on the status lines (default off). - Potentially useful if you switch between different versions or vari- - ants, or you are making screenshots or streaming video. Using the + Control what parts of the score list you are shown at the end (for + example "scores:5 top scores/4 around my score/own scores"). Only NetHack 3.7.0 March 25, 2026 @@ -5218,9 +5218,35 @@ - statuslines:3 option is recommended so that there will be more room - available for status information, unless you're using NetHack's Qt - interface or your terminal emulator window displays fewer than 25 + the first letter of each category (`t', `a', or `o') is necessary. + Persistent. + + showdamage + Whenever your character takes damage, show a message of the damage + taken, and the amount of hit points left. + + showexp + Show your accumulated experience points on bottom line (default + off). Persistent. + + showrace + Display yourself as the glyph for your race, rather than the glyph + for your role (default off). Note that this setting affects only + the appearance of the display, not the way the game treats you. + Persistent. + + showscore + Show your approximate accumulated score on bottom line (default + off). By default, this feature is suppressed when building the pro- + gram. Persistent. + + showvers + Include the game's version number on the status lines (default off). + Potentially useful if you switch between different versions or vari- + ants, or you are making screenshots or streaming video. Using the + statuslines:3 option is recommended so that there will be more room + available for status information, unless you're using NetHack's Qt + interface or your terminal emulator window displays fewer than 25 lines. Persistent. silent @@ -5232,46 +5258,20 @@ The possible values are: - o - list object types by class, in discovery order within each + o - list object types by class, in discovery order within each class; default; - s - list object types by sortloot classification: by class, by sub- - class within class for classes which have substantial groupings - (like helmets, boots, gloves, and so forth for armor), with - object types partly-discovered via assigned name coming before + s - list object types by sortloot classification: by class, by sub- + class within class for classes which have substantial groupings + (like helmets, boots, gloves, and so forth for armor), with + object types partly-discovered via assigned name coming before fully identified types; c - list by class, alphabetically within each class; a - list alphabetically across all classes. - Can be interactively set via the `O' command or via using the `m' + Can be interactively set via the `O' command or via using the `m' prefix before the `\' or ``' command. - sortloot - Controls the sorting behavior of the pickup lists for inventory and - #loot commands and some others. Persistent. - The possible values are: - - full - always sort the lists; - loot - only sort the lists that don't use inventory letters, like - with the #loot and pickup commands; - none - show lists the traditional way without sorting; default. - - sortpack - Sort the pack contents by type when displaying inventory (default - on). Persistent. - - sortvanquished - Controls the sorting behavior for the output of the #vanquished com- - mand and also for the #genocided command. Persistent. - - The possible values are: - - t - traditional--order by monster level; ties are broken by internal - monster index; default; - d - order by monster difficulty rating; ties broken by internal - index; - a - order alphabetically, first any unique monsters then all the - others; NetHack 3.7.0 March 25, 2026 @@ -5284,13 +5284,40 @@ + sortloot + Controls the sorting behavior of the pickup lists for inventory and + #loot commands and some others. Persistent. + + The possible values are: + + full - always sort the lists; + loot - only sort the lists that don't use inventory letters, like + with the #loot and pickup commands; + none - show lists the traditional way without sorting; default. + + sortpack + Sort the pack contents by type when displaying inventory (default + on). Persistent. + + sortvanquished + Controls the sorting behavior for the output of the #vanquished com- + mand and also for the #genocided command. Persistent. + + The possible values are: + + t - traditional--order by monster level; ties are broken by internal + monster index; default; + d - order by monster difficulty rating; ties broken by internal + index; + a - order alphabetically, first any unique monsters then all the + others; c - order by monster class, by low to high level within each class; n - order by count, high to low; ties are broken by internal monster index; z - order by count, low to high; ties broken by internal index. - Can be interactively set via the `m O' command or via using the `m' - prefix before either the #vanquished command or the #genocided com- + Can be interactively set via the `m O' command or via using the `m' + prefix before either the #vanquished command or the #genocided com- mand. sounds @@ -5298,7 +5325,7 @@ on). sparkle - Display a sparkly effect when a monster (including yourself) is hit + Display a sparkly effect when a monster (including yourself) is hit by an attack to which it is resistant (default on). Persistent. spot_monsters @@ -5312,33 +5339,6 @@ If negated or set to zero, disables status hiliting. See "Configur- ing Status Hilites" for further information. - status_updates - Allow updates to the status lines at the bottom of the screen - (default true). - - suppress_alert - This option may be set to a NetHack version level to suppress alert - notification messages about feature changes for that and prior ver- - sions (for example "suppress_alert:3.3.1"). - - symset - This option may be used to select one of the named symbol sets found - within "symbols" to alter the symbols displayed on the screen. Use - "symset:default" to explicitly select the default symbols. - - time - Show the elapsed game time in turns on bottom line (default off). - Persistent. - - timed_delay - When pausing momentarily for display effect, such as with explosions - and moving objects, use a timer rather than sending extra characters - to the screen. (Applies to "tty" and "curses" interfaces only; - "X11" interface always uses a timer-based delay. The default is on - if configured into the program.) Persistent. - - - NetHack 3.7.0 March 25, 2026 @@ -5350,6 +5350,31 @@ + status_updates + Allow updates to the status lines at the bottom of the screen + (default true). + + suppress_alert + This option may be set to a NetHack version level to suppress alert + notification messages about feature changes for that and prior ver- + sions (for example "suppress_alert:3.3.1"). + + symset + This option may be used to select one of the named symbol sets found + within "symbols" to alter the symbols displayed on the screen. Use + "symset:default" to explicitly select the default symbols. + + time + Show the elapsed game time in turns on bottom line (default off). + Persistent. + + timed_delay + When pausing momentarily for display effect, such as with explosions + and moving objects, use a timer rather than sending extra characters + to the screen. (Applies to "tty" and "curses" interfaces only; + "X11" interface always uses a timer-based delay. The default is on + if configured into the program.) Persistent. + tips Show some helpful tips during gameplay (default on). Persistent. @@ -5357,8 +5382,8 @@ Draw a tombstone graphic upon your death (default on). Persistent. toptenwin - Put the ending display in a NetHack window instead of on stdout - (default off). Setting this option makes the score list visible + Put the ending display in a NetHack window instead of on stdout + (default off). Setting this option makes the score list visible when a windowing version of NetHack is started without a parent win- dow, but it no longer leaves the score list around after game end on a terminal or emulating window. @@ -5366,7 +5391,7 @@ travel Allow the travel command via mouse click (default on). Turning this option off will prevent the game from attempting unintended moves if - you make inadvertent mouse clicks on the map window. Does not + you make inadvertent mouse clicks on the map window. Does not affect traveling via the `_' ("#travel") command. Persistent. tutorial @@ -5377,33 +5402,8 @@ Provide more commentary during the game (default on). Persistent. whatis_coord - When using the `/' or `;' commands to look around on the map with - autodescribe on, display coordinates after the description. Also - works in other situations where you are asked to pick a location. - - The possible settings are: - - c - compass ("east" or "3s" or "2n,4w"); - f - full compass ("east" or "3south" or "2north,4west"); - m - map (map column x=0 is not used); - s - screen [row,column] (row is offset to match tty usage); - n - none (no coordinates shown) [default]. - - The whatis_coord option is also used with the "/m", "/M", "/o", and - "/O" sub-commands of `/', where the "none" setting is overridden - with "map". - - whatis_filter - When getting a location on the map, and using the keys to cycle - through next and previous targets, allows filtering the possible - targets. - - n - no filtering [default] - v - in view only - a - in same area only - - The area-filter tries to be slightly predictive--if you're standing - on a doorway, it will consider the area on the side of the door you + When using the `/' or `;' commands to look around on the map with + autodescribe on, display coordinates after the description. Also NetHack 3.7.0 March 25, 2026 @@ -5416,6 +5416,31 @@ + works in other situations where you are asked to pick a location. + + The possible settings are: + + c - compass ("east" or "3s" or "2n,4w"); + f - full compass ("east" or "3south" or "2north,4west"); + m - map (map column x=0 is not used); + s - screen [row,column] (row is offset to match tty usage); + n - none (no coordinates shown) [default]. + + The whatis_coord option is also used with the "/m", "/M", "/o", and + "/O" sub-commands of `/', where the "none" setting is overridden + with "map". + + whatis_filter + When getting a location on the map, and using the keys to cycle + through next and previous targets, allows filtering the possible + targets. + + n - no filtering [default] + v - in view only + a - in same area only + + The area-filter tries to be slightly predictive--if you're standing + on a doorway, it will consider the area on the side of the door you were last moving towards. Filtering can also be changed when getting a location with the "get- @@ -5423,53 +5448,28 @@ whatis_menu When getting a location on the map, and using a key to cycle through - next and previous targets, use a menu instead to pick a target. + next and previous targets, use a menu instead to pick a target. (default off) whatis_moveskip - When getting a location on the map, and using shifted movement keys - or meta-digit keys to fast-move, instead of moving 8 units at a + When getting a location on the map, and using shifted movement keys + or meta-digit keys to fast-move, instead of moving 8 units at a time, move by skipping the same glyphs. (default off) windowtype - When the program has been built to support multiple interfaces, - select which one to use, such as "tty" or "X11" (default depends on - build-time settings; use "#version" to check). Cannot be set with + When the program has been built to support multiple interfaces, + select which one to use, such as "tty" or "X11" (default depends on + build-time settings; use "#version" to check). Cannot be set with the `O' command. - When used, it should be the first option set since its value might - enable or disable the availability of various other options. For + When used, it should be the first option set since its value might + enable or disable the availability of various other options. For multiple lines in a configuration file, that would be the first non- - comment line. For a comma-separated list in NETHACKOPTIONS or an - OPTIONS line in a configuration file, that would be the rightmost + comment line. For a comma-separated list in NETHACKOPTIONS or an + OPTIONS line in a configuration file, that would be the rightmost option in the list. - wizweight - Augment object descriptions with their objects' weight (default - off). Debug mode only. - zerocomp - When writing out a save file, perform zero-comp compression of the - contents. Not all ports support zero-comp compression. It has no - effect on reading an existing save file. - - 9.5. Window Port Customization options - - Here are explanations of the various options that are used to - customize and change the characteristics of the windowtype that you - have chosen. Character strings that are too long may be truncated. - Not all window ports will adjust for all settings listed here. You - can safely add any of these options to your configuration file, and if - the window port is capable of adjusting to suit your preferences, it - will attempt to do so. If it can't it will silently ignore it. You - can find out if an option is supported by the window port that you are - currently using by checking to see if it shows up in the Options list. - Some options are dynamic and can be specified during the game with the - `O' command. - - align_message - Where to align or place the message window (top, bottom, left, or - right) NetHack 3.7.0 March 25, 2026 @@ -5482,16 +5482,43 @@ + wizweight + Augment object descriptions with their objects' weight (default + off). Debug mode only. + + zerocomp + When writing out a save file, perform zero-comp compression of the + contents. Not all ports support zero-comp compression. It has no + effect on reading an existing save file. + + 9.5. Window Port Customization options + + Here are explanations of the various options that are used to + customize and change the characteristics of the windowtype that you + have chosen. Character strings that are too long may be truncated. + Not all window ports will adjust for all settings listed here. You + can safely add any of these options to your configuration file, and if + the window port is capable of adjusting to suit your preferences, it + will attempt to do so. If it can't it will silently ignore it. You + can find out if an option is supported by the window port that you are + currently using by checking to see if it shows up in the Options list. + Some options are dynamic and can be specified during the game with the + `O' command. + + align_message + Where to align or place the message window (top, bottom, left, or + right) + align_status - Where to align or place the status window (top, bottom, left, or + Where to align or place the status window (top, bottom, left, or right). ascii_map - If NetHack can, it should display the map using simple characters - (letters and punctuation) rather than tiles graphics. In some - cases, characters can be augmented with line-drawing symbols; use - the symset option to select a symbol set such as DECgraphics or - IBMgraphics if your display supports them. Setting ascii_map to + If NetHack can, it should display the map using simple characters + (letters and punctuation) rather than tiles graphics. In some + cases, characters can be augmented with line-drawing symbols; use + the symset option to select a symbol set such as DECgraphics or + IBMgraphics if your display supports them. Setting ascii_map to True forces tiled_map to be False. color @@ -5500,15 +5527,27 @@ eight_bit_tty If NetHack can, it should pass eight-bit character values (for exam- - ple, specified with the traps option) straight through to your ter- + ple, specified with the traps option) straight through to your ter- minal (default off). font_map - if NetHack can, it should use a font by the chosen name for the map + if NetHack can, it should use a font by the chosen name for the map window. font_menu - If NetHack can, it should use a font by the chosen name for menu + If NetHack can, it should use a font by the chosen name for menu + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 85 + + + windows. font_message @@ -5520,7 +5559,7 @@ tus window. font_text - If NetHack can, it should use a font by the chosen name for text + If NetHack can, it should use a font by the chosen name for text windows. font_size_map @@ -5535,19 +5574,6 @@ font_size_status If NetHack can, it should use this size font for the status window. - - - - NetHack 3.7.0 March 25, 2026 - - - - - - NetHack Guidebook 85 - - - font_size_text If NetHack can, it should use this size font for text windows. @@ -5556,8 +5582,8 @@ than in a window. guicolor - Use color text and/or highlighting attributes when displaying some - non-map data (such as menu selector letters). Curses interface + Use color text and/or highlighting attributes when displaying some + non-map data (such as menu selector letters). Curses interface only; default is on. large_font @@ -5567,17 +5593,29 @@ If NetHack can, it should display the map in the manner specified. player_selection - If NetHack can, it should pop up dialog boxes, or use prompts for + If NetHack can, it should pop up dialog boxes, or use prompts for character selection. popup_dialog If NetHack can, it should pop up dialog boxes for input. preload_tiles - If NetHack can, it should preload tiles into memory. For example, + If NetHack can, it should preload tiles into memory. For example, in the protected mode MS-DOS version, control whether tiles get pre- + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 86 + + + loaded into RAM at the start of the game. Doing so enhances perfor- - mance of the tile graphics, but uses more memory. (default on). + mance of the tile graphics, but uses more memory. (default on). Cannot be set with the `O' command. scroll_amount @@ -5594,81 +5632,43 @@ support this option. softkeyboard - Display an onscreen keyboard. Handhelds are most likely to support + Display an onscreen keyboard. Handhelds are most likely to support this option. splash_screen - If NetHack can, it should display an opening splash screen when it + If NetHack can, it should display an opening splash screen when it starts up (default yes). - - - - NetHack 3.7.0 March 25, 2026 - - - - - - NetHack Guidebook 86 - - - statuslines Number of lines for traditional below-the-map status display. Acceptable values are 2 and 3 (default is 2). When set to 3, the tty interface moves some fields around and mainly - shows status conditions on their own line. A display capable of - showing at least 25 lines is recommended. The value can be toggled + shows status conditions on their own line. A display capable of + showing at least 25 lines is recommended. The value can be toggled back and forth during the game with the `O' command. The curses interface does likewise if the align_status option is set to top or bottom but ignores statuslines when set to left or right. - The Qt interface already displays more than 3 lines for status so + The Qt interface already displays more than 3 lines for status so uses the statuslines value differently. A value of 3 renders status in the Qt interface's original format, with the status window spread - out vertically. A value of 2 makes status be slightly condensed, - moving some fields to different lines to eliminate one whole line, + out vertically. A value of 2 makes status be slightly condensed, + moving some fields to different lines to eliminate one whole line, reducing the height needed. (If NetHack has been built using a ver- - sion of Qt older than qt-5.9, statuslines can only be set in the - run-time configuration file or via NETHACKOPTIONS, not during play + sion of Qt older than qt-5.9, statuslines can only be set in the + run-time configuration file or via NETHACKOPTIONS, not during play with the `O' command.) term_cols and term_rows - Curses interface only. Number of columns and rows to use for the - display. Curses will attempt to resize to the values specified but - will settle for smaller sizes if they are too big. Default is the + Curses interface only. Number of columns and rows to use for the + display. Curses will attempt to resize to the values specified but + will settle for smaller sizes if they are too big. Default is the current window size. - tile_file - Specify the name of an alternative tile file to override the - default. - - Note: the X11 interface uses X resources rather than NetHack's - options to select an alternate tile file. See NetHack.ad, the sam- - ple X "application defaults" file. - - tile_height - Specify the preferred height of each tile in a tile capable port. - - tile_width - Specify the preferred width of each tile in a tile capable port - - tiled_map - If NetHack can, it should display the map using tiles graphics - rather than simple characters (letters and punctuation, possibly - augmented by line-drawing symbols). Setting tiled_map to True - forces ascii_map to be False. - - use_darkgray - Use bold black instead of blue for black glyphs (TTY only). - - - NetHack 3.7.0 March 25, 2026 @@ -5680,17 +5680,40 @@ + tile_file + Specify the name of an alternative tile file to override the + default. + + Note: the X11 interface uses X resources rather than NetHack's + options to select an alternate tile file. See NetHack.ad, the sam- + ple X "application defaults" file. + + tile_height + Specify the preferred height of each tile in a tile capable port. + + tile_width + Specify the preferred width of each tile in a tile capable port + + tiled_map + If NetHack can, it should display the map using tiles graphics + rather than simple characters (letters and punctuation, possibly + augmented by line-drawing symbols). Setting tiled_map to True + forces ascii_map to be False. + + use_darkgray + Use bold black instead of blue for black glyphs (TTY only). + use_inverse - If NetHack can, it should display inverse when the game specifies + If NetHack can, it should display inverse when the game specifies it. vary_msgcount - If NetHack can, it should display this number of messages at a time + If NetHack can, it should display this number of messages at a time in the message window. windowborders Whether to draw boxes around the map, status area, message area, and - persistent inventory window if enabled. Curses interface only. + persistent inventory window if enabled. Curses interface only. Acceptable values are 0 - off, never show borders @@ -5699,11 +5722,11 @@ 3 - on, except forced off for perm_invent 4 - auto, except forced off for perm_invent - (The 26x82 size threshold for `2' refers to number of rows and col- + (The 26x82 size threshold for `2' refers to number of rows and col- umns of the display. A width of at least 110 columns (80+2+26+2) is needed to show borders if align_status is set to left or right.) - The persistent inventory window, when enabled, can grow until it is + The persistent inventory window, when enabled, can grow until it is too big to fit on most displays, resulting in truncation of its con- tents. If borders are forced on (1) or the display is big enough to show them (2), setting the value to 3 or 4 instead will keep borders @@ -5711,29 +5734,6 @@ tional lines of inventory plus widen each inventory line by two col- umns. - windowcolors - If NetHack can, it should display all windows of a particular style - with the specified foreground and background colors. Windows GUI - and curses windowport only. The format is - - OPTION=windowcolors:style foreground/background - - where style is one of "menu", "message", "status", or "text", and - foreground and background are colors, either numeric (hash sign fol- - lowed by three pairs of hexadecimal digits, #rrggbb), one of the - named colors (black, red, green, brown, blue, magenta, cyan, orange, - bright-green, yellow, bright-blue, bright-magenta, bright-cyan, - white, gray, purple, silver, maroon, fuchsia, lime, olive, navy, - teal, aqua), or (for Windows only) one of Windows UI colors (true- - black, activeborder, activecaption, appworkspace, background, btn- - face, btnshadow, btntext, captiontext, graytext, greytext, high- - light, highlighttext, inactiveborder, inactivecaption, menu, menu- - text, scrollbar, window, windowframe, windowtext). - - wraptext - If NetHack can, it should wrap long lines of text if they don't fit - in the visible area of the window. - NetHack 3.7.0 March 25, 2026 @@ -5746,60 +5746,60 @@ + windowcolors + If NetHack can, it should display all windows of a particular style + with the specified foreground and background colors. Windows GUI + and curses windowport only. The format is + + OPTION=windowcolors:style foreground/background + + where style is one of "menu", "message", "status", or "text", and + foreground and background are colors, either numeric (hash sign fol- + lowed by three pairs of hexadecimal digits, #rrggbb), one of the + named colors (black, red, green, brown, blue, magenta, cyan, orange, + bright-green, yellow, bright-blue, bright-magenta, bright-cyan, + white, gray, purple, silver, maroon, fuchsia, lime, olive, navy, + teal, aqua), or (for Windows only) one of Windows UI colors (true- + black, activeborder, activecaption, appworkspace, background, btn- + face, btnshadow, btntext, captiontext, graytext, greytext, high- + light, highlighttext, inactiveborder, inactivecaption, menu, menu- + text, scrollbar, window, windowframe, windowtext). + + wraptext + If NetHack can, it should wrap long lines of text if they don't fit + in the visible area of the window. + 9.6. Crash Report Options - Please note that NetHack does not send any information off your + Please note that NetHack does not send any information off your computer unless you manually click submit on a form. OPTION=crash_email:email_address OPTION=crash_name:your_name - These options are used only to save you some typing on the + These options are used only to save you some typing on the crash report and #bugreport forms. OPTION=crash_urlmax:bytes - This option is used to limit the length of the URLs generated - and is only needed if your browser cannot handle arbitrarily + This option is used to limit the length of the URLs generated + and is only needed if your browser cannot handle arbitrarily long URLs. 9.7. Platform-specific Customization options - Here are explanations of options that are used by specific plat- + Here are explanations of options that are used by specific plat- forms or ports to customize and change the port behavior. altkeyhandling - Select an alternate way to handle keystrokes (Win32 tty NetHack - only). The name of the handling type is one of "default", "ray", + Select an alternate way to handle keystrokes (Win32 tty NetHack + only). The name of the handling type is one of "default", "ray", "340". altmeta - On systems where this option is available, it can be set to tell - NetHack to convert a two character sequence beginning with ESC into + On systems where this option is available, it can be set to tell + NetHack to convert a two character sequence beginning with ESC into a meta-shifted version of the second character (default off). - This conversion is only done for commands, not for other input - prompts. Note that typing one or more digits as a count prefix - prior to a command--preceded by n if the number_pad option is set-- - is also subject to this conversion, so attempting to abort the count - by typing ESC will leave NetHack waiting for another character to - complete the two character sequence. Type a second ESC to finish - cancelling such a count. At other prompts a single ESC suffices. - - BIOS - Use BIOS calls to update the screen display quickly and to read the - keyboard (allowing the use of arrow keys to move) on machines with - an IBM PC compatible BIOS ROM (default off, OS/2, PC, and ST NetHack - only). - - rawio - Force raw (non-cbreak) mode for faster output and more bulletproof - input (MS-DOS sometimes treats `^P' as a printer toggle without it) - (default off, OS/2, PC, and ST NetHack only). Note: DEC Rainbows - hang if this is turned on. Cannot be set with the `O' command. - - subkeyvalue - (Win32 tty NetHack only). May be used to alter the value of key- - strokes that the operating system returns to NetHack to help compen- NetHack 3.7.0 March 25, 2026 @@ -5812,19 +5812,42 @@ - sate for international keyboard issues. OPTIONS=subkeyvalue:171/92 - will return 92 to NetHack, if 171 was originally going to be - returned. You can use multiple subkeyvalue assignments in the con- + This conversion is only done for commands, not for other input + prompts. Note that typing one or more digits as a count prefix + prior to a command--preceded by n if the number_pad option is set-- + is also subject to this conversion, so attempting to abort the count + by typing ESC will leave NetHack waiting for another character to + complete the two character sequence. Type a second ESC to finish + cancelling such a count. At other prompts a single ESC suffices. + + BIOS + Use BIOS calls to update the screen display quickly and to read the + keyboard (allowing the use of arrow keys to move) on machines with + an IBM PC compatible BIOS ROM (default off, OS/2, PC, and ST NetHack + only). + + rawio + Force raw (non-cbreak) mode for faster output and more bulletproof + input (MS-DOS sometimes treats `^P' as a printer toggle without it) + (default off, OS/2, PC, and ST NetHack only). Note: DEC Rainbows + hang if this is turned on. Cannot be set with the `O' command. + + subkeyvalue + (Win32 tty NetHack only). May be used to alter the value of key- + strokes that the operating system returns to NetHack to help compen- + sate for international keyboard issues. OPTIONS=subkeyvalue:171/92 + will return 92 to NetHack, if 171 was originally going to be + returned. You can use multiple subkeyvalue assignments in the con- figuration file if needed. Cannot be set with the `O' command. video Set the video mode used (PC NetHack only). Values are "autodetect", - "default", "vga", or "vesa". Setting "vesa" will cause the game to - display tiles, using the full capability of the VGA hardware. Set- + "default", "vga", or "vesa". Setting "vesa" will cause the game to + display tiles, using the full capability of the VGA hardware. Set- ting "vga" will cause the game to display tiles, fixed at 640x480 in - 16 colors, a mode that is compatible with all VGA hardware. Third - party tilesets will probably not work. Setting "autodetect" - attempts "vesa", then "vga", and finally sets "default" if neither + 16 colors, a mode that is compatible with all VGA hardware. Third + party tilesets will probably not work. Setting "autodetect" + attempts "vesa", then "vga", and finally sets "default" if neither of those modes works. Cannot be set with the `O' command. video_height @@ -5834,38 +5857,15 @@ Set the VGA mode resolution width (MS-DOS only, with video:vesa) videocolors - Set the color palette for PC systems using NO_TERMS (default - 4-2-6-1-5-3-15-12-10-14-9-13-11, (PC NetHack only). The order of - colors is red, green, brown, blue, magenta, cyan, bright.white, - bright.red, bright.green, yellow, bright.blue, bright.magenta, and + Set the color palette for PC systems using NO_TERMS (default + 4-2-6-1-5-3-15-12-10-14-9-13-11, (PC NetHack only). The order of + colors is red, green, brown, blue, magenta, cyan, bright.white, + bright.red, bright.green, yellow, bright.blue, bright.magenta, and bright.cyan. Cannot be set with the `O' command. videoshades - Set the intensity level of the three gray scales available (default - dark normal light, PC NetHack only). If the game display is diffi- - cult to read, try adjusting these scales; if this does not correct - the problem, try !color. Cannot be set with the `O' command. - - 9.8. Regular Expressions - - Regular expressions are normally POSIX extended regular expres- - sions. It is possible to compile NetHack without regular expression - support on a platform where there is no regular expression library. - While this is not true of any modern platform, if your NetHack was - built this way, patterns are instead glob patterns; regardless, this - document refers to both as `regular expressions.' This applies to - Autopickup exceptions, Message types, Menu colors, and User sounds. - - 9.9. Configuring Autopickup Exceptions - - You can further refine the behavior of the autopickup option - beyond what is available through the pickup_types option. - - By placing autopickup_exception lines in your configuration file, - you can define patterns to be checked when the game is about to - autopickup something. - - + Set the intensity level of the three gray scales available (default + dark normal light, PC NetHack only). If the game display is diffi- NetHack 3.7.0 March 25, 2026 @@ -5878,24 +5878,46 @@ + cult to read, try adjusting these scales; if this does not correct + the problem, try !color. Cannot be set with the `O' command. + + 9.8. Regular Expressions + + Regular expressions are normally POSIX extended regular expres- + sions. It is possible to compile NetHack without regular expression + support on a platform where there is no regular expression library. + While this is not true of any modern platform, if your NetHack was + built this way, patterns are instead glob patterns; regardless, this + document refers to both as `regular expressions.' This applies to + Autopickup exceptions, Message types, Menu colors, and User sounds. + + 9.9. Configuring Autopickup Exceptions + + You can further refine the behavior of the autopickup option + beyond what is available through the pickup_types option. + + By placing autopickup_exception lines in your configuration file, + you can define patterns to be checked when the game is about to + autopickup something. + autopickup_exception Sets an exception to the pickup_types option. The autopickup_excep- tion option should be followed by a regular expression to be used as - a pattern to match against the singular form of the description of + a pattern to match against the singular form of the description of an object at your location. - In addition, some characters are treated specially if they occur as + In addition, some characters are treated specially if they occur as the first character in the pattern, specifically: < - always pickup an object that matches rest of pattern; > - never pickup an object that matches rest of pattern. - The autopickup_exception rules are processed in the order in which - they appear in your configuration file, thus allowing a later rule + The autopickup_exception rules are processed in the order in which + they appear in your configuration file, thus allowing a later rule to override an earlier rule. Exceptions can be set with the `O' command, but because they are not - included in your configuration file, they won't be in effect if you + included in your configuration file, they won't be in effect if you save and then restore your game. autopickup_exception rules and not saved with the game. @@ -5905,33 +5927,11 @@ autopickup_exception=">*corpse" autopickup_exception=">* cursed*" - The first example above will result in autopickup of any type of + The first example above will result in autopickup of any type of arrow. The second example results in the exclusion of any corpse from - autopickup. The last example results in the exclusion of items known + autopickup. The last example results in the exclusion of items known to be cursed from autopickup. - 9.10. Changing Key Bindings - - It is possible to change the default key bindings of some special - commands, menu accelerator keys, and extended commands, by using BIND - stanzas in the configuration file. Format is key, followed by the - command to bind to, separated by a colon. The key can be a single - character ("x"), a control key ("^X", "C-x"), a meta key ("M-x"), a - mouse button, or a three-digit decimal ASCII code. - - For example: - - BIND=^X:getpos.autodescribe - BIND=\:menu_first_page - BIND=v:loot - - Extended command keys - You can bind multiple keys to the same extended command. Unbind a - key by using "nothing" as the extended command to bind to. You can - also bind the "", "", and "" keys. - - Menu accelerator keys - The menu control or accelerator keys can also be rebound via OPTIONS NetHack 3.7.0 March 25, 2026 @@ -5944,60 +5944,60 @@ - lines in the configuration file. You cannot bind object symbols or + 9.10. Changing Key Bindings + + It is possible to change the default key bindings of some special + commands, menu accelerator keys, and extended commands, by using BIND + stanzas in the configuration file. Format is key, followed by the + command to bind to, separated by a colon. The key can be a single + character ("x"), a control key ("^X", "C-x"), a meta key ("M-x"), a + mouse button, or a three-digit decimal ASCII code. + + For example: + + BIND=^X:getpos.autodescribe + BIND=\:menu_first_page + BIND=v:loot + + Extended command keys + You can bind multiple keys to the same extended command. Unbind a + key by using "nothing" as the extended command to bind to. You can + also bind the "", "", and "" keys. + + Menu accelerator keys + The menu control or accelerator keys can also be rebound via OPTIONS + lines in the configuration file. You cannot bind object symbols or selection letters into menu accelerators. Some interfaces only sup- port some of the menu accelerators. Mouse buttons - You can bind "mouse1" or "mouse2" to "nothing", "therecmdmenu", + You can bind "mouse1" or "mouse2" to "nothing", "therecmdmenu", "clicklook", or "mouseaction". Special command keys - Below are the special commands you can rebind. Some of them can be - bound to same keys with no problems, others are in the same "con- + Below are the special commands you can rebind. Some of them can be + bound to same keys with no problems, others are in the same "con- text", and if bound to same keys, only one of those commands will be available. Special command can only be bound to a single key. count - Prefix key to start a count, to repeat a command this many times. + Prefix key to start a count, to repeat a command this many times. With number_pad only. Default is `n'. getdir.help - When asked for a direction, the key to show the help. Default is + When asked for a direction, the key to show the help. Default is `?'. getdir.mouse - When asked for a direction, the key to initiate a simulated mouse - click. You will be asked to pick a location. Use movement key- - strokes to move the cursor around the map, then type the get- + When asked for a direction, the key to initiate a simulated mouse + click. You will be asked to pick a location. Use movement key- + strokes to move the cursor around the map, then type the get- pos.pick.once key (default `,') or the getpos.pick key (default `.') - to finish as if performing a left or right click. Only useful when + to finish as if performing a left or right click. Only useful when using the #therecmdmenu command. Default is `_'. getdir.self - When asked for a direction, the key to target yourself. Default is - `.'. - - getdir.self2 - When asked for a direction, an alternate key to target yourself. - Default is `s'. - - getpos.autodescribe - When asked for a location, the key to toggle autodescribe. Default - is `#'. - - getpos.all.next - When asked for a location, the key to go to next closest interesting - thing. Default is `a'. - - getpos.all.prev - When asked for a location, the key to go to previous closest inter- - esting thing. Default is `A'. - - getpos.door.next - When asked for a location, the key to go to next closest door or - doorway. Default is `d'. - + When asked for a direction, the key to target yourself. Default is NetHack 3.7.0 March 25, 2026 @@ -6010,6 +6010,28 @@ + `.'. + + getdir.self2 + When asked for a direction, an alternate key to target yourself. + Default is `s'. + + getpos.autodescribe + When asked for a location, the key to toggle autodescribe. Default + is `#'. + + getpos.all.next + When asked for a location, the key to go to next closest interesting + thing. Default is `a'. + + getpos.all.prev + When asked for a location, the key to go to previous closest inter- + esting thing. Default is `A'. + + getpos.door.next + When asked for a location, the key to go to next closest door or + doorway. Default is `d'. + getpos.door.prev When asked for a location, the key to go to previous closest door or doorway. Default is `D'. @@ -6018,15 +6040,15 @@ When asked for a location, the key to show help. Default is `?'. getpos.mon.next - When asked for a location, the key to go to next closest monster. + When asked for a location, the key to go to next closest monster. Default is `m'. getpos.mon.prev - When asked for a location, the key to go to previous closest mon- + When asked for a location, the key to go to previous closest mon- ster. Default is `M'. getpos.obj.next - When asked for a location, the key to go to next closest object. + When asked for a location, the key to go to next closest object. Default is `o'. getpos.obj.prev @@ -6034,37 +6056,15 @@ Default is `O'. getpos.menu - When asked for a location, and using one of the next or previous - keys to cycle through targets, toggle showing a menu instead. + When asked for a location, and using one of the next or previous + keys to cycle through targets, toggle showing a menu instead. Default is `!'. getpos.moveskip - When asked for a location, and using the shifted movement keys or - meta-digit keys to fast-move around, move by skipping the same + When asked for a location, and using the shifted movement keys or + meta-digit keys to fast-move around, move by skipping the same glyphs instead of by 8 units. Default is `*'. - getpos.filter - When asked for a location, change the filtering mode when using one - of the next or previous keys to cycle through targets. Toggles - between no filtering, in view only, and in the same area only. - Default is `"'. - - getpos.pick - When asked for a location, the key to choose the location, and pos- - sibly ask for more info. When simulating a mouse click after being - asked for a direction (see getdir.mouse above), the key to use to - respond as right click. Default is `.'. - - getpos.pick.once - When asked for a location, the key to choose the location, and skip - asking for more info. When simulating a mouse click after being - asked for a direction, the key to respond as left click. Default is - `,'. - - getpos.pick.quick - When asked for a location, the key to choose the location, skip ask- - ing for more info, and exit the location asking loop. Default is - NetHack 3.7.0 March 25, 2026 @@ -6076,26 +6076,47 @@ + getpos.filter + When asked for a location, change the filtering mode when using one + of the next or previous keys to cycle through targets. Toggles + between no filtering, in view only, and in the same area only. + Default is `"'. + + getpos.pick + When asked for a location, the key to choose the location, and pos- + sibly ask for more info. When simulating a mouse click after being + asked for a direction (see getdir.mouse above), the key to use to + respond as right click. Default is `.'. + + getpos.pick.once + When asked for a location, the key to choose the location, and skip + asking for more info. When simulating a mouse click after being + asked for a direction, the key to respond as left click. Default is + `,'. + + getpos.pick.quick + When asked for a location, the key to choose the location, skip ask- + ing for more info, and exit the location asking loop. Default is `;'. getpos.pick.verbose - When asked for a location, the key to choose the location, and show + When asked for a location, the key to choose the location, and show more info without asking. Default is `:'. getpos.self - When asked for a location, the key to go to your location. Default + When asked for a location, the key to go to your location. Default is `@'. getpos.unexplored.next - When asked for a location, the key to go to next closest unexplored + When asked for a location, the key to go to next closest unexplored location. Default is `x'. getpos.unexplored.prev - When asked for a location, the key to go to previous closest unex- + When asked for a location, the key to go to previous closest unex- plored location. Default is `X'. getpos.valid - When asked for a location, the key to go to show valid target loca- + When asked for a location, the key to go to show valid target loca- tions. Default is `$'. getpos.valid.next @@ -6103,32 +6124,11 @@ tion. Default is `z'. getpos.valid.prev - When asked for a location, the key to go to previous closest valid + When asked for a location, the key to go to previous closest valid location. Default is `Z'. - 9.11. Configuring Message Types - You can change the way the messages are shown in the message - area, when the message matches a user-defined pattern. - In general, the configuration file entries to describe the mes- - sage types look like this: MSGTYPE=type "pattern" - - type - how the message should be shown; - pattern - the pattern to match. - - The pattern should be a regular expression. - - Allowed types are: - - show - show message normally; - hide - never show the message; - stop - wait for user with more-prompt; - norep - show the message once, but not again if no other message is - shown in between. - - Here's an example of message types using NetHack's internal pattern - matching facility: @@ -6142,11 +6142,35 @@ + 9.11. Configuring Message Types + + You can change the way the messages are shown in the message + area, when the message matches a user-defined pattern. + + In general, the configuration file entries to describe the mes- + sage types look like this: MSGTYPE=type "pattern" + + type - how the message should be shown; + pattern - the pattern to match. + + The pattern should be a regular expression. + + Allowed types are: + + show - show message normally; + hide - never show the message; + stop - wait for user with more-prompt; + norep - show the message once, but not again if no other message is + shown in between. + + Here's an example of message types using NetHack's internal pattern + matching facility: + MSGTYPE=stop "You feel hungry." MSGTYPE=hide "You displaced *." - specifies that whenever a message "You feel hungry" is shown, the - user is prompted with more-prompt, and a message matching "You dis- + specifies that whenever a message "You feel hungry" is shown, the + user is prompted with more-prompt, and a message matching "You dis- placed ." is not shown at all. The order of the defined MSGTYPE lines is important; the last match- @@ -6155,47 +6179,23 @@ 9.12. Configuring Menu Colors Some platforms allow you to define colors used in menu lines when - the line matches a user-defined pattern. At this time the tty, + the line matches a user-defined pattern. At this time the tty, curses, win32tty and win32gui interfaces support this. - In general, the configuration file entries to describe the menu + In general, the configuration file entries to describe the menu color mappings look like this: MENUCOLOR="pattern"=color&attribute pattern - the pattern to match; color - the color to use for lines matching the pattern; - attribute - the attribute to use for lines matching the pat- - tern. The attribute is optional, and if left out, - you must also leave out the preceding ampersand. + attribute - the attribute to use for lines matching the pat- + tern. The attribute is optional, and if left out, + you must also leave out the preceding ampersand. If no attribute is defined, no attribute is used. The pattern should be a regular expression. - Allowed colors are black, red, green, brown, blue, magenta, cyan, - gray, orange, light-green, yellow, light-blue, light-magenta, light- - cyan, and white. And no-color, the default foreground color, which - isn't necessarily the same as any of the other colors. - - Allowed attributes are none, bold, dim, italic, underline, blink, - and inverse. "Normal" is a synonym for "none". Note that the plat- - form used may interpret the attributes any way it wants. - - Here's an example of menu colors using NetHack's internal pattern - matching facility: - - MENUCOLOR="* blessed *"=green - MENUCOLOR="* cursed *"=red - MENUCOLOR="* cursed *(being worn)"=red&underline - - specifies that any menu line with " blessed " contained in it will - be shown in green color, lines with " cursed " will be shown in red, - and lines with " cursed " followed by "(being worn)" on the same - line will be shown in red color and underlined. You can have multi- - ple MENUCOLOR entries in your configuration file, and the last MENU- - COLOR line that matches a menu line will be used for the line. - - NetHack 3.7.0 March 25, 2026 @@ -6208,19 +6208,42 @@ - Note that if you intend to have one or more color specifications + Allowed colors are black, red, green, brown, blue, magenta, cyan, + gray, orange, light-green, yellow, light-blue, light-magenta, light- + cyan, and white. And no-color, the default foreground color, which + isn't necessarily the same as any of the other colors. + + Allowed attributes are none, bold, dim, italic, underline, blink, + and inverse. "Normal" is a synonym for "none". Note that the plat- + form used may interpret the attributes any way it wants. + + Here's an example of menu colors using NetHack's internal pattern + matching facility: + + MENUCOLOR="* blessed *"=green + MENUCOLOR="* cursed *"=red + MENUCOLOR="* cursed *(being worn)"=red&underline + + specifies that any menu line with " blessed " contained in it will + be shown in green color, lines with " cursed " will be shown in red, + and lines with " cursed " followed by "(being worn)" on the same + line will be shown in red color and underlined. You can have multi- + ple MENUCOLOR entries in your configuration file, and the last MENU- + COLOR line that matches a menu line will be used for the line. + + Note that if you intend to have one or more color specifications match " uncursed ", you will probably want to turn the - implicit_uncursed option off so that all items known to be uncursed + implicit_uncursed option off so that all items known to be uncursed are actually displayed with the "uncursed" description. 9.13. Configuring User Sounds - Some platforms allow you to define sound files to be played when + Some platforms allow you to define sound files to be played when a message that matches a user-defined pattern is delivered to the mes- - sage window. At this time the Qt port and the win32tty and win32gui + sage window. At this time the Qt port and the win32tty and win32gui ports support the use of user sounds. - The following configuration file entries are relevant to mapping + The following configuration file entries are relevant to mapping user sounds to messages: SOUNDDIR @@ -6230,15 +6253,27 @@ An entry that maps a sound file to a user-specified message pattern. Each SOUND entry is broken down into the following parts: - MESG - message window mapping (the only one supported in + MESG - message window mapping (the only one supported in 3.7.0); - msgtype - optional; message type to use, see "Configuring Mes- + msgtype - optional; message type to use, see "Configuring Mes- sage Types" pattern - the pattern to match; sound file - the sound file to play; volume - the volume to be set while playing the sound file; sound index - optional; the index corresponding to a sound file. + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 96 + + + The pattern should be a regular expression. For example: @@ -6251,17 +6286,48 @@ 9.14. Configuring Status Hilites - Your copy of NetHack may have been compiled with support for - "Status Hilites". If so, you can customize your game display by set- - ting thresholds to change the color or appearance of fields in the + Your copy of NetHack may have been compiled with support for + "Status Hilites". If so, you can customize your game display by set- + ting thresholds to change the color or appearance of fields in the status display. The format for defining status colors is: OPTION=hilite_status:field-name/behavior/color&attributes - For example, the following line in your configuration file will - cause the hitpoints field to display in the color red if your hit- + For example, the following line in your configuration file will + cause the hitpoints field to display in the color red if your hit- + points drop to or below a threshold of 30%: + + OPTION=hilite_status:hitpoints/<=30%/red/normal + + (That example is actually specifying red&normal for <=30% and no- + color&normal for >30%.) + + For another example, the following line in your configuration + file will cause wisdom to be displayed red if it drops and green if it + rises: + + OPTION=hilite_status:wisdom/down/red/up/green + + Allowed colors are black, red, green, brown, blue, magenta, cyan, + gray, orange, light-green, yellow, light-blue, light-magenta, light- + cyan, and white. And "no-color", the default foreground color on the + display, which is not necessarily the same as black or white or any of + the other colors. + + Allowed attributes are none, bold, dim, underline, italic, blink, + and inverse. "Normal" is a synonym for "none"; they should not be + used in combination with any of the other attributes. + + To specify both a color and an attribute, use `&' to combine + them. To specify multiple attributes, use `+' to combine those. For + example: "magenta&inverse+dim". + + Note that the display may substitute or ignore particular + attributes depending upon its capabilities, and in general may inter- + pret the attributes any way it wants. For example, on some display + systems a request for bold might yield blink or vice versa. On oth- NetHack 3.7.0 March 25, 2026 @@ -6270,44 +6336,13 @@ - NetHack Guidebook 96 + NetHack Guidebook 97 - points drop to or below a threshold of 30%: - - OPTION=hilite_status:hitpoints/<=30%/red/normal - - (That example is actually specifying red&normal for <=30% and no- - color&normal for >30%.) - - For another example, the following line in your configuration - file will cause wisdom to be displayed red if it drops and green if it - rises: - - OPTION=hilite_status:wisdom/down/red/up/green - - Allowed colors are black, red, green, brown, blue, magenta, cyan, - gray, orange, light-green, yellow, light-blue, light-magenta, light- - cyan, and white. And "no-color", the default foreground color on the - display, which is not necessarily the same as black or white or any of - the other colors. - - Allowed attributes are none, bold, dim, underline, italic, blink, - and inverse. "Normal" is a synonym for "none"; they should not be - used in combination with any of the other attributes. - - To specify both a color and an attribute, use `&' to combine - them. To specify multiple attributes, use `+' to combine those. For - example: "magenta&inverse+dim". - - Note that the display may substitute or ignore particular - attributes depending upon its capabilities, and in general may inter- - pret the attributes any way it wants. For example, on some display - systems a request for bold might yield blink or vice versa. On oth- ers, issuing an attribute request while another is already set up will - replace the earlier attribute rather than combine with it. Since - NetHack issues attribute requests sequentially (at least with the tty + replace the earlier attribute rather than combine with it. Since + NetHack issues attribute requests sequentially (at least with the tty interface) rather than all at once, the only way a situation like that can be controlled is to specify just one attribute. @@ -6321,79 +6356,44 @@ charisma armor-class condition alignment score - The pseudo-field "characteristics" can be used to set all six of - Str, Dex, Con, Int, Wis, and Cha at once. "HD" is "hit dice", an - approximation of experience level displayed when polymorphed. - "experience", "time", and "score" are conditionally displayed + The pseudo-field "characteristics" can be used to set all six of + Str, Dex, Con, Int, Wis, and Cha at once. "HD" is "hit dice", an + approximation of experience level displayed when polymorphed. + "experience", "time", and "score" are conditionally displayed depending upon your other option settings. - - - - NetHack 3.7.0 March 25, 2026 - - - - - - NetHack Guidebook 97 - - - - Instead of a behavior, "condition" takes the following condition - flags: stone, slime, strngl, foodpois, termill, blind, deaf, stun, + Instead of a behavior, "condition" takes the following condition + flags: stone, slime, strngl, foodpois, termill, blind, deaf, stun, conf, hallu, lev, fly, and ride. You can use "major_troubles" as an - alias for stone through termill, "minor_troubles" for blind through + alias for stone through termill, "minor_troubles" for blind through hallu, "movement" for lev, fly, and ride, and "all" for every condi- tion. - Allowed behaviors are "always", "up", "down", "changed", a percent- + Allowed behaviors are "always", "up", "down", "changed", a percent- age or absolute number threshold, or text to match against. For the - hitpoints field, the additional behavior "criticalhp" is available. - It overrides other behavior rules if hit points are at or below the - major problem threshold (which varies depending upon maximum hit + hitpoints field, the additional behavior "criticalhp" is available. + It overrides other behavior rules if hit points are at or below the + major problem threshold (which varies depending upon maximum hit points and experience level). * "always" will set the default attributes for that field. - * "up", "down" set the field attributes for when the field value - changes upwards or downwards. This attribute times out after + * "up", "down" set the field attributes for when the field value + changes upwards or downwards. This attribute times out after statushilites turns. - * "changed" sets the field attribute for when the field value - changes. This attribute times out after statushilites turns. - (If a field has both a "changed" rule and an "up" or "down" - rule which matches a change in the field's value, the "up" or + * "changed" sets the field attribute for when the field value + changes. This attribute times out after statushilites turns. + (If a field has both a "changed" rule and an "up" or "down" + rule which matches a change in the field's value, the "up" or "down" one takes precedence.) - * percentage sets the field attribute when the field value - matches the percentage. It is specified as a number between 0 - and 100, followed by `%' (percent sign). If the percentage is + * percentage sets the field attribute when the field value + matches the percentage. It is specified as a number between 0 + and 100, followed by `%' (percent sign). If the percentage is prefixed with `<=' or `>=', it also matches when value is below - or above the percentage. Use prefix `<' or `>' to match when + or above the percentage. Use prefix `<' or `>' to match when strictly below or above. (The numeric limit is relaxed - slightly for those: >-1% and <101% are allowed.) Only four - fields support percentage rules. Percentages for "hitpoints" - and "power" are straightforward; they're based on the corre- - sponding maximum field. Percentage highlight rules are also - allowed for "experience level" and "experience points" (valid - when the showexp option is enabled). For those, the percentage - is based on the progress from the start of the current experi- - ence level to the start of the next level. So if level 2 - starts at 20 points and level 3 starts at 40 points, having 30 - points is 50% and 35 points is 75%. 100% is unattainable for - experience because you'll gain a level and the calculations - will be reset for that new level, but a rule for =100% is - allowed and matches the special case of being exactly 1 experi- - ence point short of the next level. - - * absolute value sets the attribute when the field value matches - that number. The number must be 0 or higher, except for - "armor-class' which allows negative values, and may optionally - be preceded by `='. If the number is preceded by `<=' or `>=' - instead, it also matches when value is below or above. If the - prefix is `<' or `>', only match when strictly above or below. - NetHack 3.7.0 March 25, 2026 @@ -6406,19 +6406,41 @@ - * criticalhp only applies to the hitpoints field and only when + slightly for those: >-1% and <101% are allowed.) Only four + fields support percentage rules. Percentages for "hitpoints" + and "power" are straightforward; they're based on the corre- + sponding maximum field. Percentage highlight rules are also + allowed for "experience level" and "experience points" (valid + when the showexp option is enabled). For those, the percentage + is based on the progress from the start of the current experi- + ence level to the start of the next level. So if level 2 + starts at 20 points and level 3 starts at 40 points, having 30 + points is 50% and 35 points is 75%. 100% is unattainable for + experience because you'll gain a level and the calculations + will be reset for that new level, but a rule for =100% is + allowed and matches the special case of being exactly 1 experi- + ence point short of the next level. + + * absolute value sets the attribute when the field value matches + that number. The number must be 0 or higher, except for + "armor-class' which allows negative values, and may optionally + be preceded by `='. If the number is preceded by `<=' or `>=' + instead, it also matches when value is below or above. If the + prefix is `<' or `>', only match when strictly above or below. + + * criticalhp only applies to the hitpoints field and only when current hit points are below a threshold (which varies by maxi- - mum hit points and experience level). When the threshold is - met, a criticalhp rule takes precedence over all other hit- + mum hit points and experience level). When the threshold is + met, a criticalhp rule takes precedence over all other hit- points rules. - * text match sets the attribute when the field value matches the - text. Text matches can only be used for "alignment", "carry- - ing-capacity", "hunger", "dungeon-level", and "title". For - title, only the role's rank title is tested; the character's + * text match sets the attribute when the field value matches the + text. Text matches can only be used for "alignment", "carry- + ing-capacity", "hunger", "dungeon-level", and "title". For + title, only the role's rank title is tested; the character's name is ignored. - The in-game options menu can help you determine the correct syn- + The in-game options menu can help you determine the correct syn- tax for a configuration file. The whole feature can be disabled by setting option statushilites @@ -6439,28 +6461,6 @@ - 9.15. Modifying NetHack Symbols - - NetHack can load entire symbol sets from the symbol file. - - The options that are used to select a particular symbol set from - the symbol file are: - - symset - Set the name of the symbol set that you want to load. - - roguesymset - Set the name of the symbol set that you want to load for display on - the rogue level. - - You can also override one or more symbols using the SYMBOLS and - ROGUESYMBOLS configuration file options. Symbols are specified as - name:value pairs. Note that NetHack escape-processes the value string - in conventional C fashion. This means that \ is a prefix to take the - following character literally. Thus \ needs to be represented as \\. - The special prefix form \m switches on the meta bit in the symbol - value, and the ^ prefix causes the following character to be treated - NetHack 3.7.0 March 25, 2026 @@ -6472,6 +6472,27 @@ + 9.15. Modifying NetHack Symbols + + NetHack can load entire symbol sets from the symbol file. + + The options that are used to select a particular symbol set from + the symbol file are: + + symset + Set the name of the symbol set that you want to load. + + roguesymset + Set the name of the symbol set that you want to load for display on + the rogue level. + + You can also override one or more symbols using the SYMBOLS and + ROGUESYMBOLS configuration file options. Symbols are specified as + name:value pairs. Note that NetHack escape-processes the value string + in conventional C fashion. This means that \ is a prefix to take the + following character literally. Thus \ needs to be represented as \\. + The special prefix form \m switches on the meta bit in the symbol + value, and the ^ prefix causes the following character to be treated as a control character. NetHack Symbols @@ -6504,6 +6525,19 @@ C S_centaur (centaur) _ S_chain (iron chain) # S_cloud (cloud) + + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 100 + + + c S_cockatrice (cockatrice) $ S_coin (pile of coins) # S_corr (corridor) @@ -6525,19 +6559,6 @@ \ S_expl_tr (explosion top right) | S_expl_ml (explosion middle left) S_expl_mc (explosion middle center) - - - - NetHack 3.7.0 March 25, 2026 - - - - - - NetHack Guidebook 100 - - - | S_expl_mr (explosion middle right) \ S_expl_bl (explosion bottom left) - S_expl_bc (explosion bottom center) @@ -6570,6 +6591,19 @@ i S_imp (imp or minor demon) I S_invisible (invisible monster) J S_jabberwock (jabberwock) + + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 101 + + + j S_jelly (jelly) k S_kobold (kobold) K S_kop (Keystone Kop) @@ -6591,19 +6625,6 @@ N S_naga (naga) . S_ndoor (doorway without door) n S_nymph (nymph) - - - - NetHack 3.7.0 March 25, 2026 - - - - - - NetHack Guidebook 101 - - - O S_ogre (ogre) o S_orc (orc) p S_piercer (piercer) @@ -6636,6 +6657,19 @@ * S_ss4 (magic shield 4 of 4) ^ S_statue_trap (statue trap) S_stone (solid rock) + + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 102 + + + ] S_strange_obj (strange object) - S_sw_bc (swallow bottom center) \ S_sw_bl (swallow bottom left) @@ -6657,19 +6691,6 @@ # S_tree (tree) T S_troll (troll) | S_trwall (wall) - - - - NetHack 3.7.0 March 25, 2026 - - - - - - NetHack Guidebook 102 - - - - S_tuwall (wall) U S_umber (umber hulk) S_unexplored (unexplored terrain) @@ -6703,27 +6724,6 @@ Notes: - * Several symbols in this table appear to be blank. They are the - space character, except for S_pet_override and S_hero_override which - don't have any default value and can only be used if enabled in the - "sysconf" file. - - * S_rock is misleadingly named; rocks and stones use S_gem. Statues - and boulders are the rock being referred to, but since version - 3.6.0, statues are displayed as the monster they depict. So S_rock - is only used for boulders and not used at all if overridden by the - more specific S_boulder. - - 9.16. Customizing Map Glyph Representations Using Unicode - - If your platform or terminal supports the display of UTF-8 char- - acter sequences, you can customize your game display by assigning Uni- - code codepoint values and red-green-blue colors to glyph representa- - tions. The customizations can be specified for use with a symset that - has a UTF8 handler within the symbols file such as the enhanced1 set, - or individually within your nethack.rc file. - - NetHack 3.7.0 March 25, 2026 @@ -6736,60 +6736,60 @@ + * Several symbols in this table appear to be blank. They are the + space character, except for S_pet_override and S_hero_override which + don't have any default value and can only be used if enabled in the + "sysconf" file. + + * S_rock is misleadingly named; rocks and stones use S_gem. Statues + and boulders are the rock being referred to, but since version + 3.6.0, statues are displayed as the monster they depict. So S_rock + is only used for boulders and not used at all if overridden by the + more specific S_boulder. + + 9.16. Customizing Map Glyph Representations Using Unicode + + If your platform or terminal supports the display of UTF-8 char- + acter sequences, you can customize your game display by assigning Uni- + code codepoint values and red-green-blue colors to glyph representa- + tions. The customizations can be specified for use with a symset that + has a UTF8 handler within the symbols file such as the enhanced1 set, + or individually within your nethack.rc file. + The format for defining a glyph representation is: OPTIONS=glyph:glyphid/U+nnnn/R-G-B - The window port that is active needs to provide support for dis- - playing UTF-8 character sequences and explicit red-green-blue colors + The window port that is active needs to provide support for dis- + playing UTF-8 character sequences and explicit red-green-blue colors in order for the glyph representation to be visible. For example, the - following line in your configuration file will cause the glyph repre- - sentation for glyphid G_pool to use Unicode codepoint U+224B and the + following line in your configuration file will cause the glyph repre- + sentation for glyphid G_pool to use Unicode codepoint U+224B and the color represented by R-G-B value 0-0-160: OPTIONS=glyph:G_pool/U+224B/0-0-160 - The list of acceptable glyphid's can be produced by nethack --dumpg- + The list of acceptable glyphid's can be produced by nethack --dumpg- lyphids. Individual NetHack glyphs can be specified using the G_ pre- - fix, or you can use an S_ symbol for a glyphid and store the custom - representation for all NetHack glyphs that would map to that particu- + fix, or you can use an S_ symbol for a glyphid and store the custom + representation for all NetHack glyphs that would map to that particu- lar symbol. - You will need to select a symset with a UTF8 handler to enable + You will need to select a symset with a UTF8 handler to enable the display of the customizations, such as the Enhanced symset. 9.17. Configuring NetHack for Play by the Blind - NetHack can be set up to use only standard ASCII characters for - making maps of the dungeons. This makes even the MS-DOS versions of - NetHack (which use special line-drawing characters by default) com- - pletely accessible to the blind who use speech and/or Braille access - technologies. Players will require a good working knowledge of their + NetHack can be set up to use only standard ASCII characters for + making maps of the dungeons. This makes even the MS-DOS versions of + NetHack (which use special line-drawing characters by default) com- + pletely accessible to the blind who use speech and/or Braille access + technologies. Players will require a good working knowledge of their screen-reader's review features, and will have to know how to navigate - horizontally and vertically character by character. They will also + horizontally and vertically character by character. They will also find the search capabilities of their screen-readers to be quite valu- - able. Be certain to examine this Guidebook before playing so you have + able. Be certain to examine this Guidebook before playing so you have an idea what the screen layout is like. You'll also need to be able to - locate the PC cursor. It is always where your character is located. - Merely searching for an @-sign will not always find your character - since there are other humanoids represented by the same sign. Your - screen-reader should also have a function which gives you the row and - column of your review cursor and the PC cursor. These co-ordinates - are often useful in giving players a better sense of the overall loca- - tion of items on the screen. - - NetHack can also be compiled with support for sending the game - messages to an external program, such as a text-to-speech synthesizer. - If the "#version" extended command shows "external program as a mes- - sage handler", your NetHack has been compiled with the capability. - When compiling NetHack from source on Linux and other POSIX systems, - define MSGHANDLER to enable it. To use the capability, set the envi- - ronment variable NETHACK_MSGHANDLER to an executable, which will be - executed with the game message as the program's only parameter. - - The most crucial settings to make the game more accessible are: - - NetHack 3.7.0 March 25, 2026 @@ -6802,6 +6802,25 @@ + locate the PC cursor. It is always where your character is located. + Merely searching for an @-sign will not always find your character + since there are other humanoids represented by the same sign. Your + screen-reader should also have a function which gives you the row and + column of your review cursor and the PC cursor. These co-ordinates + are often useful in giving players a better sense of the overall loca- + tion of items on the screen. + + NetHack can also be compiled with support for sending the game + messages to an external program, such as a text-to-speech synthesizer. + If the "#version" extended command shows "external program as a mes- + sage handler", your NetHack has been compiled with the capability. + When compiling NetHack from source on Linux and other POSIX systems, + define MSGHANDLER to enable it. To use the capability, set the envi- + ronment variable NETHACK_MSGHANDLER to an executable, which will be + executed with the game message as the program's only parameter. + + The most crucial settings to make the game more accessible are: + symset:plain Load a symbol set appropriate for use by blind players. @@ -6812,8 +6831,8 @@ Show menus on a cleared screen and aligned to the left edge. number_pad - A lot of speech access programs use the number-pad to review the - screen. If this is the case, disable the number_pad option and use + A lot of speech access programs use the number-pad to review the + screen. If this is the case, disable the number_pad option and use the traditional Rogue-like commands. paranoid_confirmation:swim @@ -6823,11 +6842,11 @@ Adds direction or location information to messages. spot_monsters - Shows a message when hero notices a monster; combine with accessi- + Shows a message when hero notices a monster; combine with accessi- blemsg. mon_movement - Shows a message when hero notices a monster movement; combine with + Shows a message when hero notices a monster movement; combine with spot_monsters and accessiblemsg. autodescribe @@ -6836,26 +6855,7 @@ mention_map Give feedback messages when interesting map locations change. - mention_walls - Give feedback messages when walking towards a wall or when travel - command was interrupted. - whatis_coord:compass - When targeting with cursor, describe the cursor position with coor- - dinates relative to your character. - - whatis_filter:area - When targeting with cursor, filter possible locations so only those - in the same area (eg. same room, or same corridor) are considered. - - whatis_moveskip - When targeting with cursor and using fast-move, skip the same glyphs - instead of moving 8 units at a time. - - nostatus_updates - Prevent updates to the status lines at the bottom of the screen, if - your screen-reader reads those lines. The same information can be - seen via the "#attributes" command. NetHack 3.7.0 March 25, 2026 @@ -6868,61 +6868,61 @@ + mention_walls + Give feedback messages when walking towards a wall or when travel + command was interrupted. + + whatis_coord:compass + When targeting with cursor, describe the cursor position with coor- + dinates relative to your character. + + whatis_filter:area + When targeting with cursor, filter possible locations so only those + in the same area (eg. same room, or same corridor) are considered. + + whatis_moveskip + When targeting with cursor and using fast-move, skip the same glyphs + instead of moving 8 units at a time. + + nostatus_updates + Prevent updates to the status lines at the bottom of the screen, if + your screen-reader reads those lines. The same information can be + seen via the "#attributes" command. + showdamage Give a message of damage taken and how many hit points are left. 9.18. Global Configuration for System Administrators - If NetHack is compiled with the SYSCF option, a system adminis- - trator should set up a global configuration; this is a file in the - same format as the traditional per-user configuration file (see - above). This file should be named sysconf and placed in the same - directory as the other NetHack support files. The options recognized - in this file are listed below. Any option not set uses a compiled-in + If NetHack is compiled with the SYSCF option, a system adminis- + trator should set up a global configuration; this is a file in the + same format as the traditional per-user configuration file (see + above). This file should be named sysconf and placed in the same + directory as the other NetHack support files. The options recognized + in this file are listed below. Any option not set uses a compiled-in default (which may not be appropriate for your system). - WIZARDS = A space-separated list of user names who are allowed to - play in debug mode (commonly referred to as wizard mode). A value - of a single asterisk (*) allows anyone to start a game in debug + WIZARDS = A space-separated list of user names who are allowed to + play in debug mode (commonly referred to as wizard mode). A value + of a single asterisk (*) allows anyone to start a game in debug mode. - SHELLERS = A list of users who are allowed to use the shell escape + SHELLERS = A list of users who are allowed to use the shell escape command (!). The syntax is the same as WIZARDS. EXPLORERS = A list of users who are allowed to use the explore mode. The syntax is the same as WIZARDS. MSGHANDLER = A path and filename of executable. Whenever a message- - window message is shown, NetHack runs this program. The program + window message is shown, NetHack runs this program. The program will get the message as the only parameter. - MAXPLAYERS = Limit the maximum number of games that can be running + MAXPLAYERS = Limit the maximum number of games that can be running at the same time. - SUPPORT = A string explaining how to get local support (no default + SUPPORT = A string explaining how to get local support (no default value). - RECOVER = A string explaining how to recover a game on this system - (no default value). - - SEDUCE = 0 or 1 to disable or enable, respectively, the SEDUCE - option. When disabled, incubi and succubi behave like nymphs. - - CHECK_PLNAME = Setting this to 1 will make the EXPLORERS, WIZARDS, - and SHELLERS check for the player name instead of the user's login - name. - - CHECK_SAVE_UID = 0 or 1 to disable or enable, respectively, the UID - (used identification number) checking for save files (to verify that - the user who is restoring is the same one who saved). - - The following four options affect the score file: - - PERSMAX = Maximum number of entries for one person. - - ENTRYMAX = Maximum number of entries in the score file. - - NetHack 3.7.0 March 25, 2026 @@ -6934,28 +6934,48 @@ - POINTSMIN = Minimum number of points to get an entry in the score + RECOVER = A string explaining how to recover a game on this system + (no default value). + + SEDUCE = 0 or 1 to disable or enable, respectively, the SEDUCE + option. When disabled, incubi and succubi behave like nymphs. + + CHECK_PLNAME = Setting this to 1 will make the EXPLORERS, WIZARDS, + and SHELLERS check for the player name instead of the user's login + name. + + CHECK_SAVE_UID = 0 or 1 to disable or enable, respectively, the UID + (used identification number) checking for save files (to verify that + the user who is restoring is the same one who saved). + + The following four options affect the score file: + + PERSMAX = Maximum number of entries for one person. + + ENTRYMAX = Maximum number of entries in the score file. + + POINTSMIN = Minimum number of points to get an entry in the score file. - PERS_IS_UID = 0 or 1 to use user names or numeric userids, respec- + PERS_IS_UID = 0 or 1 to use user names or numeric userids, respec- tively, to identify unique people for the score file. - HIDEUSAGE = 0 or 1 to control whether the help menu entry for com- + HIDEUSAGE = 0 or 1 to control whether the help menu entry for com- mand line usage is shown or suppressed. - MAX_STATUENAME_RANK = Maximum number of score file entries to use + MAX_STATUENAME_RANK = Maximum number of score file entries to use for random statue names (default is 10). ACCESSIBILITY = 0 or 1 to disable or enable, respectively, the abil- ity for players to set S_pet_override and S_hero_override symbols in their configuration file. - PORTABLE_DEVICE_PATHS = 0 or 1 Windows OS only, the game will look - for all of its external files, and write to all of its output files + PORTABLE_DEVICE_PATHS = 0 or 1 Windows OS only, the game will look + for all of its external files, and write to all of its output files in one place rather than at the standard locations. - DUMPLOGFILE = A filename where the end-of-game dumplog is saved. - Not defining this will prevent dumplog from being created. Only + DUMPLOGFILE = A filename where the end-of-game dumplog is saved. + Not defining this will prevent dumplog from being created. Only available if your game is compiled with DUMPLOG. Allows the follow- ing placeholders: @@ -6969,26 +6989,6 @@ %n - player name %N - first character of player name - LIVELOG = A bit-mask of types of events that should be written to - the livelog file if one is present. The sample sysconf file accom- - panying the program contains a comment which lists the meaning of - the various bits used. Intended for server systems supporting - simultaneous play by multiple players (to be clear, each one running - a separate single player game), for displaying their game progress - to observers. Only relevant if the program was built with LIVELOG - enabled. When available, it should be left commented out on single - player installations because over time the file could grow to be - extremely large unless it is actively maintained. - - CRASHREPORTURL = If set to - https://www.nethack.org/links/cr-37BETA.html and support is compiled - in, brings up a browser window pre-populated with the information - needed to report a problem if the game panics or ends up in an - internally inconsistent state, or if the #bugreport command is - invoked. - - - NetHack 3.7.0 March 25, 2026 @@ -7000,60 +7000,60 @@ + LIVELOG = A bit-mask of types of events that should be written to + the livelog file if one is present. The sample sysconf file accom- + panying the program contains a comment which lists the meaning of + the various bits used. Intended for server systems supporting + simultaneous play by multiple players (to be clear, each one running + a separate single player game), for displaying their game progress + to observers. Only relevant if the program was built with LIVELOG + enabled. When available, it should be left commented out on single + player installations because over time the file could grow to be + extremely large unless it is actively maintained. + + CRASHREPORTURL = If set to + https://www.nethack.org/links/cr-37BETA.html and support is compiled + in, brings up a browser window pre-populated with the information + needed to report a problem if the game panics or ends up in an + internally inconsistent state, or if the #bugreport command is + invoked. + 10. Scoring - NetHack maintains a list of the top scores or scorers on your - machine, depending on how it is set up. In the latter case, each - account on the machine can post only one non-winning score on this - list. If you score higher than someone else on this list, or better - your previous score, you will be inserted in the proper place under - your current name. How many scores are kept can also be set up when + NetHack maintains a list of the top scores or scorers on your + machine, depending on how it is set up. In the latter case, each + account on the machine can post only one non-winning score on this + list. If you score higher than someone else on this list, or better + your previous score, you will be inserted in the proper place under + your current name. How many scores are kept can also be set up when NetHack is compiled. - Your score is chiefly based upon how much experience you gained, + Your score is chiefly based upon how much experience you gained, how much loot you accumulated, how deep you explored, and how the game ended. If you quit the game, you escape with all of your gold intact. - If, however, you get killed in the Mazes of Menace, the guild will - only hear about 90% of your gold when your corpse is discovered - (adventurers have been known to collect finder's fees). So, consider - whether you want to take one last hit at that monster and possibly - live, or quit and stop with whatever you have. If you quit, you keep + If, however, you get killed in the Mazes of Menace, the guild will + only hear about 90% of your gold when your corpse is discovered + (adventurers have been known to collect finder's fees). So, consider + whether you want to take one last hit at that monster and possibly + live, or quit and stop with whatever you have. If you quit, you keep all your gold, but if you swing and live, you might find more. - If you just want to see what the current top players/games list + If you just want to see what the current top players/games list is, you can type nethack -s all on most versions. 11. Explore mode NetHack is an intricate and difficult game. Novices might falter in fear, aware of their ignorance of the means to survive. Well, fear - not. Your dungeon comes equipped with an "explore" or "discovery" - mode that enables you to keep old save files and cheat death, at the + not. Your dungeon comes equipped with an "explore" or "discovery" + mode that enables you to keep old save files and cheat death, at the paltry cost of not getting on the high score list. There are two ways of enabling explore mode. One is to start the - game with the -X command-line switch or with the playmode:explore - option. The other is to issue the "#exploremode" extended command - while already playing the game. Starting a new game in explore mode - provides your character with a wand of wishing in initial inventory; - switching during play does not. The other benefits of explore mode - are left for the trepid reader to discover. - - 11.1. Debug mode - - Debug mode, also known as wizard mode, is undocumented aside from - this brief description and the various "debug mode only" commands - listed among the command descriptions. It is intended for tracking - down problems within the program rather than to provide god-like pow- - ers to your character, and players who attempt debugging are expected - to figure out how to use it themselves. It is initiated by starting - the game with the -D command-line switch or with the playmode:debug - option. - - For some systems, the player must be logged in under a particular - user name to be allowed to use debug mode; for others, the hero must - be given a particular character name (but may be any role; there's no - connection between "wizard mode" and the Wizard role). Attempting to + game with the -X command-line switch or with the playmode:explore + option. The other is to issue the "#exploremode" extended command + while already playing the game. Starting a new game in explore mode + provides your character with a wand of wishing in initial inventory; NetHack 3.7.0 March 25, 2026 @@ -7066,60 +7066,60 @@ - start a game in debug mode when not allowed or not available will + switching during play does not. The other benefits of explore mode + are left for the trepid reader to discover. + + 11.1. Debug mode + + Debug mode, also known as wizard mode, is undocumented aside from + this brief description and the various "debug mode only" commands + listed among the command descriptions. It is intended for tracking + down problems within the program rather than to provide god-like pow- + ers to your character, and players who attempt debugging are expected + to figure out how to use it themselves. It is initiated by starting + the game with the -D command-line switch or with the playmode:debug + option. + + For some systems, the player must be logged in under a particular + user name to be allowed to use debug mode; for others, the hero must + be given a particular character name (but may be any role; there's no + connection between "wizard mode" and the Wizard role). Attempting to + start a game in debug mode when not allowed or not available will result in falling back to explore mode instead. 12. Credits - The original hack game was modeled on the Berkeley UNIX rogue + The original hack game was modeled on the Berkeley UNIX rogue game. Large portions of this document were shamelessly cribbed from A - Guide to the Dungeons of Doom, by Michael C. Toy and Kenneth C. R. C. - Arnold. Small portions were adapted from Further Exploration of the + Guide to the Dungeons of Doom, by Michael C. Toy and Kenneth C. R. C. + Arnold. Small portions were adapted from Further Exploration of the Dungeons of Doom, by Ken Arromdee. - NetHack is the product of literally scores of people's work. + NetHack is the product of literally scores of people's work. Main events in the course of the game development are described below: - Jay Fenlason wrote the original Hack, with help from Kenny Wood- + Jay Fenlason wrote the original Hack, with help from Kenny Wood- land, Mike Thome, and Jon Payne. - Andries Brouwer did a major re-write while at Stichting Mathema- - tisch Centrum (now Centrum Wiskunde & Informatica), transforming Hack + Andries Brouwer did a major re-write while at Stichting Mathema- + tisch Centrum (now Centrum Wiskunde & Informatica), transforming Hack into a very different game. He published the Hack source code for use on UNIX systems by posting that to Usenet newsgroup net.sources (later - renamed comp.sources) releasing version 1.0 in December of 1984, then - versions 1.0.1, 1.0.2, and finally 1.0.3 in July of 1985. Usenet - newsgroup net.games.hack (later renamed rec.games.hack, eventually - replaced by rec.games.roguelike.nethack) was created for discussing + renamed comp.sources) releasing version 1.0 in December of 1984, then + versions 1.0.1, 1.0.2, and finally 1.0.3 in July of 1985. Usenet + newsgroup net.games.hack (later renamed rec.games.hack, eventually + replaced by rec.games.roguelike.nethack) was created for discussing it. - Don G. Kneller ported Hack 1.0.3 to Microsoft C and MS-DOS, pro- - ducing PC HACK 1.01e, added support for DEC Rainbow graphics in ver- - sion 1.03g, and went on to produce at least four more versions (3.0, - 3.2, 3.51, and 3.6; note that these are old Hack version numbers, not + Don G. Kneller ported Hack 1.0.3 to Microsoft C and MS-DOS, pro- + ducing PC HACK 1.01e, added support for DEC Rainbow graphics in ver- + sion 1.03g, and went on to produce at least four more versions (3.0, + 3.2, 3.51, and 3.6; note that these are old Hack version numbers, not contemporary NetHack ones). - R. Black ported PC HACK 3.51 to Lattice C and the Atari + R. Black ported PC HACK 3.51 to Lattice C and the Atari 520/1040ST, producing ST Hack 1.03. - Mike Stephenson merged these various versions back together, - incorporating many of the added features, and produced NetHack version - 1.4 in 1987. He then coordinated a cast of thousands in enhancing and - debugging NetHack 1.4 and released NetHack versions 2.2 and 2.3. Like - Hack, they were released by posting their source code to Usenet where - they remained available in various archives accessible via ftp and - uucp after expiring from the newsgroup. - - Later, Mike coordinated a major re-write of the game, heading a - team which included Ken Arromdee, Jean-Christophe Collet, Steve Creps, - Eric Hendrickson, Izchak Miller, Eric S. Raymond, John Rupley, Mike - Threepoint, and Janet Walz, to produce NetHack 3.0c. - - NetHack 3.0 was ported to the Atari by Eric R. Smith, to OS/2 by - Timo Hakulinen, and to VMS by David Gentzel. The three of them and - Kevin Darcy later joined the main NetHack Development Team to produce - subsequent revisions of 3.0. - NetHack 3.7.0 March 25, 2026 @@ -7132,61 +7132,61 @@ - Olaf Seibert ported NetHack 2.3 and 3.0 to the Amiga. Norm - Meluch, Stephen Spackman and Pierre Martineau designed overlay code - for PC NetHack 3.0. Johnny Lee ported NetHack 3.0 to the Macintosh. - Along with various other Dungeoneers, they continued to enhance the + Mike Stephenson merged these various versions back together, + incorporating many of the added features, and produced NetHack version + 1.4 in 1987. He then coordinated a cast of thousands in enhancing and + debugging NetHack 1.4 and released NetHack versions 2.2 and 2.3. Like + Hack, they were released by posting their source code to Usenet where + they remained available in various archives accessible via ftp and + uucp after expiring from the newsgroup. + + Later, Mike coordinated a major re-write of the game, heading a + team which included Ken Arromdee, Jean-Christophe Collet, Steve Creps, + Eric Hendrickson, Izchak Miller, Eric S. Raymond, John Rupley, Mike + Threepoint, and Janet Walz, to produce NetHack 3.0c. + + NetHack 3.0 was ported to the Atari by Eric R. Smith, to OS/2 by + Timo Hakulinen, and to VMS by David Gentzel. The three of them and + Kevin Darcy later joined the main NetHack Development Team to produce + subsequent revisions of 3.0. + + Olaf Seibert ported NetHack 2.3 and 3.0 to the Amiga. Norm + Meluch, Stephen Spackman and Pierre Martineau designed overlay code + for PC NetHack 3.0. Johnny Lee ported NetHack 3.0 to the Macintosh. + Along with various other Dungeoneers, they continued to enhance the PC, Macintosh, and Amiga ports through the later revisions of 3.0. - Version 3.0 went through ten relatively rapidly released "patch- + Version 3.0 went through ten relatively rapidly released "patch- level" revisions. Versions at the time were known as 3.0 for the base - release and variously as "3.0a" through "3.0j", "3.0 patchlevel 1" + release and variously as "3.0a" through "3.0j", "3.0 patchlevel 1" through "3.0 patchlevel 10", or "3.0pl1" through "3.0pl10" rather than - 3.0.0 and 3.0.1 through 3.0.10; the three component numbering scheme + 3.0.0 and 3.0.1 through 3.0.10; the three component numbering scheme began to be used with 3.1.0. - Headed by Mike Stephenson and coordinated by Izchak Miller and - Janet Walz, the NetHack Development Team which now included Ken - Arromdee, David Cohrs, Jean-Christophe Collet, Kevin Darcy, Matt Day, - Timo Hakulinen, Steve Linhart, Dean Luick, Pat Rankin, Eric Raymond, - and Eric Smith undertook a radical revision of 3.0. They re-struc- - tured the game's design, and re-wrote major parts of the code. They - added multiple dungeons, a new display, special individual character - quests, a new endgame and many other new features, and produced + Headed by Mike Stephenson and coordinated by Izchak Miller and + Janet Walz, the NetHack Development Team which now included Ken + Arromdee, David Cohrs, Jean-Christophe Collet, Kevin Darcy, Matt Day, + Timo Hakulinen, Steve Linhart, Dean Luick, Pat Rankin, Eric Raymond, + and Eric Smith undertook a radical revision of 3.0. They re-struc- + tured the game's design, and re-wrote major parts of the code. They + added multiple dungeons, a new display, special individual character + quests, a new endgame and many other new features, and produced NetHack 3.1. Version 3.1.0 was released in January of 1993. Ken Lorber, Gregg Wonderly and Greg Olson, with help from Richard - Addison, Mike Passaretti, and Olaf Seibert, developed NetHack 3.1 for + Addison, Mike Passaretti, and Olaf Seibert, developed NetHack 3.1 for the Amiga. - Norm Meluch and Kevin Smolkowski, with help from Carl Schelin, - Stephen Spackman, Steve VanDevender, and Paul Winner, ported NetHack + Norm Meluch and Kevin Smolkowski, with help from Carl Schelin, + Stephen Spackman, Steve VanDevender, and Paul Winner, ported NetHack 3.1 to the PC. Jon W{tte and Hao-yang Wang, with help from Ross Brown, Mike Eng- - ber, David Hairston, Michael Hamel, Jonathan Handler, Johnny Lee, Tim - Lennan, Rob Menke, and Andy Swanson, developed NetHack 3.1 for the - Macintosh, porting it for MPW. Building on their development, Bart + ber, David Hairston, Michael Hamel, Jonathan Handler, Johnny Lee, Tim + Lennan, Rob Menke, and Andy Swanson, developed NetHack 3.1 for the + Macintosh, porting it for MPW. Building on their development, Bart House added a Think C port. - Timo Hakulinen ported NetHack 3.1 to OS/2. Eric Smith ported - NetHack 3.1 to the Atari. Pat Rankin, with help from Joshua - Delahunty, was responsible for the VMS version of NetHack 3.1. - Michael Allison ported NetHack 3.1 to Windows NT. - - Dean Luick, with help from David Cohrs, developed NetHack 3.1 for - X11. It drew the map as text rather than graphically but included - nh10.bdf, an optionally used custom X11 font which has tiny images in - place of letters and punctuation, a precursor of tiles. Those images - don't extend to individual monster and object types, just replacements - for monster and object classes (so one custom image for all "a" - insects and another for all "[" armor and so forth, not separate - images for beetles and ants or for cloaks and boots). - - Warwick Allison wrote a graphically displayed version of NetHack - for the Atari where the tiny pictures were described as "icons" and - were distinct for specific types of monsters and objects rather than - NetHack 3.7.0 March 25, 2026 @@ -7198,60 +7198,60 @@ - just their classes. He contributed them to the NetHack Development - Team which rechristened them "tiles", original usage which has subse- - quently been picked up by various other games. NetHack's tiles sup- - port was then implemented on other platforms (initially MS-DOS but + Timo Hakulinen ported NetHack 3.1 to OS/2. Eric Smith ported + NetHack 3.1 to the Atari. Pat Rankin, with help from Joshua + Delahunty, was responsible for the VMS version of NetHack 3.1. + Michael Allison ported NetHack 3.1 to Windows NT. + + Dean Luick, with help from David Cohrs, developed NetHack 3.1 for + X11. It drew the map as text rather than graphically but included + nh10.bdf, an optionally used custom X11 font which has tiny images in + place of letters and punctuation, a precursor of tiles. Those images + don't extend to individual monster and object types, just replacements + for monster and object classes (so one custom image for all "a" + insects and another for all "[" armor and so forth, not separate + images for beetles and ants or for cloaks and boots). + + Warwick Allison wrote a graphically displayed version of NetHack + for the Atari where the tiny pictures were described as "icons" and + were distinct for specific types of monsters and objects rather than + just their classes. He contributed them to the NetHack Development + Team which rechristened them "tiles", original usage which has subse- + quently been picked up by various other games. NetHack's tiles sup- + port was then implemented on other platforms (initially MS-DOS but eventually Windows, Qt, and X11 too). - The 3.2 NetHack Development Team, comprised of Michael Allison, - Ken Arromdee, David Cohrs, Jessie Collet, Steve Creps, Kevin Darcy, - Timo Hakulinen, Steve Linhart, Dean Luick, Pat Rankin, Eric Smith, - Mike Stephenson, Janet Walz, and Paul Winner, released version 3.2.0 + The 3.2 NetHack Development Team, comprised of Michael Allison, + Ken Arromdee, David Cohrs, Jessie Collet, Steve Creps, Kevin Darcy, + Timo Hakulinen, Steve Linhart, Dean Luick, Pat Rankin, Eric Smith, + Mike Stephenson, Janet Walz, and Paul Winner, released version 3.2.0 in April of 1996. - Version 3.2 marked the tenth anniversary of the formation of the + Version 3.2 marked the tenth anniversary of the formation of the development team. In a testament to their dedication to the game, all - thirteen members of the original NetHack Development Team remained on - the team at the start of work on that release. During the interval + thirteen members of the original NetHack Development Team remained on + the team at the start of work on that release. During the interval between the release of 3.1.3 and 3.2.0, one of the founding members of - the NetHack Development Team, Dr. Izchak Miller, was diagnosed with + the NetHack Development Team, Dr. Izchak Miller, was diagnosed with cancer and passed away. That release of the game was dedicated to him by the development and porting teams. - Version 3.2 proved to be more stable than previous versions. - Many bugs were fixed, abuses eliminated, and game features tuned for + Version 3.2 proved to be more stable than previous versions. + Many bugs were fixed, abuses eliminated, and game features tuned for better game play. - During the lifespan of NetHack 3.1 and 3.2, several enthusiasts - of the game added their own modifications to the game and made these + During the lifespan of NetHack 3.1 and 3.2, several enthusiasts + of the game added their own modifications to the game and made these "variants" publicly available: Tom Proudfoot and Yuval Oren created NetHack++, which was quickly - renamed NetHack-- when some people incorrectly assumed that it was a - conversion of the C source code to C++. Working independently, - Stephen White wrote NetHack Plus. Tom Proudfoot later merged NetHack - Plus and his own NetHack-- to produce SLASH. Larry Stewart-Zerba and - Warwick Allison improved the spell casting system with the Wizard + renamed NetHack-- when some people incorrectly assumed that it was a + conversion of the C source code to C++. Working independently, + Stephen White wrote NetHack Plus. Tom Proudfoot later merged NetHack + Plus and his own NetHack-- to produce SLASH. Larry Stewart-Zerba and + Warwick Allison improved the spell casting system with the Wizard Patch. Warwick Allison also ported NetHack to use the Qt interface. - Warren Cheung combined SLASH with the Wizard Patch to produce - Slash'EM, and with the help of Kevin Hugo, added more features. Kevin - later joined the NetHack Development Team and incorporated the best of - these ideas into NetHack 3.3. - - The final update to 3.2 was the bug fix release 3.2.3, which was - released simultaneously with 3.3.0 in December 1999 just in time for - the Year 2000. Because of the newer version, 3.2.3 was released as a - source code patch only, without any ready-to-play distribution for - systems that usually had such. - - (To anyone considering resurrecting an old version: all versions - before 3.2.3 had a Y2K bug. The high scores file and the log file - contained dates which were formatted using a two-digit year, and - 1999's year 99 was followed by 2000's year 100. That got written out - successfully but it unintentionally introduced an extra column in the - file layout which prevented score entries from being read back in cor- NetHack 3.7.0 March 25, 2026 @@ -7264,60 +7264,60 @@ - rectly, interfering with insertion of new high scores and with - retrieval of old character names to use for random ghost and statue + Warren Cheung combined SLASH with the Wizard Patch to produce + Slash'EM, and with the help of Kevin Hugo, added more features. Kevin + later joined the NetHack Development Team and incorporated the best of + these ideas into NetHack 3.3. + + The final update to 3.2 was the bug fix release 3.2.3, which was + released simultaneously with 3.3.0 in December 1999 just in time for + the Year 2000. Because of the newer version, 3.2.3 was released as a + source code patch only, without any ready-to-play distribution for + systems that usually had such. + + (To anyone considering resurrecting an old version: all versions + before 3.2.3 had a Y2K bug. The high scores file and the log file + contained dates which were formatted using a two-digit year, and + 1999's year 99 was followed by 2000's year 100. That got written out + successfully but it unintentionally introduced an extra column in the + file layout which prevented score entries from being read back in cor- + rectly, interfering with insertion of new high scores and with + retrieval of old character names to use for random ghost and statue names in the current game.) - The 3.3 NetHack Development Team, consisting of Michael Allison, - Ken Arromdee, David Cohrs, Jessie Collet, Steve Creps, Kevin Darcy, + The 3.3 NetHack Development Team, consisting of Michael Allison, + Ken Arromdee, David Cohrs, Jessie Collet, Steve Creps, Kevin Darcy, Timo Hakulinen, Kevin Hugo, Steve Linhart, Ken Lorber, Dean Luick, Pat - Rankin, Eric Smith, Mike Stephenson, Janet Walz, and Paul Winner, + Rankin, Eric Smith, Mike Stephenson, Janet Walz, and Paul Winner, released 3.3.0 in December 1999 and 3.3.1 in August of 2000. Version 3.3 offered many firsts. It was the first version to sep- - arate race and profession. The Elf class was removed in preference to - an elf race, and the races of dwarves, gnomes, and orcs made their - first appearance in the game alongside the familiar human race. Monk - and Ranger roles joined Archeologists, Barbarians, Cavemen, Healers, - Knights, Priests, Rogues, Samurai, Tourists, Valkyries and of course, - Wizards. It was also the first version to allow you to ride a steed, - and was the first version to have a publicly available web-site list- - ing all the bugs that had been discovered. Despite that constantly - growing bug list, 3.3 proved stable enough to last for more than a + arate race and profession. The Elf class was removed in preference to + an elf race, and the races of dwarves, gnomes, and orcs made their + first appearance in the game alongside the familiar human race. Monk + and Ranger roles joined Archeologists, Barbarians, Cavemen, Healers, + Knights, Priests, Rogues, Samurai, Tourists, Valkyries and of course, + Wizards. It was also the first version to allow you to ride a steed, + and was the first version to have a publicly available web-site list- + ing all the bugs that had been discovered. Despite that constantly + growing bug list, 3.3 proved stable enough to last for more than a year and a half. - The 3.4 NetHack Development Team initially consisted of Michael - Allison, Ken Arromdee, David Cohrs, Jessie Collet, Kevin Hugo, Ken - Lorber, Dean Luick, Pat Rankin, Mike Stephenson, Janet Walz, and Paul - Winner, with Warwick Allison joining just before the release of + The 3.4 NetHack Development Team initially consisted of Michael + Allison, Ken Arromdee, David Cohrs, Jessie Collet, Kevin Hugo, Ken + Lorber, Dean Luick, Pat Rankin, Mike Stephenson, Janet Walz, and Paul + Winner, with Warwick Allison joining just before the release of NetHack 3.4.0 in March 2002. - As with version 3.3, various people contributed to the game as a - whole as well as supporting ports on the different platforms that + As with version 3.3, various people contributed to the game as a + whole as well as supporting ports on the different platforms that NetHack runs on: Pat Rankin maintained 3.4 for VMS. - Michael Allison maintained NetHack 3.4 for the MS-DOS platform. + Michael Allison maintained NetHack 3.4 for the MS-DOS platform. Paul Winner and Yitzhak Sapir provided encouragement. - Dean Luick, Mark Modrall, and Kevin Hugo maintained and enhanced - the Macintosh port of 3.4. - - Michael Allison, David Cohrs, Alex Kompel, Dion Nicolaas, and - Yitzhak Sapir maintained and enhanced 3.4 for the Microsoft Windows - platform. Alex Kompel contributed a new graphical interface for the - Windows port. Alex Kompel also contributed a Windows CE port for - 3.4.1. - - Ron Van Iwaarden was the sole maintainer of NetHack for OS/2 the - past several releases. Unfortunately Ron's last OS/2 machine stopped - working in early 2006. A great many thanks to Ron for keeping NetHack - alive on OS/2 all these years. - - Janne Salmijarvi and Teemu Suikki maintained and enhanced the - Amiga port of 3.4 after Janne Salmijarvi resurrected it for 3.3.1. - NetHack 3.7.0 March 25, 2026 @@ -7330,60 +7330,60 @@ + Dean Luick, Mark Modrall, and Kevin Hugo maintained and enhanced + the Macintosh port of 3.4. + + Michael Allison, David Cohrs, Alex Kompel, Dion Nicolaas, and + Yitzhak Sapir maintained and enhanced 3.4 for the Microsoft Windows + platform. Alex Kompel contributed a new graphical interface for the + Windows port. Alex Kompel also contributed a Windows CE port for + 3.4.1. + + Ron Van Iwaarden was the sole maintainer of NetHack for OS/2 the + past several releases. Unfortunately Ron's last OS/2 machine stopped + working in early 2006. A great many thanks to Ron for keeping NetHack + alive on OS/2 all these years. + + Janne Salmijarvi and Teemu Suikki maintained and enhanced the + Amiga port of 3.4 after Janne Salmijarvi resurrected it for 3.3.1. + Christian "Marvin" Bressler maintained 3.4 for the Atari after he resurrected it for 3.3.1. - The release of NetHack 3.4.3 in December 2003 marked the begin- - ning of a long release hiatus. 3.4.3 proved to be a remarkably stable - version that provided continued enjoyment by the community for more + The release of NetHack 3.4.3 in December 2003 marked the begin- + ning of a long release hiatus. 3.4.3 proved to be a remarkably stable + version that provided continued enjoyment by the community for more than a decade. The NetHack Development Team slowly and quietly contin- - ued to work on the game behind the scenes during the tenure of 3.4.3. - It was during that same period that several new variants emerged - within the NetHack community. Notably sporkhack by Derek S. Ray, - unnethack by Patric Mueller, nitrohack and its successors originally - by Daniel Thaler and then by Alex Smith, and Dynahack by Tung Nguyen. - Some of those variants continue to be developed, maintained, and + ued to work on the game behind the scenes during the tenure of 3.4.3. + It was during that same period that several new variants emerged + within the NetHack community. Notably sporkhack by Derek S. Ray, + unnethack by Patric Mueller, nitrohack and its successors originally + by Daniel Thaler and then by Alex Smith, and Dynahack by Tung Nguyen. + Some of those variants continue to be developed, maintained, and enjoyed by the community to this day. In September 2014, an interim snapshot of the code under develop- - ment was released publicly by other parties. Since that code was a - work-in-progress and had not gone through the process of debugging it + ment was released publicly by other parties. Since that code was a + work-in-progress and had not gone through the process of debugging it as a suitable release, it was decided that the version numbers present - on that code snapshot would be retired and never used in an official - NetHack release. An announcement was posted on the NetHack Develop- - ment Team's official nethack.org website to that effect, stating that + on that code snapshot would be retired and never used in an official + NetHack release. An announcement was posted on the NetHack Develop- + ment Team's official nethack.org website to that effect, stating that there would never be a 3.4.4, 3.5, or 3.5.0 official release version. - In January 2015, preparation began for the release of NetHack + In January 2015, preparation began for the release of NetHack 3.6. - At the beginning of development for what would eventually get - released as 3.6.0, the NetHack Development Team consisted of Warwick - Allison, Michael Allison, Ken Arromdee, David Cohrs, Jessie Collet, - Ken Lorber, Dean Luick, Pat Rankin, Mike Stephenson, Janet Walz, and - Paul Winner. In early 2015, ahead of the release of 3.6.0, new mem- - bers Sean Hunt, Pasi Kallinen, and Derek S. Ray joined the NetHack + At the beginning of development for what would eventually get + released as 3.6.0, the NetHack Development Team consisted of Warwick + Allison, Michael Allison, Ken Arromdee, David Cohrs, Jessie Collet, + Ken Lorber, Dean Luick, Pat Rankin, Mike Stephenson, Janet Walz, and + Paul Winner. In early 2015, ahead of the release of 3.6.0, new mem- + bers Sean Hunt, Pasi Kallinen, and Derek S. Ray joined the NetHack Development Team. - Near the end of the development of 3.6.0, one of the significant - inspirations for many of the humorous and fun features found in the - game, author Terry Pratchett, passed away. NetHack 3.6.0 introduced a - tribute to him. - - 3.6.0 was released in December 2015, and merged work done by the - development team since the release of 3.4.3 with some of the beloved - community patches. Many bugs were fixed and some code was restruc- - tured. - - The NetHack Development Team, as well as Steve VanDevender and - Kevin Smolkowski, ensured that NetHack 3.6 continued to operate on - various UNIX flavors and maintained the X11 interface. - - Ken Lorber, Haoyang Wang, Pat Rankin, and Dean Luick maintained - the port of NetHack 3.6 for MacOS. - - Michael Allison, David Cohrs, Bart House, Pasi Kallinen, Alex - Kompel, Dion Nicolaas, Derek S. Ray and Yitzhak Sapir maintained the + Near the end of the development of 3.6.0, one of the significant + inspirations for many of the humorous and fun features found in the NetHack 3.7.0 March 25, 2026 @@ -7396,47 +7396,76 @@ + game, author Terry Pratchett, passed away. NetHack 3.6.0 introduced a + tribute to him. + + 3.6.0 was released in December 2015, and merged work done by the + development team since the release of 3.4.3 with some of the beloved + community patches. Many bugs were fixed and some code was restruc- + tured. + + The NetHack Development Team, as well as Steve VanDevender and + Kevin Smolkowski, ensured that NetHack 3.6 continued to operate on + various UNIX flavors and maintained the X11 interface. + + Ken Lorber, Haoyang Wang, Pat Rankin, and Dean Luick maintained + the port of NetHack 3.6 for MacOS. + + Michael Allison, David Cohrs, Bart House, Pasi Kallinen, Alex + Kompel, Dion Nicolaas, Derek S. Ray and Yitzhak Sapir maintained the port of NetHack 3.6 for Microsoft Windows. - Pat Rankin attempted to keep the VMS port running for NetHack - 3.6, hindered by limited access. Kevin Smolkowski has updated and - tested it for the most recent version of OpenVMS (V8.4 as of this + Pat Rankin attempted to keep the VMS port running for NetHack + 3.6, hindered by limited access. Kevin Smolkowski has updated and + tested it for the most recent version of OpenVMS (V8.4 as of this writing) on Alpha and Integrity (aka Itanium aka IA64) but not VAX. - Ray Chason resurrected the MS-DOS port for 3.6 and contributed + Ray Chason resurrected the MS-DOS port for 3.6 and contributed the necessary updates to the community at large. - In late April 2018, several hundred bug fixes for 3.6.0 and some - new features were assembled and released as NetHack 3.6.1. The - NetHack Development Team at the time of release of 3.6.1 consisted of - Warwick Allison, Michael Allison, Ken Arromdee, David Cohrs, Jessie - Collet, Pasi Kallinen, Ken Lorber, Dean Luick, Patric Mueller, Pat - Rankin, Derek S. Ray, Alex Smith, Mike Stephenson, Janet Walz, and + In late April 2018, several hundred bug fixes for 3.6.0 and some + new features were assembled and released as NetHack 3.6.1. The + NetHack Development Team at the time of release of 3.6.1 consisted of + Warwick Allison, Michael Allison, Ken Arromdee, David Cohrs, Jessie + Collet, Pasi Kallinen, Ken Lorber, Dean Luick, Patric Mueller, Pat + Rankin, Derek S. Ray, Alex Smith, Mike Stephenson, Janet Walz, and Paul Winner. In early May 2019, another 320 bug fixes along with some enhance- ments and the adopted curses window port, were released as 3.6.2. - Bart House, who had contributed to the game as a porting team - participant for decades, joined the NetHack Development Team in late + Bart House, who had contributed to the game as a porting team + participant for decades, joined the NetHack Development Team in late May 2019. - NetHack 3.6.3 was released on December 5, 2019 containing over + NetHack 3.6.3 was released on December 5, 2019 containing over 190 bug fixes to NetHack 3.6.2. - NetHack 3.6.4 was released on December 18, 2019 containing a + NetHack 3.6.4 was released on December 18, 2019 containing a security fix and a few bug fixes. - NetHack 3.6.5 was released on January 27, 2020 containing some + NetHack 3.6.5 was released on January 27, 2020 containing some security fixes and a small number of bug fixes. NetHack 3.6.6 was released on March 8, 2020 containing a security fix and some bug fixes. - NetHack 3.6.7 was released on February 16, 2023 containing a + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 114 + + + + NetHack 3.6.7 was released on February 16, 2023 containing a security fix and some bug fixes. - The official NetHack web site is maintained by Ken Lorber at + The official NetHack web site is maintained by Ken Lorber at https://www.nethack.org/. @@ -7452,24 +7481,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + 12.1. Special Thanks + + On behalf of the NetHack community, thank you very much once + again to M. Drew Streib and Pasi Kallinen for providing a public + NetHack server at nethack.alt.org. Thanks to Keith Simpson and Andy + Thomson for hardfought.org. Thanks to all those unnamed dungeoneers + who invest their time and effort into annual NetHack tournaments such + as Junethack, The November NetHack Tournament, and in days past, + + NetHack 3.7.0 March 25, 2026 - NetHack Guidebook 114 + NetHack Guidebook 115 - 12.1. Special Thanks - - On behalf of the NetHack community, thank you very much once - again to M. Drew Streib and Pasi Kallinen for providing a public - NetHack server at nethack.alt.org. Thanks to Keith Simpson and Andy - Thomson for hardfought.org. Thanks to all those unnamed dungeoneers - who invest their time and effort into annual NetHack tournaments such - as Junethack, The November NetHack Tournament, and in days past, devnull.net (gone for now, but not forgotten). @@ -7514,21 +7572,9 @@ 12.2. Dungeoneers - From time to time, some depraved individual out there in netland - sends a particularly intriguing modification to help out with the - - - NetHack 3.7.0 March 25, 2026 - - - - - - NetHack Guidebook 115 - - - - game. The NetHack Development Team sometimes makes note of the names + From time to time, some depraved individual out there in netland + sends a particularly intriguing modification to help out with the + game. The NetHack Development Team sometimes makes note of the names of the worst of these miscreants in this, the list of Dungeoneers: @@ -7538,35 +7584,13 @@ + NetHack 3.7.0 March 25, 2026 - - - - - - - - - - - - - - - - - - - - - - - - + NetHack Guidebook 116 @@ -7581,19 +7605,6 @@ Bart House John S. Bien Pierre Martineau Benson I. Margulies Johnny Lee Ralf Brown Bill Dyer Jon W{tte Ray Chason - - - - NetHack 3.7.0 March 25, 2026 - - - - - - NetHack Guidebook 116 - - - Boudewijn Waijers Jonathan Handler Richard Addison Bruce Cox Joshua Delahunty Richard Beigel Bruce Holloway Karl Garrison Richard P. Hughey @@ -7634,17 +7645,6 @@ - - - - - - - - - - - @@ -7660,7 +7660,7 @@ - Brand and product names are trademarks or registered trademarks + Brand and product names are trademarks or registered trademarks of their respective holders. From f49ac037f9e726a79e2de0f53a5843593b77445f Mon Sep 17 00:00:00 2001 From: PatR Date: Wed, 25 Mar 2026 15:54:44 -0700 Subject: [PATCH 364/442] release wish history memory The memory leak only happened at end of game so wasn't a big deal. --- include/extern.h | 1 + src/save.c | 1 + src/zap.c | 17 ++++++++++++++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/include/extern.h b/include/extern.h index ac0992b05..a3cc23213 100644 --- a/include/extern.h +++ b/include/extern.h @@ -3989,6 +3989,7 @@ extern char *item_what(int); extern int destroy_items(struct monst *, int, int) NONNULLARG1; extern int resist(struct monst *, char, int, int) NONNULLARG1; extern void wish_history_add(char *); +extern void wish_history_flush(void); extern void makewish(void); extern const char *flash_str(int, boolean) NONNULL; diff --git a/src/save.c b/src/save.c index 461ed10a1..a7ea05e7a 100644 --- a/src/save.c +++ b/src/save.c @@ -1111,6 +1111,7 @@ freedynamicdata(void) cmdq_clear(CQ_REPEAT); cmdbind_freeall(); free_tutorial(); /* (only needed if quitting while in tutorial) */ + wish_history_flush(); /* per-turn data, but might get added to when freeing other stuff */ dobjsfree(); /* really free deleted objects */ diff --git a/src/zap.c b/src/zap.c index a1466e1d5..83f25195a 100644 --- a/src/zap.c +++ b/src/zap.c @@ -6240,13 +6240,28 @@ wish_history_add(char *buf) if (wish_history[idx]) free(wish_history[idx]); - wish_history[idx] = (char *)alloc(strlen(buf) + 1); + wish_history[idx] = (char *) alloc(strlen(buf) + 1); strcpy(wish_history[idx], buf); wish_history_idx = (wish_history_idx + 1) % MAX_WISH_HISTORY; } #endif /* DEBUG */ } +/* release any old wish text; called from freedynamicdata(save.c) */ +void +wish_history_flush(void) +{ +#ifdef DEBUG + int idx; + + for (idx = 0; idx < MAX_WISH_HISTORY; ++idx) { + if (wish_history[idx]) + free((genericptr_t) wish_history[idx]), wish_history[idx] = NULL; + } + wish_history_idx = 0; +#endif +} + /* shows menu of previous wishes, copies selected into buf, max BUFSZ len. buf is not modified, if nothing was selected. */ staticfn void From 5d814053a12ef967e188f6811d713f32cdbe39dc Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 27 Mar 2026 06:59:49 -0400 Subject: [PATCH 365/442] some outdated cleanup outdated/sys/windows --- outdated/sys/windows/travis-gcc.sh | 8 ----- outdated/sys/windows/vs/travisci.sh | 49 ----------------------------- 2 files changed, 57 deletions(-) delete mode 100644 outdated/sys/windows/travis-gcc.sh delete mode 100644 outdated/sys/windows/vs/travisci.sh diff --git a/outdated/sys/windows/travis-gcc.sh b/outdated/sys/windows/travis-gcc.sh deleted file mode 100644 index e6092375f..000000000 --- a/outdated/sys/windows/travis-gcc.sh +++ /dev/null @@ -1,8 +0,0 @@ -set -x -mkdir -p lib -cd lib -git clone --depth 1 https://github.com/wmcbrine/PDCurses.git pdcurses -#git clone --depth 1 https://github.com/universal-ctags/ctags.git ctags -curl -R -O http://www.lua.org/ftp/lua-5.4.8.tar.gz -tar zxf lua-5.4.8.tar.gz -cd ../ diff --git a/outdated/sys/windows/vs/travisci.sh b/outdated/sys/windows/vs/travisci.sh deleted file mode 100644 index f9f1b5080..000000000 --- a/outdated/sys/windows/vs/travisci.sh +++ /dev/null @@ -1,49 +0,0 @@ -set -x -export VSVER=2017 -export MSVER=14.16.27023 -export SDKVER=10.0.17763.0 -export FRAMEVER=4.0.30319 -export NETFXVER=4.6.1 -export WKITVER=10.0.17134.0 -#export TOOLSVER=Enterprise -export TOOLSVER=BuildTools -export PATH=/c/Program\ Files\ \(x86\)/Microsoft\ Visual\ Studio/$VSVER/$TOOLSVER/Common7/IDE/VC/VCPackages:$PATH -export PATH=/c/Program\ Files\ \(x86\)/Windows\ Kits/10/bin/$WKITVER/x64:$PATH -export PATH=/c/Program\ Files\ \(x86\)/Microsoft\ Visual\ Studio/$VSVER/$TOOLSVER/VC/Tools/MSVC/$MSVER/bin/HostX64/x64:$PATH -export PATH=/c/Program\ Files\ \(x86\)/Microsoft\ Visual\ Studio/$VSVER/$TOOLSVER/VC/Tools/MSVC/$MSVER/bin/HostX64/x86:$PATH -export PATH=$PATH:/c/Program\ Files\ \(x86\)/Microsoft\ Visual\ Studio/$VSVER/$TOOLSVER/Common7/IDE/CommonExtensions/Microsoft/TestWindow -export PATH=$PATH:/c/Program\ Files\ \(x86\)/Microsoft\ Visual\ Studio/$VSVER/$TOOLSVER/MSBuild/Current/bin/Roslyn -export INCLUDE=/c/Program\ Files\ \(x86\)/Microsoft\ Visual\ Studio/2017/$TOOLSVER/VC/Tools/MSVC/$MSVER/include -export INCLUDE=$INCLUDE:/c/Program\ Files\ \(x86\)/Windows\ Kits/10/Include/$WKITVER/ucrt -export INCLUDE=$INCLUDE:/c/Program\ Files\ \(x86\)/Windows\ Kits/10/include/$WKITVER/ucrt -export INCLUDE=$INCLUDE:/c/Program\ Files\ \(x86\)/Windows\ Kits/10/include/$WKITVER/shared -export INCLUDE=$INCLUDE:/c/Program\ Files\ \(x86\)/Windows\ Kits/10/include/$WKITVER/um -export INCLUDE=$INCLUDE:/c/Program\ Files\ \(x86\)/Windows\ Kits/10/include/$WKITVER/winrt -export INCLUDE=$INCLUDE:/c/Program\ Files\ \(x86\)/Windows\ Kits/10/include/$WKITVER/cppwinrt -export LIB=/c/Program\ Files\ \(x86\)/Microsoft\ Visual\ Studio/$VSVER/$TOOLSVER/VC/Tools/MSVC/$MSVER/ATLMFC/lib/x86 -export LIB=/c/Program\ Files\ \(x86\)/Microsoft\ Visual\ Studio/$VSVER/$TOOLSVER/VC/Tools/MSVC/$MSVER/lib/x86:$LIB -export LIB=/c/Program\ Files\ \(x86\)/Windows\ Kits/10/lib/$WKITVER/ucrt/x86:$LIB -export LIB=/c/Program\ Files\ \(x86\)/Windows\ Kits/10/lib/$WKITVER/um/x86:$LIB -export LUA_VERSION=5.4.3 -mkdir -p lib -cd lib -git clone --depth 1 https://github.com/wmcbrine/PDCurses.git pdcurses -#git clone --depth 1 https://github.com/universal-ctags/ctags.git ctags -curl -R -O http://www.lua.org/ftp/lua-$LUA_VERSION.tar.gz -tar zxf lua-$LUA_VERSION.tar.gz -#cd ctags -#nmake -f mk_mvc.mak -#cd ../../ -cd ../ -test -d "lib/lua-$LUA_VERSION/src" || echo "Lua-$LUA_VERSION fetch failed" -test -d "lib/pdcurses" || echo "pdcurses fetch failed" -test -d "lib/pdcurses" || exit 0 -test -d "lib/lua-$LUA_VERSION/src" || exit 0 -export ADD_CURSES=Y -export PDCURSES_TOP=../lib/pdcurses -export -cd src -cp ../sys/windows/Makefile.nmake ./Makefile -nmake install -cd .. -powershell -Command "Compress-Archive -U -Path binary/* -DestinationPath $TRAVIS_TAG.x86.zip" From e3e01267aaee7990ff8bf92914c05defee1805bb Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 29 Mar 2026 18:47:55 +0300 Subject: [PATCH 366/442] Debug panic when level has too many (sub)rooms --- src/mklev.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/mklev.c b/src/mklev.c index 703133ca4..2ddeefae9 100644 --- a/src/mklev.c +++ b/src/mklev.c @@ -306,6 +306,10 @@ add_room(coordxy lowx, coordxy lowy, coordxy hix, coordxy hiy, { struct mkroom *croom; +#ifdef DEBUG + if (svn.nroom >= MAXNROFROOMS) + panic("level has too many rooms"); +#endif /*DEBUG*/ croom = &svr.rooms[svn.nroom]; do_room_or_subroom(croom, lowx, lowy, hix, hiy, lit, rtype, special, (boolean) TRUE); @@ -322,6 +326,12 @@ add_subroom(struct mkroom *proom, { struct mkroom *croom; +#ifdef DEBUG + if (gn.nsubroom >= MAXNROFROOMS) + panic("level has too many subrooms"); + if (proom->nsubrooms >= MAX_SUBROOMS) + panic("room has too many subrooms"); +#endif /*DEBUG*/ croom = &gs.subrooms[gn.nsubroom]; do_room_or_subroom(croom, lowx, lowy, hix, hiy, lit, rtype, special, (boolean) FALSE); From 1813e27098b260b7ef666919fe0a31df4a3ac707 Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 29 Mar 2026 14:02:21 -0700 Subject: [PATCH 367/442] farlook of quest stairs down When access to the quest isn't available yet, describe the stairs down as "blocked staircase down" instead of the usual "staircase down". Applies to mimics posing as stairs too. Does not apply to the stairs when standing on them and using lookhere. I was going to use "locked staircase down" but that would imply that a key or unlocking magic could be applicable. --- src/pager.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pager.c b/src/pager.c index ca5ed2810..f016cd258 100644 --- a/src/pager.c +++ b/src/pager.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 pager.c $NHDT-Date: 1764044196 2025/11/24 20:16:36 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.292 $ */ +/* NetHack 3.7 pager.c $NHDT-Date: 1774846177 2026/03/29 20:49:37 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.296 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2018. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1600,6 +1600,9 @@ do_screen_description( *for_supplement = pm; if (!strcmp(look_buf, "ice")) (void) ice_descr(cc.x, cc.y, look_buf); + if (!strcmp(look_buf, "staircase down") + && on_level(&u.uz, &qstart_level) && !ok_to_quest()) + Strcpy(look_buf, "blocked staircase down"); if (look_buf[0] != '\0') *firstmatch = look_buf; From 12663473ab56ba3b0dda30f500a4a914d87c9ed8 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 29 Mar 2026 20:20:56 -0400 Subject: [PATCH 368/442] update tested versions of Visual Studio 2026-03-29 --- sys/windows/Makefile.nmake | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index 5a549d3ef..dacee7775 100644 --- a/sys/windows/Makefile.nmake +++ b/sys/windows/Makefile.nmake @@ -9,7 +9,7 @@ # # Visual Studio Compilers Tested: # - Microsoft Visual Studio Community 2022 v 17.14.29 -# - Microsoft Visual Studio Community 2026 v 18.4.1 +# - Microsoft Visual Studio Community 2026 v 18.4.2 # #============================================================================== # This is used for building two distinct executables of NetHack: @@ -1191,8 +1191,7 @@ rc=Rc.exe # # Recently tested versions: TESTEDVS2022 = 14.44.35222.0 -TESTEDVS2026 = 14.50.35726.0 -TESTEDVS2026 = 14.50.35727.0 +TESTEDVS2026 = 14.50.35728.0 VS20261ST = 1450000000 VS2026CUR = $(TESTEDVS2026:.=) From 9fba4af32344edc062639b4f3dd8ab1ba57c7788 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Mon, 30 Mar 2026 13:29:09 +0300 Subject: [PATCH 369/442] Status hilite menu retry limit Fuzzer got stuck here for a very long time, so bail out from the status hilite menu after few failed tries. --- src/botl.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/botl.c b/src/botl.c index 2e9ed01db..7071e6638 100644 --- a/src/botl.c +++ b/src/botl.c @@ -3637,6 +3637,7 @@ status_hilite_menu_add(int origfld) unsigned long cond = 0UL; char colorqry[BUFSZ]; char attrqry[BUFSZ]; + int retry = 0; choose_field: fld = origfld; @@ -3672,6 +3673,10 @@ status_hilite_menu_add(int origfld) hilite.behavior = behavior; choose_value: + if (retry++ > 5) { + pline("That's enough tries."); + return FALSE; + } if (behavior == BL_TH_VAL_PERCENTAGE || behavior == BL_TH_VAL_ABSOLUTE) { char inbuf[BUFSZ], buf[BUFSZ]; From f4173adc88783f897d5858f0e8ebc6902b43553d Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Mon, 30 Mar 2026 16:40:37 +0300 Subject: [PATCH 370/442] Use define for player name length in botl This defines the cut-off how many characters of the player's name is shown in the bottom status line. Also increase the limit from 10 characters to 16. --- include/botl.h | 3 +++ src/botl.c | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/include/botl.h b/include/botl.h index 87c82d25c..1a1507010 100644 --- a/include/botl.h +++ b/include/botl.h @@ -27,6 +27,9 @@ Astral Plane \GXXXXNNNN:123456 HP:1234(1234) Pw:1234(1234) AC:-127 #define MAXCO (COLNO + 40) #endif +/* limit of the player's name in the status window */ +#define BOTL_NSIZ 16 + struct condmap { const char *id; unsigned long bitmask; diff --git a/src/botl.c b/src/botl.c index 7071e6638..934d9e7ee 100644 --- a/src/botl.c +++ b/src/botl.c @@ -57,7 +57,7 @@ do_statusline1(void) Strcpy(newbot1, svp.plname); if ('a' <= newbot1[0] && newbot1[0] <= 'z') newbot1[0] += 'A' - 'a'; - newbot1[10] = 0; + newbot1[BOTL_NSIZ] = 0; Sprintf(nb = eos(newbot1), " the "); if (Upolyd) { @@ -769,12 +769,12 @@ bot_via_windowport(void) nb[0] = highc(nb[0]); titl = !Upolyd ? rank() : pmname(&mons[u.umonnum], Ugender); i = (int) (strlen(buf) + sizeof " the " + strlen(titl) - sizeof ""); - /* if "Name the Rank/monster" is too long, we truncate the name - but always keep at least 10 characters of it; when hitpointbar is + /* if "Name the Rank/monster" is too long, we truncate the name but + always keep at least BOTL_NSIZ characters of it; when hitpointbar is enabled, anything beyond 30 (long monster name) will be truncated */ if (i > 30) { i = 30 - (int) (sizeof " the " + strlen(titl) - sizeof ""); - nb[max(i, 10)] = '\0'; + nb[max(i, BOTL_NSIZ)] = '\0'; } Strcpy(nb = eos(nb), " the "); Strcpy(nb = eos(nb), titl); From f68f15e3b5189c1734cf84cf5ea2aef8f4f09c93 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Mon, 30 Mar 2026 18:23:51 +0300 Subject: [PATCH 371/442] Simplify some special level lua These were generated by a script that converted the des-file format to lua. --- dat/Rog-strt.lua | 4 ++-- dat/astral.lua | 4 ++-- dat/minetn-1.lua | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/dat/Rog-strt.lua b/dat/Rog-strt.lua index 55bf11969..1e4700d49 100644 --- a/dat/Rog-strt.lua +++ b/dat/Rog-strt.lua @@ -157,10 +157,10 @@ des.monster({ id = "leprechaun", x=74, y=04, peaceful=0 }) des.monster({ id = "leprechaun", x=25, y=19, peaceful=0 }) des.monster({ id = "water nymph", x=25, y=18, peaceful=0 }) -- Wandering the streets. -for i=1,4 + math.random(1 - 1,1*3) do +for i = 1, math.random(4,7) do des.monster({ id = "water nymph", coord = streets:rndcoord(1), peaceful=0 }) des.monster({ id = "leprechaun", coord = streets:rndcoord(1), peaceful=0 }) end -for i=1,7 + math.random(1 - 1,1*3) do +for i = 1, math.random(7,10) do des.monster({ id = "chameleon", coord = streets:rndcoord(1), peaceful=0 }) end diff --git a/dat/astral.lua b/dat/astral.lua index f0ba8951e..6d6f0808b 100644 --- a/dat/astral.lua +++ b/dat/astral.lua @@ -35,7 +35,7 @@ des.map([[ -- chance to alter above map and turn the wings of the bottom-center into -- a pair of big (5x15) rooms -for i=1,2 do +for i = 1, 2 do -- 3.6.[01]: 75% chance that both sides opened up, 25% that neither did; -- 3.6.2: 60% twice == 36% chance that both sides open up, 24% left side -- only, 24% right side only, 16% that neither side opens up @@ -58,7 +58,7 @@ for i=1,2 do des.terrain(41,18, ".") end -- extra monsters; was [6 + 3d4] when both wings were opened up at once - for i=1,3 + math.random(2 - 1,2*3) do + for j = 1, math.random(4,9) do des.monster({ id="Angel", coord = hall:rndcoord(1), align="noalign", peaceful=0 }) if percent(50) then des.monster({ coord = hall:rndcoord(1), peaceful=0 }) diff --git a/dat/minetn-1.lua b/dat/minetn-1.lua index 960553d4e..ef10d70b7 100644 --- a/dat/minetn-1.lua +++ b/dat/minetn-1.lua @@ -88,7 +88,7 @@ des.object({ id = "corpse", montype="watchman" }) des.object({ id = "corpse", montype="watch captain" }) -- Rubble! -for i=1,9 + math.random(2 - 1,2*5) do +for i = 1, math.random(10,19) do if percent(90) then des.object("boulder") end @@ -118,7 +118,7 @@ des.object({ id = "wand of magic missile", coord = place[5], buc="uncursed", spe local inside = selection.floodfill(18,8) local near_temple = selection.area(17,8, 23,14) & inside -for i=1,5 + math.random(1 - 1,1*10) do +for i = 1, math.random(5,15) do if percent(50) then des.monster({ id = "orc-captain", coord = inside:rndcoord(1), peaceful=0 }); else @@ -131,13 +131,13 @@ for i=1,5 + math.random(1 - 1,1*10) do end -- shamans can be hanging out in/near the temple -- one of the shamans is higher level -for i=1,math.random(1,6) do +for i = 1, math.random(1,6) do des.monster({ id = "orc shaman", coord = near_temple:rndcoord(0), peaceful=0, m_lev_adj = (i == 1) and 3 or 0 }); end -- these are not such a big deal -- to run into outside the bars -for i=1,9 + math.random(2 - 1,2*5) do +for i = 1, math.random(10,19) do if percent(90) then des.monster({ id = "hill orc", peaceful = 0 }) else From 9bf90ee07f33d10ddf5c3cd99065a2a63f583b4b Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Mon, 30 Mar 2026 19:07:18 +0300 Subject: [PATCH 372/442] Replace magic number with define --- src/display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/display.c b/src/display.c index e3f8225f8..5f854c64d 100644 --- a/src/display.c +++ b/src/display.c @@ -2656,7 +2656,7 @@ const int altarcolors[] = { altar_color_unaligned, altar_color_chaotic, altar_color_neutral, altar_color_lawful, altar_color_other }; -const int explodecolors[7] = { +const int explodecolors[EXPL_MAX] = { explode_color_dark, explode_color_noxious, explode_color_muddy, explode_color_wet, explode_color_magical, explode_color_fiery, explode_color_frosty, From 90db8d7e2d325fec2fd94ab59f0d4c4773d507d5 Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 31 Mar 2026 10:16:28 -0400 Subject: [PATCH 373/442] clear up some warnings appearing with gcc 15.2.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit allmain.c: In function ‘debug_fields’: allmain.c:1133:16: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers] 1133 | while ((op = strchr(opts, ',')) != 0) { | ^ cfgfiles.c: In function ‘find_optparam’: cfgfiles.c:590:10: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers] 590 | bufp = strchr(buf, '='); | ^ cfgfiles.c:591:10: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers] 591 | altp = strchr(buf, ':'); | ^ end.c: In function ‘should_query_disclose_option’: end.c:482:14: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers] 482 | if ((dop = strchr(disclosure_options, category)) != 0) { | ^ invent.c: In function ‘loot_classify’: invent.c:175:7: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers] 175 | p = strchr(classorder, oclass); | ^ o_init.c: In function ‘dodiscovered’: o_init.c:774:33: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers] 774 | if (!flags.discosort || !(p = strchr(disco_order_let, flags.discosort))) | ^ o_init.c: In function ‘doclassdisco’: o_init.c:907:33: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers] 907 | if (!flags.discosort || !(p = strchr(disco_order_let, flags.discosort))) | ^ objnam.c: In function ‘the’: objnam.c:2200:19: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers] 2200 | if (((tmp = strrchr(str, ' ')) != 0 || (tmp = strrchr(str, '-')) != 0) | ^ objnam.c:2200:53: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers] 2200 | if (((tmp = strrchr(str, ' ')) != 0 || (tmp = strrchr(str, '-')) != 0) | ^ options.c: In function ‘optfn_disclose’: options.c:1529:17: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers] 1529 | dop = strchr(disclosure_options, c); | ^ pager.c: In function ‘add_cmap_descr’: pager.c:1212:28: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers] 1212 | || ((p = strchr(x_str, ' ')) != 0 && !strcmpi(p, " ice")) | ^ --- src/allmain.c | 11 +++++++---- src/cfgfiles.c | 4 ++-- src/end.c | 3 ++- src/invent.c | 2 +- src/o_init.c | 16 ++++++++++------ src/objnam.c | 3 ++- src/options.c | 3 ++- src/pager.c | 3 ++- 8 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/allmain.c b/src/allmain.c index b53f92fe0..4e0021eaf 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -21,7 +21,7 @@ staticfn void do_positionbar(void); staticfn void regen_pw(int); staticfn void regen_hp(int); staticfn void interrupt_multi(const char *); -staticfn void debug_fields(const char *); +staticfn void debug_fields(char *); #ifndef NODUMPENUMS staticfn void dump_enums(void); #endif @@ -1041,8 +1041,11 @@ argcheck(int argc, char *argv[], enum earlyarg e_arg) switch(e_arg) { case ARG_DEBUG: if (extended_opt) { - extended_opt++; - debug_fields(extended_opt); + char *cpy_extended_opt; + + cpy_extended_opt = dupstr(extended_opt); + debug_fields(cpy_extended_opt + 1); + free((genericptr_t) cpy_extended_opt); } return 1; case ARG_VERSION: { @@ -1125,7 +1128,7 @@ argcheck(int argc, char *argv[], enum earlyarg e_arg) * fuzzer - enable fuzzer without debugger intervention. */ staticfn void -debug_fields(const char *opts) +debug_fields(char *opts) { char *op; boolean negated = FALSE; diff --git a/src/cfgfiles.c b/src/cfgfiles.c index ca05d35cd..2084866d0 100644 --- a/src/cfgfiles.c +++ b/src/cfgfiles.c @@ -34,7 +34,7 @@ staticfn void free_config_sections(void); staticfn char *is_config_section(char *); staticfn boolean handle_config_section(char *); boolean parse_config_line(char *); -staticfn char *find_optparam(const char *); +staticfn char *find_optparam(char *); #ifndef SFCTOOL staticfn boolean cnf_line_OPTIONS(char *); staticfn boolean cnf_line_AUTOPICKUP_EXCEPTION(char *); @@ -583,7 +583,7 @@ handle_config_section(char *buf) /* find the '=' or ':' */ staticfn char * -find_optparam(const char *buf) +find_optparam(char *buf) { char *bufp, *altp; diff --git a/src/end.c b/src/end.c index 6d8da53a7..389d8f548 100644 --- a/src/end.c +++ b/src/end.c @@ -476,7 +476,8 @@ staticfn boolean should_query_disclose_option(int category, char *defquery) { int idx; - char disclose, *dop; + char disclose; + const char *dop; *defquery = 'n'; if ((dop = strchr(disclosure_options, category)) != 0) { diff --git a/src/invent.c b/src/invent.c index 713bd7d7a..a34517acf 100644 --- a/src/invent.c +++ b/src/invent.c @@ -159,7 +159,7 @@ loot_classify(Loot *sort_item, struct obj *obj) }; static char armcat[8]; const char *classorder; - char *p; + const char *p; int k, otyp = obj->otyp, oclass = obj->oclass; boolean seen, discovered = objects[otyp].oc_name_known ? TRUE : FALSE; diff --git a/src/o_init.c b/src/o_init.c index 67fd4d21d..5bb82d2e2 100644 --- a/src/o_init.c +++ b/src/o_init.c @@ -764,9 +764,10 @@ int dodiscovered(void) /* free after Robert Viduya */ { winid tmpwin; - char *s, *p, oclass, prev_class, + char *s, oclass, prev_class, classes[MAXOCLASSES], buf[BUFSZ], *sorted_lines[NUM_OBJECTS]; /* overkill */ + const char *p; int i, dis, ct, uniq_ct, arti_ct, sorted_ct, uidx; long sortindx; // should be ptrdiff_t, but we don't require that exists boolean alphabetized, alphabyclass, lootsort; @@ -897,9 +898,10 @@ doclassdisco(void) winid tmpwin = WIN_ERR; menu_item *pick_list = 0; anything any; - char *p, *s, c, oclass, menulet, allclasses[MAXOCLASSES], + char *s, c, oclass, menulet, allclasses[MAXOCLASSES], discosyms[3 + MAXOCLASSES + 1], buf[BUFSZ], *sorted_lines[NUM_OBJECTS]; /* overkill */ + const char *p; int i, ct, dis, xtras, sorted_ct, uidx; boolean traditional, alphabetized, lootsort; int clr = NO_COLOR; @@ -1105,12 +1107,14 @@ doclassdisco(void) } else if (sorted_ct) { qsort(sorted_lines, sorted_ct, sizeof (char *), discovered_cmp); for (i = 0; i < sorted_ct; ++i) { - p = sorted_lines[i]; + char *sl; + + sl = sorted_lines[i]; if (lootsort) { - p[6] = p[0]; /* '*' or ' ' */ - p += 6; + sl[6] = sl[0]; /* '*' or ' ' */ + sl += 6; } - putstr(tmpwin, 0, p); + putstr(tmpwin, 0, sl); free(sorted_lines[i]), sorted_lines[i] = 0; } } diff --git a/src/objnam.c b/src/objnam.c index 7c5a2d637..1246029fd 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -2193,7 +2193,8 @@ the(const char *str) insert_the = TRUE; } else { /* Probably a proper name, might not need an article */ - char *tmp, *named, *called; + char *named, *called; + const char *tmp; int l; /* some objects have capitalized adjectives in their names */ diff --git a/src/options.c b/src/options.c index 6f5a565ae..c22105c03 100644 --- a/src/options.c +++ b/src/options.c @@ -1519,7 +1519,8 @@ optfn_disclose( DISCLOSE_NO_WITHOUT_PROMPT, DISCLOSE_SPECIAL_WITHOUT_PROMPT, '\0' }; - char c, *dop; + char c; + const char *dop; c = lowc(*op); if (c == 'k') diff --git a/src/pager.c b/src/pager.c index f016cd258..82b8bc7eb 100644 --- a/src/pager.c +++ b/src/pager.c @@ -1143,7 +1143,8 @@ add_cmap_descr( const char **firstmatch, /* output: pointer to 1st matching description */ char *out_str) /* input/output: current description gets appended */ { - char *mbuf = NULL, *p; + char *mbuf = NULL; + const char *p; int absidx = abs(idx); if (glyph == NO_GLYPH) { From aa25005cf027c47a1f3dc97e5ef6087310656f94 Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 31 Mar 2026 12:57:41 -0400 Subject: [PATCH 374/442] whitespace cleanup on recent file replace leading tab with spaces to match include style --- include/amiconf.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/amiconf.h b/include/amiconf.h index c729e6c30..77baa09f1 100644 --- a/include/amiconf.h +++ b/include/amiconf.h @@ -193,21 +193,21 @@ void amii_setpens(int); #define M(c) ((c) -128) #endif struct ami_sysflags { - char sysflagsid[10]; + char sysflagsid[10]; #ifdef AMIFLUSH - boolean altmeta; /* use ALT keys as META */ - boolean amiflush; /* kill typeahead */ + boolean altmeta; /* use ALT keys as META */ + boolean amiflush; /* kill typeahead */ #endif #ifdef AMII_GRAPHICS - int numcols; - unsigned short amii_dripens[20]; /* DrawInfo Pens currently there are 13 in v39 */ - AMII_COLOR_TYPE amii_curmap[AMII_MAXCOLORS]; /* colormap */ + int numcols; + unsigned short amii_dripens[20]; /* DrawInfo Pens currently there are 13 in v39 */ + AMII_COLOR_TYPE amii_curmap[AMII_MAXCOLORS]; /* colormap */ #endif #ifdef OPT_DISPMAP - boolean fast_map; /* use optimized, less flexible map display */ + boolean fast_map; /* use optimized, less flexible map display */ #endif #ifdef MFLOPPY - boolean asksavedisk; + boolean asksavedisk; #endif }; extern struct ami_sysflags sysflags; From c8e97527f11c7e75c91783c39c253635b60713d1 Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 31 Mar 2026 20:48:01 -0700 Subject: [PATCH 375/442] Schroedinger's live cat Give a little experience when releasing live housecat from Schroedinger's Box, similar to recent change giving experience when opening the Box produces a dead cat. --- src/pickup.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/pickup.c b/src/pickup.c index 73c513135..331933137 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -2857,6 +2857,13 @@ observe_quantum_cat(struct obj *box, boolean makecat, boolean givemsg) } box->owt = weight(box); box->spe = 0; + + if (!svc.context.mon_moving) { + /* give experience points for releasing live cat; slightly + different amount from what is given for "killing" it */ + more_experienced(10, 20); /* 10:current exp; 20:score bonus */ + newexplevel(); + } } } else { box->spe = 0; /* now an ordinary box (with a cat corpse inside) */ From 3917f5493d90f19a151c87b012ff237acb7f601c Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Wed, 1 Apr 2026 13:17:05 +0300 Subject: [PATCH 376/442] More mcastu code reorg Instead of using two separate functions with switch-cases for wizard and clerical spell lists, define the spell lists as arrays and use a single function to pick a spell from the lists. Adds levels to the monster spells, using the switch-case values, with some minor fudging. --- include/mcastu.h | 44 ++++++------- src/mcastu.c | 163 +++++++++++++++++------------------------------ 2 files changed, 82 insertions(+), 125 deletions(-) diff --git a/include/mcastu.h b/include/mcastu.h index fd59871c9..dd1e37420 100644 --- a/include/mcastu.h +++ b/include/mcastu.h @@ -6,30 +6,30 @@ #define MCF_HOSTILE 0x0004 /* cast by hostile monsters only */ #if defined(MCASTU_ENUM) -#define MONSPELL(def, flags) MCAST_##def +#define MONSPELL(def, lvl, flags) MCAST_##def #elif defined(MCASTU_INIT) -#define MONSPELL(def, flags) flags +#define MONSPELL(def, lvl, flags) { lvl, flags } #endif -MONSPELL(PSI_BOLT, MCF_HOSTILE|MCF_SIGHT), -MONSPELL(OPEN_WOUNDS, MCF_HOSTILE|MCF_SIGHT), -MONSPELL(LIGHTNING, MCF_HOSTILE|MCF_SIGHT), -MONSPELL(FIRE_PILLAR, MCF_HOSTILE|MCF_SIGHT), -MONSPELL(GEYSER, MCF_HOSTILE|MCF_SIGHT), -MONSPELL(DEATH_TOUCH, MCF_HOSTILE|MCF_SIGHT), -MONSPELL(CURE_SELF, MCF_INDIRECT), -MONSPELL(HASTE_SELF, MCF_INDIRECT), -MONSPELL(DISAPPEAR, MCF_INDIRECT), -MONSPELL(AGGRAVATION, MCF_INDIRECT|MCF_HOSTILE|MCF_SIGHT), -MONSPELL(STUN_YOU, MCF_HOSTILE|MCF_SIGHT), -MONSPELL(WEAKEN_YOU, MCF_HOSTILE|MCF_SIGHT), -MONSPELL(CONFUSE_YOU, MCF_HOSTILE|MCF_SIGHT), -MONSPELL(PARALYZE, MCF_HOSTILE|MCF_SIGHT), -MONSPELL(BLIND_YOU, MCF_HOSTILE|MCF_SIGHT), -MONSPELL(DESTRY_ARMR, MCF_HOSTILE|MCF_SIGHT), -MONSPELL(CURSE_ITEMS, MCF_HOSTILE|MCF_SIGHT), -MONSPELL(INSECTS, MCF_HOSTILE|MCF_INDIRECT|MCF_SIGHT), -MONSPELL(SUMMON_MONS, MCF_HOSTILE|MCF_INDIRECT|MCF_SIGHT), -MONSPELL(CLONE_WIZ, MCF_HOSTILE|MCF_INDIRECT|MCF_SIGHT), +MONSPELL(PSI_BOLT, 0, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(OPEN_WOUNDS, 0, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(CURE_SELF, 1, MCF_INDIRECT), +MONSPELL(HASTE_SELF, 2, MCF_INDIRECT), +MONSPELL(CONFUSE_YOU, 2, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(STUN_YOU, 3, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(DISAPPEAR, 4, MCF_INDIRECT), +MONSPELL(PARALYZE, 4, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(BLIND_YOU, 6, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(WEAKEN_YOU, 6, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(DESTRY_ARMR, 8, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(INSECTS, 8, MCF_HOSTILE|MCF_INDIRECT|MCF_SIGHT), +MONSPELL(CURSE_ITEMS, 10, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(LIGHTNING, 11, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(FIRE_PILLAR, 12, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(GEYSER, 13, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(AGGRAVATION, 13, MCF_INDIRECT|MCF_HOSTILE|MCF_SIGHT), +MONSPELL(SUMMON_MONS, 15, MCF_HOSTILE|MCF_INDIRECT|MCF_SIGHT), +MONSPELL(CLONE_WIZ, 18, MCF_HOSTILE|MCF_INDIRECT|MCF_SIGHT), +MONSPELL(DEATH_TOUCH, 20, MCF_HOSTILE|MCF_SIGHT), #undef MONSPELL diff --git a/src/mcastu.c b/src/mcastu.c index 0d8af9d68..34d839906 100644 --- a/src/mcastu.c +++ b/src/mcastu.c @@ -11,15 +11,32 @@ enum mcast_spells { }; #undef MCASTU_ENUM +struct _mcast_data { + int level; + int flags; +}; + #define MCASTU_INIT -static int mcast_flags[] = { +static struct _mcast_data mcast_data[] = { #include "mcastu.h" }; #undef MCASTU_INIT +/* spell lists for specific monster casters */ +/* the spells in the list should be in ascending level order */ +static int mon_cleric_spells[] = { + MCAST_OPEN_WOUNDS, MCAST_CURE_SELF, MCAST_CONFUSE_YOU, MCAST_PARALYZE, + MCAST_BLIND_YOU, MCAST_INSECTS, MCAST_CURSE_ITEMS, MCAST_LIGHTNING, + MCAST_FIRE_PILLAR, MCAST_GEYSER +}; +static int mon_wizard_spells[] = { + MCAST_PSI_BOLT, MCAST_CURE_SELF, MCAST_HASTE_SELF, MCAST_STUN_YOU, + MCAST_DISAPPEAR, MCAST_WEAKEN_YOU, MCAST_DESTRY_ARMR, MCAST_CURSE_ITEMS, + MCAST_AGGRAVATION, MCAST_SUMMON_MONS, MCAST_CLONE_WIZ, MCAST_DEATH_TOUCH +}; + staticfn void cursetxt(struct monst *, boolean); -staticfn int choose_magic_spell(struct monst *); -staticfn int choose_clerical_spell(struct monst *); +staticfn int choose_monster_spell(struct monst *, int); staticfn int m_cure_self(struct monst *, int); staticfn void mcast_death_touch(struct monst *); staticfn void mcast_clone_wiz(struct monst *); @@ -67,107 +84,42 @@ cursetxt(struct monst *mtmp, boolean undirected) } } -/* convert a level-based random selection into a specific mage spell; - inappropriate choices will be screened out by spell_would_be_useless() */ +/* choose a spell for monster to cast */ staticfn int -choose_magic_spell(struct monst *mtmp) +choose_monster_spell(struct monst *mtmp, int adtyp) { - int spellval = rn2(mtmp->m_lev); + int *list = NULL; + int i, spellval, len = 0; + int maxlev; - /* for 3.4.3 and earlier, val greater than 22 selected default spell */ - while (spellval > 24 && rn2(25)) - spellval = rn2(spellval); + /* which spell list to use? */ + if (adtyp == AD_SPEL) { + list = mon_wizard_spells; + len = SIZE(mon_wizard_spells); + } else if (adtyp == AD_CLRC) { + list = mon_cleric_spells; + len = SIZE(mon_cleric_spells); + } - switch (spellval) { - case 24: - case 23: - if (Antimagic || Hallucination) - return MCAST_PSI_BOLT; - FALLTHROUGH; - /*FALLTHRU*/ - case 22: - case 21: - case 20: - return MCAST_DEATH_TOUCH; - case 19: - case 18: - return MCAST_CLONE_WIZ; - case 17: - case 16: - case 15: - return MCAST_SUMMON_MONS; - case 14: - case 13: - return MCAST_AGGRAVATION; - case 12: - case 11: - case 10: - return MCAST_CURSE_ITEMS; - case 9: - case 8: - return MCAST_DESTRY_ARMR; - case 7: - case 6: - return MCAST_WEAKEN_YOU; - case 5: - case 4: - return MCAST_DISAPPEAR; - case 3: - return MCAST_STUN_YOU; - case 2: - return MCAST_HASTE_SELF; - case 1: - return MCAST_CURE_SELF; - case 0: - default: + if (!list || len < 1) return MCAST_PSI_BOLT; - } -} -/* convert a level-based random selection into a specific cleric spell */ -staticfn int -choose_clerical_spell(struct monst *mtmp) -{ - int spellnum = rn2(mtmp->m_lev); + /* max spell level in this monster spell list */ + maxlev = mcast_data[list[len - 1]].level; - /* for 3.4.3 and earlier, num greater than 13 selected the default spell - */ - while (spellnum > 15 && rn2(16)) - spellnum = rn2(spellnum); + /* which level spell to cast? */ + spellval = rn2(mtmp->m_lev); + if (spellval > maxlev && rn2(maxlev)) + spellval = rn2(maxlev); - switch (spellnum) { - case 15: - case 14: - if (rn2(3)) - return MCAST_OPEN_WOUNDS; - FALLTHROUGH; - /*FALLTHRU*/ - case 13: - return MCAST_GEYSER; - case 12: - return MCAST_FIRE_PILLAR; - case 11: - return MCAST_LIGHTNING; - case 10: - case 9: - return MCAST_CURSE_ITEMS; - case 8: - return MCAST_INSECTS; - case 7: - case 6: - return MCAST_BLIND_YOU; - case 5: - case 4: - return MCAST_PARALYZE; - case 3: - case 2: - return MCAST_CONFUSE_YOU; - case 1: - return MCAST_CURE_SELF; - case 0: - default: - return MCAST_OPEN_WOUNDS; - } + /* find the highest spell in the list we could cast */ + for (i = len-1; i >= 0; i--) + if (mcast_data[list[i]].level <= spellval + && !spell_would_be_useless(mtmp, list[i])) + return list[i]; + + /* or return the first spell in the list */ + return list[0]; } /* return values: @@ -201,10 +153,7 @@ castmu( int cnt = 40; do { - if (mattk->adtyp == AD_SPEL) - spellnum = choose_magic_spell(mtmp); - else - spellnum = choose_clerical_spell(mtmp); + spellnum = choose_monster_spell(mtmp, mattk->adtyp); /* not trying to attack? don't allow directed spells */ if (!thinks_it_foundyou) { if (!is_undirected_spell(spellnum) @@ -950,7 +899,7 @@ mcast_spell(struct monst *mtmp, int dmg, int spellnum) staticfn boolean is_undirected_spell(int spellnum) { - if ((mcast_flags[spellnum] & MCF_INDIRECT) != 0) + if ((mcast_data[spellnum].flags & MCF_INDIRECT) != 0) return TRUE; return FALSE; } @@ -967,13 +916,13 @@ spell_would_be_useless(struct monst *mtmp, int spellnum) */ /* spell is only cast by hostile monsters */ - if ((mcast_flags[spellnum] & MCF_HOSTILE) != 0) { + if ((mcast_data[spellnum].flags & MCF_HOSTILE) != 0) { if (mtmp->mpeaceful) return TRUE; } /* spell needs the monster to see hero */ - if ((mcast_flags[spellnum] & MCF_SIGHT) != 0) { + if ((mcast_data[spellnum].flags & MCF_SIGHT) != 0) { boolean mcouldseeu = couldsee(mtmp->mx, mtmp->my); if (!mcouldseeu) @@ -981,6 +930,14 @@ spell_would_be_useless(struct monst *mtmp, int spellnum) } switch (spellnum) { + case MCAST_DEATH_TOUCH: + if ((Antimagic || Hallucination) && !rn2(2)) + return TRUE; + break; + case MCAST_GEYSER: + if (!rn2(5)) + return TRUE; + break; case MCAST_CLONE_WIZ: /* only the Wizard is allowed to clone himself */ if (!mtmp->iswiz || svc.context.no_of_wizards > 1) From 686618f34d872c11b1ef911b1414890852145d7f Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 1 Apr 2026 08:32:13 -0400 Subject: [PATCH 377/442] move early arg and enum dump processing to own file --- Cross-compiling | 9 +- include/extern.h | 5 +- include/mcastu.h | 2 + src/allmain.c | 392 --------------------- src/earlyarg.c | 416 +++++++++++++++++++++++ sys/msdos/Makefile.GCC | 43 +-- sys/unix/Makefile.src | 42 +-- sys/vms/Makefile.src | 5 +- sys/vms/Makefile_src.vms | 10 +- sys/windows/GNUmakefile | 2 +- sys/windows/Makefile.nmake | 216 ++++++------ sys/windows/vs/NetHack/NetHack.vcxproj | 1 + sys/windows/vs/NetHackW/NetHackW.vcxproj | 1 + 13 files changed, 589 insertions(+), 555 deletions(-) create mode 100755 src/earlyarg.c diff --git a/Cross-compiling b/Cross-compiling index 43155a024..285e824b5 100644 --- a/Cross-compiling +++ b/Cross-compiling @@ -369,10 +369,11 @@ Using the cross-compiler, build the following targets: src/dlb.c, src/do.c, src/do_name.c, src/do_wear.c, src/dog.c, src/dogmove.c, src/dokick.c, src/dothrow.c, src/drawing.c, src/dungeon.c, - src/eat.c, src/end.c, src/engrave.c, src/exper.c, - src/explode.c, src/extralev.c, src/files.c, - src/fountain.c, src/getpos.c, src/glyphs.c, - src/hack.c, src/hacklib.c, src/insight.c, + src/earlyarg.c src/eat.c, src/end.c, src/engrave.c, + src/exper.c, src/explode.c, src/extralev.c, + src/files.c, src/fountain.c, src/getpos.c, + src/glyphs.c, src/hack.c, src/hacklib.c, + src/iactions.c, src/insight.c, src/invent.c, src/isaac64.c, src/light.c, src/lock.c, src/mail.c, src/makemon.c, src/mcastu.c, src/mdlib.c, src/mhitm.c, src/mhitu.c, src/minion.c, diff --git a/include/extern.h b/include/extern.h index a3cc23213..cf20ca090 100644 --- a/include/extern.h +++ b/include/extern.h @@ -105,7 +105,6 @@ extern void stop_occupation(void); extern void init_sound_disp_gamewindows(void); extern void newgame(void); extern void welcome(boolean); -extern int argcheck(int, char **, enum earlyarg); extern long timet_to_seconds(time_t); extern long timet_delta(time_t, time_t); @@ -920,6 +919,10 @@ extern void overview_stats(winid, const char *, long *, long *) NONNULLPTRS; extern void remdun_mapseen(int); extern const char *endgamelevelname(char *, int); +/* ### earlyarg.c ### */ + +extern int argcheck(int, char **, enum earlyarg); + /* ### eat.c ### */ extern void eatmupdate(void); diff --git a/include/mcastu.h b/include/mcastu.h index dd1e37420..a4491bf75 100644 --- a/include/mcastu.h +++ b/include/mcastu.h @@ -9,6 +9,8 @@ #define MONSPELL(def, lvl, flags) MCAST_##def #elif defined(MCASTU_INIT) #define MONSPELL(def, lvl, flags) { lvl, flags } +#elif defined(DUMP_MCASTU_ENUM) +#define MONSPELL(def, lvl, flags) { MCAST_##def, #def } #endif MONSPELL(PSI_BOLT, 0, MCF_HOSTILE|MCF_SIGHT), diff --git a/src/allmain.c b/src/allmain.c index 4e0021eaf..e63db5826 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -21,10 +21,6 @@ staticfn void do_positionbar(void); staticfn void regen_pw(int); staticfn void regen_hp(int); staticfn void interrupt_multi(const char *); -staticfn void debug_fields(char *); -#ifndef NODUMPENUMS -staticfn void dump_enums(void); -#endif #ifdef CRASHREPORT #define USED_FOR_CRASHREPORT @@ -960,221 +956,6 @@ interrupt_multi(const char *msg) } } -/* - * Argument processing helpers - for xxmain() to share - * and call. - * - * These should return TRUE if the argument matched, - * whether the processing of the argument was - * successful or not. - * - * Most of these do their thing, then after returning - * to xxmain(), the code exits without starting a game. - * - */ - -static const struct early_opt earlyopts[] = { - { ARG_DEBUG, "debug", 5, TRUE }, - { ARG_VERSION, "version", 4, TRUE }, - { ARG_SHOWPATHS, "showpaths", 8, FALSE }, -#ifndef NODUMPENUMS - { ARG_DUMPENUMS, "dumpenums", 9, FALSE }, -#endif - { ARG_DUMPGLYPHIDS, "dumpglyphids", 12, FALSE }, - { ARG_DUMPMONGEN, "dumpmongen", 10, FALSE }, - { ARG_DUMPWEIGHTS, "dumpweights", 11, FALSE }, -#ifdef WIN32 - { ARG_WINDOWS, "windows", 4, TRUE }, -#endif -#if defined(CRASHREPORT) - { ARG_BIDSHOW, "bidshow", 7, FALSE }, -#endif -}; - -#ifdef WIN32 -extern int windows_early_options(const char *); -#endif - -/* - * Returns: - * 0 = no match - * 1 = found and skip past this argument - * 2 = found and trigger immediate exit - */ -int -argcheck(int argc, char *argv[], enum earlyarg e_arg) -{ - int i, idx; - boolean match = FALSE; - char *userea = (char *) 0; - const char *dashdash = ""; - - for (idx = 0; idx < SIZE(earlyopts); idx++) { - if (earlyopts[idx].e == e_arg){ - break; - } - } - if (idx >= SIZE(earlyopts) || argc < 1) - return 0; - - for (i = 0; i < argc; ++i) { - if (argv[i][0] != '-') - continue; - if (argv[i][1] == '-') { - userea = &argv[i][2]; - dashdash = "-"; - } else { - userea = &argv[i][1]; - } - match = match_optname(userea, earlyopts[idx].name, - earlyopts[idx].minlength, - earlyopts[idx].valallowed); - if (match) - break; - } - - if (match) { - const char *extended_opt = strchr(userea, ':'); - - if (!extended_opt) - extended_opt = strchr(userea, '='); - switch(e_arg) { - case ARG_DEBUG: - if (extended_opt) { - char *cpy_extended_opt; - - cpy_extended_opt = dupstr(extended_opt); - debug_fields(cpy_extended_opt + 1); - free((genericptr_t) cpy_extended_opt); - } - return 1; - case ARG_VERSION: { - boolean insert_into_pastebuf = FALSE; - - if (extended_opt) { - extended_opt++; - /* Deprecated in favor of "copy" - remove no later - than next major version */ - if (match_optname(extended_opt, "paste", 5, FALSE)) { - insert_into_pastebuf = TRUE; - } else if (match_optname(extended_opt, "copy", 4, FALSE)) { - insert_into_pastebuf = TRUE; - } else if (match_optname(extended_opt, "dump", 4, FALSE)) { - /* version number plus enabled features and sanity - values that the program compares against the same - thing recorded in save and bones files to check - whether they're being used compatibly */ - dump_version_info(); - return 2; /* done */ - } else if (!match_optname(extended_opt, "show", 4, FALSE)) { - raw_printf("-%sversion can only be extended with" - " -%sversion:copy or :dump or :show.\n", - dashdash, dashdash); - /* exit after we've reported bad command line argument */ - return 2; - } - } - early_version_info(insert_into_pastebuf); - return 2; - } - case ARG_SHOWPATHS: - return 2; -#ifndef NODUMPENUMS - case ARG_DUMPENUMS: - dump_enums(); - return 2; -#endif - case ARG_DUMPGLYPHIDS: - dump_glyphids(); - return 2; - case ARG_DUMPMONGEN: - dump_mongen(); - return 2; - case ARG_DUMPWEIGHTS: - dump_weights(); - return 2; -#ifdef CRASHREPORT - case ARG_BIDSHOW: - crashreport_bidshow(); - return 2; -#endif -#ifdef WIN32 - case ARG_WINDOWS: - if (extended_opt) { - extended_opt++; - return windows_early_options(extended_opt); - } - FALLTHROUGH; - /*FALLTHRU*/ -#endif - default: - break; - } - }; - return 0; -} - -/* - * These are internal controls to aid developers with - * testing and debugging particular aspects of the code. - * They are not player options and the only place they - * are documented is right here. No gameplay is altered. - * - * test - test whether this parser is working - * ttystatus - TTY: - * immediateflips - WIN32: turn off display performance - * optimization so that display output - * can be debugged without buffering. - * fuzzer - enable fuzzer without debugger intervention. - */ -staticfn void -debug_fields(char *opts) -{ - char *op; - boolean negated = FALSE; - - while ((op = strchr(opts, ',')) != 0) { - *op++ = 0; - /* recurse */ - debug_fields(op); - } - if (strlen(opts) > BUFSZ / 2) - return; - - - /* strip leading and trailing white space */ - while (isspace((uchar) *opts)) - opts++; - op = eos((char *) opts); - while (--op >= opts && isspace((uchar) *op)) - *op = '\0'; - - if (!*opts) { - /* empty */ - return; - } - while ((*opts == '!') || !strncmpi(opts, "no", 2)) { - if (*opts == '!') - opts++; - else - opts += 2; - negated = !negated; - } - if (match_optname(opts, "test", 4, FALSE)) - iflags.debug.test = negated ? FALSE : TRUE; -#ifdef TTY_GRAPHICS - if (match_optname(opts, "ttystatus", 9, FALSE)) - iflags.debug.ttystatus = negated ? FALSE : TRUE; -#endif -#ifdef WIN32 - if (match_optname(opts, "immediateflips", 14, FALSE)) - iflags.debug.immediateflips = negated ? FALSE : TRUE; -#endif - if (match_optname(opts, "fuzzer", 4, FALSE)) - iflags.fuzzerpending = TRUE; - return; -} - /* convert from time_t to number of seconds */ long timet_to_seconds(time_t ttim) @@ -1193,177 +974,4 @@ timet_delta(time_t etim, time_t stim) /* end and start times */ return (long) difftime(etim, stim); } -#if !defined(NODUMPENUMS) -/* monsdump[] and objdump[] are also used in utf8map.c */ - -#define DUMP_ENUMS -#define UNPREFIXED_COUNT (5) -struct enum_dump monsdump[] = { -#include "monsters.h" - { NUMMONS, "NUMMONS" }, - { NON_PM, "NON_PM" }, - { LOW_PM, "LOW_PM" }, - { HIGH_PM, "HIGH_PM" }, - { SPECIAL_PM, "SPECIAL_PM" } -}; -struct enum_dump objdump[] = { -#include "objects.h" - { NUM_OBJECTS, "NUM_OBJECTS" }, -}; - -#define DUMP_ENUMS_PCHAR -static struct enum_dump defsym_cmap_dump[] = { -#include "defsym.h" - { MAXPCHARS, "MAXPCHARS" }, -}; -#undef DUMP_ENUMS_PCHAR - -#define DUMP_ENUMS_MONSYMS -static struct enum_dump defsym_mon_syms_dump[] = { -#include "defsym.h" - { MAXMCLASSES, "MAXMCLASSES" }, -}; -#undef DUMP_ENUMS_MONSYMS - -#define DUMP_ENUMS_MONSYMS_DEFCHAR -static struct enum_dump defsym_mon_defchars_dump[] = { -#include "defsym.h" -}; -#undef DUMP_ENUMS_MONSYMS_DEFCHAR - -#define DUMP_ENUMS_OBJCLASS_DEFCHARS -static struct enum_dump objclass_defchars_dump[] = { -#include "defsym.h" -}; -#undef DUMP_ENUMS_OBJCLASS_DEFCHARS - -#define DUMP_ENUMS_OBJCLASS_CLASSES -static struct enum_dump objclass_classes_dump[] = { -#include "defsym.h" - { MAXOCLASSES, "MAXOCLASSES" }, -}; -#undef DUMP_ENUMS_OBJCLASS_CLASSES - -#define DUMP_ENUMS_OBJCLASS_SYMS -static struct enum_dump objclass_syms_dump[] = { -#include "defsym.h" -}; -#undef DUMP_ENUMS_OBJCLASS_SYMS - -#define DUMP_ARTI_ENUM -static struct enum_dump arti_enum_dump[] = { -#include "artilist.h" - { AFTER_LAST_ARTIFACT, "AFTER_LAST_ARTIFACT" } -}; -#undef DUMP_ARTI_ENUM - -#undef DUMP_ENUMS - - -#ifndef NODUMPENUMS - -staticfn void -dump_enums(void) -{ - enum enum_dumps { - monsters_enum, - objects_enum, - objects_misc_enum, - defsym_cmap_enum, - defsym_mon_syms_enum, - defsym_mon_defchars_enum, - objclass_defchars_enum, - objclass_classes_enum, - objclass_syms_enum, - arti_enum, - NUM_ENUM_DUMPS - }; - -#define dump_om(om) { om, #om } - static const struct enum_dump omdump[] = { - dump_om(LAST_GENERIC), - dump_om(OBJCLASS_HACK), - dump_om(FIRST_OBJECT), - dump_om(FIRST_AMULET), - dump_om(LAST_AMULET), - dump_om(FIRST_SPELL), - dump_om(LAST_SPELL), - dump_om(MAXSPELL), - dump_om(FIRST_REAL_GEM), - dump_om(LAST_REAL_GEM), - dump_om(FIRST_GLASS_GEM), - dump_om(LAST_GLASS_GEM), - dump_om(NUM_REAL_GEMS), - dump_om(NUM_GLASS_GEMS), - dump_om(MAX_GLYPH), - }; -#undef dump_om - - static const struct enum_dump *const ed[NUM_ENUM_DUMPS] = { - monsdump, objdump, omdump, - defsym_cmap_dump, defsym_mon_syms_dump, - defsym_mon_defchars_dump, - objclass_defchars_dump, - objclass_classes_dump, - objclass_syms_dump, - arti_enum_dump, - }; - - static const struct de_params { - const char *const title; - const char *const pfx; - int unprefixed_count; - int dumpflgs; /* 0 = dump numerically only, 1 = add 'char' comment */ - int szd; - } edmp[NUM_ENUM_DUMPS] = { - { "monnums", "PM_", UNPREFIXED_COUNT, 0, SIZE(monsdump) }, - { "objects_nums", "", 1, 0, SIZE(objdump) }, - { "misc_object_nums", "", 1, 0, SIZE(omdump) }, - { "cmap_symbols", "", 1, 0, SIZE(defsym_cmap_dump) }, - { "mon_syms", "", 1, 0, SIZE(defsym_mon_syms_dump) }, - { "mon_defchars", "", 1, 1, SIZE(defsym_mon_defchars_dump) }, - { "objclass_defchars", "", 1, 1, SIZE(objclass_defchars_dump) }, - { "objclass_classes", "", 1, 0, SIZE(objclass_classes_dump) }, - { "objclass_syms", "", 1, 0, SIZE(objclass_syms_dump) }, - { "artifacts_nums", "", 1, 0, SIZE(arti_enum_dump) }, - }; - - const char *nmprefix; - int i, j, nmwidth; - char comment[BUFSZ]; - - for (i = 0; i < NUM_ENUM_DUMPS; ++ i) { - raw_printf("enum %s = {", edmp[i].title); - for (j = 0; j < edmp[i].szd; ++j) { - nmprefix = (j >= edmp[i].szd - edmp[i].unprefixed_count) - ? "" : edmp[i].pfx; /* "" or "PM_" */ - nmwidth = 27 - (int) strlen(nmprefix); /* 27 or 24 */ - if (edmp[i].dumpflgs > 0) { - Snprintf(comment, sizeof comment, - " /* '%c' */", - (ed[i][j].val >= 32 && ed[i][j].val <= 126) - ? ed[i][j].val : ' '); - } else { - comment[0] = '\0'; - } - raw_printf(" %s%*s = %3d,%s", - nmprefix, -nmwidth, - ed[i][j].nm, ed[i][j].val, - comment); - } - raw_print("};"); - raw_print(""); - } - raw_print(""); -} -#undef UNPREFIXED_COUNT -#endif /* NODUMPENUMS */ - -void -dump_glyphids(void) -{ - dump_all_glyphids(stdout); -} -#endif /* !NODUMPENUMS */ - /*allmain.c*/ diff --git a/src/earlyarg.c b/src/earlyarg.c new file mode 100755 index 000000000..e0fd2a56b --- /dev/null +++ b/src/earlyarg.c @@ -0,0 +1,416 @@ +/* NetHack 3.7 enumdmp.c $NHDT-Date: 1771213100 2026/02/15 19:38:20 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.286 $ */ +/* Copyright (c) Robert Patrick Rankin, 2012. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +staticfn void debug_fields(char *); +#ifndef NODUMPENUMS +staticfn void dump_enums(void); +#endif + +/* + * Argument processing helpers - for xxmain() to share + * and call. + * + * These should return TRUE if the argument matched, + * whether the processing of the argument was + * successful or not. + * + * Most of these do their thing, then after returning + * to xxmain(), the code exits without starting a game. + * + */ + +static const struct early_opt earlyopts[] = { + { ARG_DEBUG, "debug", 5, TRUE }, + { ARG_VERSION, "version", 4, TRUE }, + { ARG_SHOWPATHS, "showpaths", 8, FALSE }, +#ifndef NODUMPENUMS + { ARG_DUMPENUMS, "dumpenums", 9, FALSE }, +#endif + { ARG_DUMPGLYPHIDS, "dumpglyphids", 12, FALSE }, + { ARG_DUMPMONGEN, "dumpmongen", 10, FALSE }, + { ARG_DUMPWEIGHTS, "dumpweights", 11, FALSE }, +#ifdef WIN32 + { ARG_WINDOWS, "windows", 4, TRUE }, +#endif +#if defined(CRASHREPORT) + { ARG_BIDSHOW, "bidshow", 7, FALSE }, +#endif +}; + +#ifdef WIN32 +extern int windows_early_options(const char *); +#endif + +/* + * Returns: + * 0 = no match + * 1 = found and skip past this argument + * 2 = found and trigger immediate exit + */ +int +argcheck(int argc, char *argv[], enum earlyarg e_arg) +{ + int i, idx; + boolean match = FALSE; + char *userea = (char *) 0; + const char *dashdash = ""; + + for (idx = 0; idx < SIZE(earlyopts); idx++) { + if (earlyopts[idx].e == e_arg){ + break; + } + } + if (idx >= SIZE(earlyopts) || argc < 1) + return 0; + + for (i = 0; i < argc; ++i) { + if (argv[i][0] != '-') + continue; + if (argv[i][1] == '-') { + userea = &argv[i][2]; + dashdash = "-"; + } else { + userea = &argv[i][1]; + } + match = match_optname(userea, earlyopts[idx].name, + earlyopts[idx].minlength, + earlyopts[idx].valallowed); + if (match) + break; + } + + if (match) { + const char *extended_opt = strchr(userea, ':'); + + if (!extended_opt) + extended_opt = strchr(userea, '='); + switch(e_arg) { + case ARG_DEBUG: + if (extended_opt) { + char *cpy_extended_opt; + + cpy_extended_opt = dupstr(extended_opt); + debug_fields(cpy_extended_opt + 1); + free((genericptr_t) cpy_extended_opt); + } + return 1; + case ARG_VERSION: { + boolean insert_into_pastebuf = FALSE; + + if (extended_opt) { + extended_opt++; + /* Deprecated in favor of "copy" - remove no later + than next major version */ + if (match_optname(extended_opt, "paste", 5, FALSE)) { + insert_into_pastebuf = TRUE; + } else if (match_optname(extended_opt, "copy", 4, FALSE)) { + insert_into_pastebuf = TRUE; + } else if (match_optname(extended_opt, "dump", 4, FALSE)) { + /* version number plus enabled features and sanity + values that the program compares against the same + thing recorded in save and bones files to check + whether they're being used compatibly */ + dump_version_info(); + return 2; /* done */ + } else if (!match_optname(extended_opt, "show", 4, FALSE)) { + raw_printf("-%sversion can only be extended with" + " -%sversion:copy or :dump or :show.\n", + dashdash, dashdash); + /* exit after we've reported bad command line argument */ + return 2; + } + } + early_version_info(insert_into_pastebuf); + return 2; + } + case ARG_SHOWPATHS: + return 2; +#ifndef NODUMPENUMS + case ARG_DUMPENUMS: + dump_enums(); + return 2; +#endif + case ARG_DUMPGLYPHIDS: + dump_glyphids(); + return 2; + case ARG_DUMPMONGEN: + dump_mongen(); + return 2; + case ARG_DUMPWEIGHTS: + dump_weights(); + return 2; +#ifdef CRASHREPORT + case ARG_BIDSHOW: + crashreport_bidshow(); + return 2; +#endif +#ifdef WIN32 + case ARG_WINDOWS: + if (extended_opt) { + extended_opt++; + return windows_early_options(extended_opt); + } + FALLTHROUGH; + /*FALLTHRU*/ +#endif + default: + break; + } + }; + return 0; +} + +/* + * These are internal controls to aid developers with + * testing and debugging particular aspects of the code. + * They are not player options and the only place they + * are documented is right here. No gameplay is altered. + * + * test - test whether this parser is working + * ttystatus - TTY: + * immediateflips - WIN32: turn off display performance + * optimization so that display output + * can be debugged without buffering. + * fuzzer - enable fuzzer without debugger intervention. + */ +staticfn void +debug_fields(char *opts) +{ + char *op; + boolean negated = FALSE; + + while ((op = strchr(opts, ',')) != 0) { + *op++ = 0; + /* recurse */ + debug_fields(op); + } + if (strlen(opts) > BUFSZ / 2) + return; + + + /* strip leading and trailing white space */ + while (isspace((uchar) *opts)) + opts++; + op = eos((char *) opts); + while (--op >= opts && isspace((uchar) *op)) + *op = '\0'; + + if (!*opts) { + /* empty */ + return; + } + while ((*opts == '!') || !strncmpi(opts, "no", 2)) { + if (*opts == '!') + opts++; + else + opts += 2; + negated = !negated; + } + if (match_optname(opts, "test", 4, FALSE)) + iflags.debug.test = negated ? FALSE : TRUE; +#ifdef TTY_GRAPHICS + if (match_optname(opts, "ttystatus", 9, FALSE)) + iflags.debug.ttystatus = negated ? FALSE : TRUE; +#endif +#ifdef WIN32 + if (match_optname(opts, "immediateflips", 14, FALSE)) + iflags.debug.immediateflips = negated ? FALSE : TRUE; +#endif + if (match_optname(opts, "fuzzer", 4, FALSE)) + iflags.fuzzerpending = TRUE; + return; +} + +#if !defined(NODUMPENUMS) +/* monsdump[] and objdump[] are also used in utf8map.c */ + +#define DUMP_ENUMS +#define UNPREFIXED_COUNT (5) +struct enum_dump monsdump[] = { +#include "monsters.h" + { NUMMONS, "NUMMONS" }, + { NON_PM, "NON_PM" }, + { LOW_PM, "LOW_PM" }, + { HIGH_PM, "HIGH_PM" }, + { SPECIAL_PM, "SPECIAL_PM" } +}; +struct enum_dump objdump[] = { +#include "objects.h" + { NUM_OBJECTS, "NUM_OBJECTS" }, +}; + +#define DUMP_ENUMS_PCHAR +static struct enum_dump defsym_cmap_dump[] = { +#include "defsym.h" + { MAXPCHARS, "MAXPCHARS" }, +}; +#undef DUMP_ENUMS_PCHAR + +#define DUMP_ENUMS_MONSYMS +static struct enum_dump defsym_mon_syms_dump[] = { +#include "defsym.h" + { MAXMCLASSES, "MAXMCLASSES" }, +}; +#undef DUMP_ENUMS_MONSYMS + +#define DUMP_ENUMS_MONSYMS_DEFCHAR +static struct enum_dump defsym_mon_defchars_dump[] = { +#include "defsym.h" +}; +#undef DUMP_ENUMS_MONSYMS_DEFCHAR + +#define DUMP_ENUMS_OBJCLASS_DEFCHARS +static struct enum_dump objclass_defchars_dump[] = { +#include "defsym.h" +}; +#undef DUMP_ENUMS_OBJCLASS_DEFCHARS + +#define DUMP_ENUMS_OBJCLASS_CLASSES +static struct enum_dump objclass_classes_dump[] = { +#include "defsym.h" + { MAXOCLASSES, "MAXOCLASSES" }, +}; +#undef DUMP_ENUMS_OBJCLASS_CLASSES + +#define DUMP_ENUMS_OBJCLASS_SYMS +static struct enum_dump objclass_syms_dump[] = { +#include "defsym.h" +}; +#undef DUMP_ENUMS_OBJCLASS_SYMS + +#define DUMP_ARTI_ENUM +static struct enum_dump arti_enum_dump[] = { +#include "artilist.h" + { AFTER_LAST_ARTIFACT, "AFTER_LAST_ARTIFACT" } +}; +#undef DUMP_ARTI_ENUM + +/* the enums are not part of hack.h for this one */ +#define MCASTU_ENUM +enum mcast_spells { + #include "mcastu.h" +}; +#undef MCASTU_ENUM + +#define DUMP_MCASTU_ENUM +static struct enum_dump mcastu_enum_dump[] = { +#include "mcastu.h" +}; +#undef DUMP_MCASTU_ENUM + +#undef DUMP_ENUMS + + +#ifndef NODUMPENUMS + +staticfn void +dump_enums(void) +{ + enum enum_dumps { + monsters_enum, + objects_enum, + objects_misc_enum, + defsym_cmap_enum, + defsym_mon_syms_enum, + defsym_mon_defchars_enum, + objclass_defchars_enum, + objclass_classes_enum, + objclass_syms_enum, + arti_enum, + mcastu_enum, + NUM_ENUM_DUMPS + }; + +#define dump_om(om) { om, #om } + static const struct enum_dump omdump[] = { + dump_om(LAST_GENERIC), + dump_om(OBJCLASS_HACK), + dump_om(FIRST_OBJECT), + dump_om(FIRST_AMULET), + dump_om(LAST_AMULET), + dump_om(FIRST_SPELL), + dump_om(LAST_SPELL), + dump_om(MAXSPELL), + dump_om(FIRST_REAL_GEM), + dump_om(LAST_REAL_GEM), + dump_om(FIRST_GLASS_GEM), + dump_om(LAST_GLASS_GEM), + dump_om(NUM_REAL_GEMS), + dump_om(NUM_GLASS_GEMS), + dump_om(MAX_GLYPH), + }; +#undef dump_om + + static const struct enum_dump *const ed[NUM_ENUM_DUMPS] = { + monsdump, objdump, omdump, + defsym_cmap_dump, defsym_mon_syms_dump, + defsym_mon_defchars_dump, + objclass_defchars_dump, + objclass_classes_dump, + objclass_syms_dump, + arti_enum_dump, + mcastu_enum_dump, + }; + + static const struct de_params { + const char *const title; + const char *const pfx; + int unprefixed_count; + int dumpflgs; /* 0 = dump numerically only, 1 = add 'char' comment */ + int szd; + } edmp[NUM_ENUM_DUMPS] = { + { "monnums", "PM_", UNPREFIXED_COUNT, 0, SIZE(monsdump) }, + { "objects_nums", "", 1, 0, SIZE(objdump) }, + { "misc_object_nums", "", 1, 0, SIZE(omdump) }, + { "cmap_symbols", "", 1, 0, SIZE(defsym_cmap_dump) }, + { "mon_syms", "", 1, 0, SIZE(defsym_mon_syms_dump) }, + { "mon_defchars", "", 1, 1, SIZE(defsym_mon_defchars_dump) }, + { "objclass_defchars", "", 1, 1, SIZE(objclass_defchars_dump) }, + { "objclass_classes", "", 1, 0, SIZE(objclass_classes_dump) }, + { "objclass_syms", "", 1, 0, SIZE(objclass_syms_dump) }, + { "artifacts_nums", "", 1, 0, SIZE(arti_enum_dump) }, + { "mcast_spells", "MCAST_", 0, 0, SIZE(mcastu_enum_dump) }, + }; + + const char *nmprefix; + int i, j, nmwidth; + char comment[BUFSZ]; + + for (i = 0; i < NUM_ENUM_DUMPS; ++ i) { + raw_printf("enum %s = {", edmp[i].title); + for (j = 0; j < edmp[i].szd; ++j) { + nmprefix = (j >= edmp[i].szd - edmp[i].unprefixed_count) + ? "" : edmp[i].pfx; /* "" or "PM_" */ + nmwidth = 27 - (int) strlen(nmprefix); /* 27 or 24 */ + if (edmp[i].dumpflgs > 0) { + Snprintf(comment, sizeof comment, + " /* '%c' */", + (ed[i][j].val >= 32 && ed[i][j].val <= 126) + ? ed[i][j].val : ' '); + } else { + comment[0] = '\0'; + } + raw_printf(" %s%*s = %3d,%s", + nmprefix, -nmwidth, + ed[i][j].nm, ed[i][j].val, + comment); + } + raw_print("};"); + raw_print(""); + } + raw_print(""); +} +#undef UNPREFIXED_COUNT +#endif /* NODUMPENUMS */ + +void +dump_glyphids(void) +{ + dump_all_glyphids(stdout); +} +#endif /* !NODUMPENUMS */ + +/*allmain.c*/ diff --git a/sys/msdos/Makefile.GCC b/sys/msdos/Makefile.GCC index 17d3a8bd8..782e1cd5c 100644 --- a/sys/msdos/Makefile.GCC +++ b/sys/msdos/Makefile.GCC @@ -276,27 +276,27 @@ VOBJ02 = $(O)ball.o $(O)bones.o $(O)botl.o $(O)calendar.o $(O)cfgfile VOBJ03 = $(O)cmd.o $(O)coloratt.o $(O)date.o $(O)dbridge.o $(O)decl.o VOBJ04 = $(O)detect.o $(O)dig.o $(O)display.o $(O)do.o $(O)do_name.o VOBJ05 = $(O)do_wear.o $(O)dog.o $(O)dogmove.o $(O)dokick.o $(O)dothrow.o -VOBJ06 = $(O)drawing.o $(O)dungeon.o $(O)eat.o $(O)end.o $(O)engrave.o -VOBJ07 = $(O)exper.o $(O)explode.o $(O)extralev.o $(O)files.o $(O)fountain.o -VOBJ08 = $(O)getpos.o $(O)glyphs.o $(O)getline.o $(O)hack.o $(O)hacklib.o -VOBJ09 = $(O)insight.o $(O)invent.o $(O)isaac64.o $(O)lock.o $(O)mail.o -VOBJ10 = $(O)main.o $(O)makemon.o $(O)mcastu.o $(O)mdlib.o $(O)mhitm.o -VOBJ11 = $(O)mhitu.o $(O)minion.o $(O)mkmap.o $(O)mklev.o $(O)mkmaze.o -VOBJ12 = $(O)mkobj.o $(O)mkroom.o $(O)mon.o $(O)mondata.o $(O)monmove.o -VOBJ13 = $(O)monst.o $(O)mplayer.o $(O)mthrowu.o $(O)muse.o $(O)music.o -VOBJ14 = $(O)o_init.o $(O)objects.o $(O)objnam.o $(O)options.o $(O)pickup.o -VOBJ15 = $(O)pline.o $(O)polyself.o $(O)potion.o $(O)quest.o $(O)questpgr.o -VOBJ16 = $(O)pager.o $(O)pray.o $(O)priest.o $(O)read.o $(O)rect.o -VOBJ17 = $(O)region.o $(O)report.o $(O)restore.o $(O)rip.o $(O)rnd.o -VOBJ18 = $(O)role.o $(O)rumors.o $(O)save.o $(O)selvar.o $(O)sfstruct.o -VOBJ19 = $(O)shk.o $(O)shknam.o $(O)sit.o $(O)sounds.o $(O)sp_lev.o -VOBJ20 = $(O)spell.o $(O)stairs.o $(O)steal.o $(O)steed.o $(O)symbols.o -VOBJ21 = $(O)sys.o $(O)teleport.o $(O)strutil.o $(O)termcap.o $(O)timeout.o -VOBJ22 = $(O)topl.o $(O)topten.o $(O)trap.o $(O)u_init.o $(O)uhitm.o -VOBJ23 = $(O)utf8map.o $(O)vault.o $(O)track.o $(O)vision.o $(O)weapon.o -VOBJ24 = $(O)were.o $(O)wield.o $(O)windows.o $(O)wintty.o $(O)wizard.o -VOBJ25 = $(O)wizcmds.o $(O)worm.o $(O)worn.o $(O)write.o $(O)zap.o -VOBJ26 = $(O)light.o $(O)dlb.o $(O)iactions.o $(REGEX) +VOBJ06 = $(O)drawing.o $(O)dungeon.o $(O)earlyarg.o $(O)eat.o $(O)end.o +VOBJ07 = $(O)engrave.o $(O)exper.o $(O)explode.o $(O)extralev.o $(O)files.o +VOBJ08 = $(O)fountain.o $(O)getpos.o $(O)glyphs.o $(O)getline.o $(O)hack.o +VOBJ09 = $(O)hacklib.o $(O)iactions.o $(O)insight.o $(O)invent.o $(O)isaac64.o +VOBJ10 = $(O)lock.o $(O)mail.o $(O)main.o $(O)makemon.o $(O)mcastu.o +VOBJ11 = $(O)mdlib.o $(O)mhitm.o $(O)mhitu.o $(O)minion.o $(O)mkmap.o +VOBJ12 = $(O)mklev.o $(O)mkmaze.o $(O)mkobj.o $(O)mkroom.o $(O)mon.o +VOBJ13 = $(O)mondata.o $(O)monmove.o $(O)monst.o $(O)mplayer.o $(O)mthrowu.o +VOBJ14 = $(O)muse.o $(O)music.o $(O)o_init.o $(O)objects.o $(O)objnam.o +VOBJ15 = $(O)options.o $(O)pickup.o $(O)pline.o $(O)polyself.o $(O)potion.o +VOBJ16 = $(O)quest.o $(O)questpgr.o $(O)pager.o $(O)pray.o $(O)priest.o +VOBJ17 = $(O)read.o $(O)rect.o $(O)region.o $(O)report.o $(O)restore.o +VOBJ18 = $(O)rip.o $(O)rnd.o $(O)role.o $(O)rumors.o $(O)save.o +VOBJ19 = $(O)selvar.o $(O)sfstruct.o $(O)shk.o $(O)shknam.o $(O)sit.o +VOBJ20 = $(O)sounds.o $(O)sp_lev.o $(O)spell.o $(O)stairs.o $(O)steal.o +VOBJ21 = $(O)steed.o $(O)symbols.o $(O)sys.o $(O)teleport.o $(O)strutil.o +VOBJ22 = $(O)termcap.o $(O)timeout.o $(O)topl.o $(O)topten.o $(O)trap.o +VOBJ23 = $(O)u_init.o $(O)uhitm.o $(O)utf8map.o $(O)vault.o $(O)track.o +VOBJ24 = $(O)vision.o $(O)weapon.o $(O)were.o $(O)wield.o $(O)windows.o +VOBJ25 = $(O)wintty.o $(O)wizard.o $(O)wizcmds.o $(O)worm.o $(O)worn.o +VOBJ26 = $(O)write.o $(O)zap.o $(O)light.o $(O)dlb.o $(REGEX) SOBJ = $(O)msdos.o $(O)pcsys.o $(O)tty.o $(O)unix.o \ $(O)video.o $(O)vidtxt.o $(O)pckeys.o @@ -1389,6 +1389,7 @@ $(TARGETPFX)drawing.o: drawing.c $(CONFIG_H) ../include/color.h \ ../include/objects.h ../include/wintype.h ../include/sym.h $(TARGETPFX)dungeon.o: dungeon.c $(HACK_H) ../include/dgn_file.h \ ../include/dlb.h +$(TARGETPFX)earlyarg.o: earlyarg.c $(HACK_H) $(TARGETPFX)eat.o: eat.c $(HACK_H) $(TARGETPFX)end.o: end.c $(HACK_H) ../include/dlb.h $(TARGETPFX)engrave.o: engrave.c $(HACK_H) diff --git a/sys/unix/Makefile.src b/sys/unix/Makefile.src index 861afc2d3..46dca7f71 100644 --- a/sys/unix/Makefile.src +++ b/sys/unix/Makefile.src @@ -517,7 +517,7 @@ HACK_H = ../src/hack.h-t HACKCSRC = allmain.c alloc.c apply.c artifact.c attrib.c ball.c bones.c \ botl.c calendar.c cmd.c coloratt.c dbridge.c decl.c detect.c dig.c display.c \ dlb.c do.c do_name.c do_wear.c dog.c dogmove.c dokick.c dothrow.c \ - drawing.c dungeon.c eat.c end.c engrave.c exper.c explode.c \ + drawing.c dungeon.c earlyarg.c eat.c end.c engrave.c exper.c explode.c \ extralev.c files.c fountain.c hack.c hacklib.c \ getpos.c glyphs.c iactions.c insight.c invent.c isaac64.c light.c \ lock.c mail.c makemon.c mcastu.c mdlib.c mhitm.c \ @@ -598,22 +598,21 @@ HOBJ = $(TARGETPFX)allmain.o $(TARGETPFX)alloc.o \ $(TARGETPFX)dlb.o $(TARGETPFX)do.o $(TARGETPFX)do_name.o \ $(TARGETPFX)do_wear.o $(TARGETPFX)dog.o $(TARGETPFX)dogmove.o \ $(TARGETPFX)dokick.o $(TARGETPFX)dothrow.o $(TARGETPFX)drawing.o \ - $(TARGETPFX)dungeon.o $(TARGETPFX)eat.o $(TARGETPFX)end.o \ - $(TARGETPFX)engrave.o $(TARGETPFX)exper.o $(TARGETPFX)explode.o \ - $(TARGETPFX)extralev.o $(TARGETPFX)files.o $(TARGETPFX)fountain.o \ - $(TARGETPFX)getpos.o $(TARGETPFX)glyphs.o $(TARGETPFX)hack.o \ - $(TARGETPFX)iactions.o \ - $(TARGETPFX)insight.o $(TARGETPFX)invent.o $(TARGETPFX)isaac64.o \ - $(TARGETPFX)light.o $(TARGETPFX)lock.o $(TARGETPFX)mail.o \ - $(TARGETPFX)makemon.o $(TARGETPFX)mcastu.o $(TARGETPFX)mdlib.o \ - $(TARGETPFX)mhitm.o $(TARGETPFX)mhitu.o $(TARGETPFX)minion.o \ - $(TARGETPFX)mklev.o $(TARGETPFX)mkmap.o $(TARGETPFX)mkmaze.o \ - $(TARGETPFX)mkobj.o $(TARGETPFX)mkroom.o $(TARGETPFX)mon.o \ - $(TARGETPFX)mondata.o $(TARGETPFX)monmove.o $(TARGETPFX)monst.o \ - $(TARGETPFX)mplayer.o $(TARGETPFX)mthrowu.o $(TARGETPFX)muse.o \ - $(TARGETPFX)music.o $(TARGETPFX)nhlua.o $(TARGETPFX)nhlsel.o \ - $(TARGETPFX)nhlobj.o $(TARGETPFX)nhmd4.o \ - $(TARGETPFX)objects.o $(TARGETPFX)o_init.o \ + $(TARGETPFX)dungeon.o $(TARGETPFX)earlyarg.o $(TARGETPFX)eat.o \ + $(TARGETPFX)end.o $(TARGETPFX)engrave.o $(TARGETPFX)exper.o \ + $(TARGETPFX)explode.o $(TARGETPFX)extralev.o $(TARGETPFX)files.o \ + $(TARGETPFX)fountain.o $(TARGETPFX)getpos.o $(TARGETPFX)glyphs.o \ + $(TARGETPFX)hack.o $(TARGETPFX)iactions.o $(TARGETPFX)insight.o \ + $(TARGETPFX)invent.o $(TARGETPFX)isaac64.o $(TARGETPFX)light.o \ + $(TARGETPFX)lock.o $(TARGETPFX)mail.o $(TARGETPFX)makemon.o \ + $(TARGETPFX)mcastu.o $(TARGETPFX)mdlib.o $(TARGETPFX)mhitm.o \ + $(TARGETPFX)mhitu.o $(TARGETPFX)minion.o $(TARGETPFX)mklev.o \ + $(TARGETPFX)mkmap.o $(TARGETPFX)mkmaze.o $(TARGETPFX)mkobj.o \ + $(TARGETPFX)mkroom.o $(TARGETPFX)mon.o $(TARGETPFX)mondata.o \ + $(TARGETPFX)monmove.o $(TARGETPFX)monst.o $(TARGETPFX)mplayer.o \ + $(TARGETPFX)mthrowu.o $(TARGETPFX)muse.o $(TARGETPFX)music.o \ + $(TARGETPFX)nhlua.o $(TARGETPFX)nhlsel.o $(TARGETPFX)nhlobj.o \ + $(TARGETPFX)nhmd4.o $(TARGETPFX)objects.o $(TARGETPFX)o_init.o \ $(TARGETPFX)objnam.o $(TARGETPFX)options.o $(TARGETPFX)pager.o \ $(TARGETPFX)pickup.o $(TARGETPFX)pline.o $(TARGETPFX)polyself.o \ $(TARGETPFX)potion.o $(TARGETPFX)pray.o $(TARGETPFX)priest.o \ @@ -629,10 +628,10 @@ HOBJ = $(TARGETPFX)allmain.o $(TARGETPFX)alloc.o \ $(TARGETPFX)teleport.o $(TARGETPFX)timeout.o $(TARGETPFX)topten.o \ $(TARGETPFX)track.o $(TARGETPFX)trap.o $(TARGETPFX)u_init.o \ $(TARGETPFX)uhitm.o $(TARGETPFX)utf8map.o $(TARGETPFX)vault.o \ - $(TARGETPFX)vision.o $(TARGETPFX)weapon.o \ - $(TARGETPFX)were.o $(TARGETPFX)wield.o $(TARGETPFX)windows.o \ - $(TARGETPFX)wizard.o $(TARGETPFX)wizcmds.o $(TARGETPFX)worm.o \ - $(TARGETPFX)worn.o $(TARGETPFX)write.o $(TARGETPFX)zap.o \ + $(TARGETPFX)vision.o $(TARGETPFX)weapon.o $(TARGETPFX)were.o \ + $(TARGETPFX)wield.o $(TARGETPFX)windows.o $(TARGETPFX)wizard.o \ + $(TARGETPFX)wizcmds.o $(TARGETPFX)worm.o $(TARGETPFX)worn.o \ + $(TARGETPFX)write.o $(TARGETPFX)zap.o \ $(REGEXOBJ) $(RANDOBJ) $(SYSOBJ) $(WINOBJ) $(HINTOBJ) $(SNDLIBOBJ) \ $(TARGETPFX)version.o @@ -1166,6 +1165,7 @@ $(TARGETPFX)drawing.o: drawing.c $(CONFIG_H) ../include/defsym.h \ ../include/sym.h ../include/wintype.h $(TARGETPFX)dungeon.o: dungeon.c $(HACK_H) ../include/dgn_file.h \ ../include/dlb.h +$(TARGETPFX)earlyarg.o: earlyarg.c $(HACK_H) $(TARGETPFX)eat.o: eat.c $(HACK_H) $(TARGETPFX)end.o: end.c $(HACK_H) ../include/dlb.h $(TARGETPFX)engrave.o: engrave.c $(HACK_H) diff --git a/sys/vms/Makefile.src b/sys/vms/Makefile.src index 9cf3f465f..20ee1643f 100644 --- a/sys/vms/Makefile.src +++ b/sys/vms/Makefile.src @@ -149,7 +149,7 @@ HACK_H = $(SRC)hack.h-t HACKCSRC = allmain.c alloc.c apply.c artifact.c attrib.c ball.c bones.c \ botl.c calendar.c cfgfiles.c cmd.c coloratt.c dbridge.c decl.c detect.c \ dig.c display.c dlb.c do.c do_name.c do_wear.c dog.c dogmove.c dokick.c \ - dothrow.c drawing.c dungeon.c eat.c end.c engrave.c exper.c \ + dothrow.c drawing.c dungeon.c earlyarg.c eat.c end.c engrave.c exper.c \ explode.c extralev.c files.c fountain.c getpos.c glyphs.c hack.c \ hacklib.c iactions.c insight.c invent.c light.c lock.c \ mail.c makemon.c mcastu.c mhitm.c mhitu.c minion.c \ @@ -198,7 +198,7 @@ HOBJ1 = allmain.obj,alloc.obj,apply.obj,artifact.obj,attrib.obj, \ coloratt.obj,dbridge.obj,decl.obj,detect.obj,dig.obj,display.obj, \ dlb.obj,do.obj,do_name.obj,do_wear.obj HOBJ2 = dog.obj,dogmove.obj,dokick.obj,dothrow.obj,drawing.obj, \ - dungeon.obj,eat.obj,end.obj,engrave.obj,exper.obj,explode.obj, \ + dungeon.obj,earlyarg.obj,eat.obj,end.obj,engrave.obj,exper.obj,explode.obj, \ extralev.obj,files.obj,fountain.obj,getpos.obj,glyphs.obj,hack.obj, \ hacklib.obj,iactions.obj,insight.obj,invent.obj HOBJ3 = light.obj,lock.obj,mail.obj,makemon.obj,mcastu.obj, \ @@ -519,6 +519,7 @@ dokick.obj : dokick.c $(HACK_H) dothrow.obj : dothrow.c $(HACK_H) drawing.obj : drawing.c $(HACK_H) $(INC)tcap.h dungeon.obj : dungeon.c $(HACK_H) $(INC)dgn_file.h $(INC)dlb.h +earlyarg.obj : earlyarg.c $(HACK_H) eat.obj : eat.c $(HACK_H) end.obj : end.c $(HACK_H) $(INC)dlb.h engrave.obj : engrave.c $(HACK_H) diff --git a/sys/vms/Makefile_src.vms b/sys/vms/Makefile_src.vms index 473116e81..74f22ea55 100644 --- a/sys/vms/Makefile_src.vms +++ b/sys/vms/Makefile_src.vms @@ -117,10 +117,10 @@ HACK_H = $(addsuffix .h, $(addprefix $(INCL), $(CONFIGBASEH) $(HACKBASEH))) HACKFILES := allmain alloc apply artifact attrib ball bones botl \ calendar cfgfiles cmd coloratt dbridge decl detect \ dig display dlb do do_name do_wear dog dogmove dokick \ - dothrow drawing dungeon eat end engrave exper explode extralev \ - files fountain getpos glyphs hack hacklib insight invent isaac64 \ - light lock mail makemon mcastu mdlib mhitm mhitu minion mklev \ - mkmap mkmaze mkobj mkroom mon \ + dothrow drawing dungeon earlyarg eat end engrave exper explode \ + extralev files fountain getpos glyphs hack hacklib iactions \ + insight invent isaac64 light lock mail makemon mcastu mdlib mhitm \ + mhitu minion mklev mkmap mkmaze mkobj mkroom mon \ mondata monmove monst mplayer mthrowu muse music \ nhlua nhlsel nhlobj objnam o_init objects \ options pager pickup pline polyself potion pray \ @@ -130,7 +130,7 @@ HACKFILES := allmain alloc apply artifact attrib ball bones botl \ sp_lev spell stairs steal steed strutil symbols sys teleport \ timeout topten track trap u_init utf8map \ uhitm vault version vision weapon were wield \ - windows wizard wizcmds worm worn write zap iactions + windows wizard wizcmds worm worn write zap # the date file DATEFILES = date diff --git a/sys/windows/GNUmakefile b/sys/windows/GNUmakefile index d4cbafead..3ea5c777b 100644 --- a/sys/windows/GNUmakefile +++ b/sys/windows/GNUmakefile @@ -1232,7 +1232,7 @@ COREOBJS = $(addsuffix .o, allmain alloc apply artifact attrib ball bones botl \ calendar cfgfiles cmd coloratt cppregex \ dbridge decl detect dig display dlb do do_name do_wear \ dog dogmove dokick dothrow drawing dungeon \ - eat end engrave exper explode extralev files fountain \ + earlyarg eat end engrave exper explode extralev files fountain \ getpos glyphs hack iactions insight invent isaac64 light lock \ mail makemon mcastu mdlib mhitm mhitu minion mklev mkmap mkmaze mkobj mkroom \ mon mondata monmove monst mplayer mthrowu muse music \ diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index dacee7775..9fd673698 100644 --- a/sys/windows/Makefile.nmake +++ b/sys/windows/Makefile.nmake @@ -650,66 +650,64 @@ PDCINCLCON= # windowing-system specific HACKCSRC = \ - $(SRC)allmain.c $(SRC)alloc.c $(SRC)apply.c $(SRC)artifact.c \ - $(SRC)attrib.c $(SRC)ball.c $(SRC)bones.c $(SRC)botl.c \ - $(SRC)calendar.c $(SRC)cmd.c $(SRC)coloratt.c $(SRC)dbridge.c \ - $(SRC)decl.c $(SRC)detect.c $(SRC)dig.c $(SRC)display.c \ - $(SRC)dlb.c $(SRC)do.c $(SRC)do_name.c $(SRC)do_wear.c \ - $(SRC)dog.c $(SRC)dogmove.c $(SRC)dokick.c $(SRC)dothrow.c \ - $(SRC)drawing.c $(SRC)dungeon.c $(SRC)eat.c $(SRC)end.c \ - $(SRC)engrave.c $(SRC)exper.c $(SRC)explode.c $(SRC)files.c \ - $(SRC)fountain.c $(SRC)getpos.c $(SRC)glyphs.c $(SRC)hack.c \ - $(SRC)iactions.c \ - $(SRC)insight.c $(SRC)invent.c $(SRC)isaac64.c $(SRC)light.c \ - $(SRC)lock.c $(SRC)mail.c $(SRC)makemon.c $(SRC)mcastu.c \ - $(SRC)mdlib.c $(SRC)mhitm.c $(SRC)mhitu.c $(SRC)minion.c \ - $(SRC)mklev.c $(SRC)mkmap.c $(SRC)mkmaze.c $(SRC)mkobj.c \ - $(SRC)mkroom.c $(SRC)mon.c $(SRC)mondata.c $(SRC)monmove.c \ - $(SRC)monst.c $(SRC)mplayer.c $(SRC)mthrowu.c $(SRC)muse.c \ - $(SRC)music.c $(SRC)nhlua.c $(SRC)nhlsel.c $(SRC)nhlobj.c \ - $(SRC)o_init.c $(SRC)objects.c $(SRC)objnam.c $(SRC)options.c \ - $(SRC)pager.c $(SRC)pickup.c $(SRC)pline.c $(SRC)polyself.c \ - $(SRC)potion.c $(SRC)pray.c $(SRC)priest.c $(SRC)quest.c \ - $(SRC)questpgr.c $(SRC)read.c $(SRC)rect.c $(SRC)region.c \ - $(SRC)restore.c $(SRC)rip.c $(SRC)rnd.c $(SRC)role.c \ - $(SRC)rumors.c $(SRC)save.c $(SRC)selvar.c\ - $(SRC)sfbase.c $(SRC)sfstruct.c \ - $(SRC)shk.c $(SRC)shknam.c $(SRC)sit.c $(SRC)sounds.c \ - $(SRC)sp_lev.c $(SRC)spell.c $(SRC)stairs.c $(SRC)steal.c \ - $(SRC)steed.c $(SRC)symbols.c $(SRC)sys.c $(SRC)teleport.c \ - $(SRC)timeout.c $(SRC)topten.c $(SRC)track.c $(SRC)trap.c \ - $(SRC)u_init.c $(SRC)uhitm.c $(SRC)utf8map.c $(SRC)vault.c \ - $(SRC)version.c $(SRC)vision.c $(SRC)weapon.c $(SRC)were.c \ - $(SRC)wield.c $(SRC)windows.c $(SRC)wizard.c $(SRC)worm.c \ - $(SRC)worn.c $(SRC)write.c $(SRC)zap.c + $(SRC)allmain.c $(SRC)alloc.c $(SRC)apply.c $(SRC)artifact.c \ + $(SRC)attrib.c $(SRC)ball.c $(SRC)bones.c $(SRC)botl.c \ + $(SRC)calendar.c $(SRC)cmd.c $(SRC)coloratt.c $(SRC)dbridge.c \ + $(SRC)decl.c $(SRC)detect.c $(SRC)dig.c $(SRC)display.c \ + $(SRC)dlb.c $(SRC)do.c $(SRC)do_name.c $(SRC)do_wear.c \ + $(SRC)dog.c $(SRC)dogmove.c $(SRC)dokick.c $(SRC)dothrow.c \ + $(SRC)drawing.c $(SRC)dungeon.c $(SRC)earlyarg.c $(SRC)eat.c \ + $(SRC)end.c $(SRC)engrave.c $(SRC)exper.c $(SRC)explode.c \ + $(SRC)files.c $(SRC)fountain.c $(SRC)getpos.c $(SRC)glyphs.c \ + $(SRC)hack.c $(SRC)iactions.c $(SRC)insight.c $(SRC)invent.c \ + $(SRC)isaac64.c $(SRC)light.c $(SRC)lock.c $(SRC)mail.c \ + $(SRC)makemon.c $(SRC)mcastu.c $(SRC)mdlib.c $(SRC)mhitm.c \ + $(SRC)mhitu.c $(SRC)minion.c $(SRC)mklev.c $(SRC)mkmap.c \ + $(SRC)mkmaze.c $(SRC)mkobj.c $(SRC)mkroom.c $(SRC)mon.c \ + $(SRC)mondata.c $(SRC)monmove.c $(SRC)monst.c $(SRC)mplayer.c \ + $(SRC)mthrowu.c $(SRC)muse.c $(SRC)music.c $(SRC)nhlua.c \ + $(SRC)nhlsel.c $(SRC)nhlobj.c $(SRC)o_init.c $(SRC)objects.c \ + $(SRC)objnam.c $(SRC)options.c $(SRC)pager.c $(SRC)pickup.c \ + $(SRC)pline.c $(SRC)polyself.c $(SRC)potion.c $(SRC)pray.c \ + $(SRC)priest.c $(SRC)quest.c $(SRC)questpgr.c $(SRC)read.c \ + $(SRC)rect.c $(SRC)region.c $(SRC)restore.c $(SRC)rip.c \ + $(SRC)rnd.c $(SRC)role.c $(SRC)rumors.c $(SRC)save.c \ + $(SRC)selvar.c $(SRC)sfbase.c $(SRC)sfstruct.c $(SRC)shk.c \ + $(SRC)shknam.c $(SRC)sit.c $(SRC)sounds.c $(SRC)sp_lev.c \ + $(SRC)spell.c $(SRC)stairs.c $(SRC)steal.c $(SRC)steed.c \ + $(SRC)symbols.c $(SRC)sys.c $(SRC)teleport.c $(SRC)timeout.c \ + $(SRC)topten.c $(SRC)track.c $(SRC)trap.c $(SRC)u_init.c \ + $(SRC)uhitm.c $(SRC)utf8map.c $(SRC)vault.c $(SRC)version.c \ + $(SRC)vision.c $(SRC)weapon.c $(SRC)were.c $(SRC)wield.c \ + $(SRC)windows.c $(SRC)wizard.c $(SRC)worm.c $(SRC)worn.c \ + $(SRC)write.c $(SRC)zap.c # all .h files except date.h HACKINCL = \ - $(INCL)align.h $(INCL)artifact.h $(INCL)artilist.h \ - $(INCL)attrib.h $(INCL)botl.h $(INCL)color.h \ - $(INCL)config.h $(INCL)config1.h $(INCL)context.h \ - $(INCL)coord.h $(INCL)cstd.h $(INCL)decl.h \ - $(INCL)defsym.h $(INCL)display.h $(INCL)dlb.h \ - $(INCL)dungeon.h $(INCL)engrave.h $(INCL)extern.h \ - $(INCL)flag.h $(INCL)fnamesiz.h $(INCL)func_tab.h \ - $(INCL)global.h $(INCL)warnings.h $(INCL)hack.h \ - $(INCL)lint.h $(INCL)mextra.h $(INCL)micro.h \ - $(INCL)mcastu.h \ - $(INCL)mfndpos.h $(INCL)mkroom.h $(INCL)monattk.h \ - $(INCL)mondata.h $(INCL)monflag.h $(INCL)monst.h \ - $(INCL)monsters.h $(INCL)nhmd4.h $(INCL)obj.h \ - $(INCL)objects.h $(INCL)objclass.h $(INCL)optlist.h \ - $(INCL)patchlevel.h $(INCL)pcconf.h $(INCL)permonst.h \ - $(INCL)prop.h $(INCL)rect.h $(INCL)region.h \ - $(INCL)savefile.h $(INCL)selvar.h $(INCL)sfprocs.h \ - $(INCL)sym.h $(INCL)rm.h $(INCL)sp_lev.h \ - $(INCL)spell.h $(INCL)sndprocs.h $(INCL)seffects.h \ - $(INCL)stairs.h $(INCL)sys.h $(INCL)tcap.h \ - $(INCL)timeout.h $(INCL)tradstdc.h $(INCL)trap.h \ - $(INCL)unixconf.h $(INCL)vision.h $(INCL)vmsconf.h \ - $(INCL)wintty.h $(INCL)wincurs.h $(INCL)winX.h \ - $(INCL)winprocs.h $(INCL)wintype.h $(INCL)you.h \ - $(INCL)youprop.h $(INCL)weight.h + $(INCL)align.h $(INCL)artifact.h $(INCL)artilist.h \ + $(INCL)attrib.h $(INCL)botl.h $(INCL)color.h \ + $(INCL)config.h $(INCL)config1.h $(INCL)context.h \ + $(INCL)coord.h $(INCL)cstd.h $(INCL)decl.h \ + $(INCL)defsym.h $(INCL)display.h $(INCL)dlb.h \ + $(INCL)dungeon.h $(INCL)engrave.h $(INCL)extern.h \ + $(INCL)flag.h $(INCL)fnamesiz.h $(INCL)func_tab.h \ + $(INCL)global.h $(INCL)warnings.h $(INCL)hack.h \ + $(INCL)lint.h $(INCL)mextra.h $(INCL)micro.h \ + $(INCL)mcastu.h $(INCL)mfndpos.h $(INCL)mkroom.h \ + $(INCL)monattk.h $(INCL)mondata.h $(INCL)monflag.h \ + $(INCL)monst.h $(INCL)monsters.h $(INCL)nhmd4.h \ + $(INCL)obj.h $(INCL)objects.h $(INCL)objclass.h \ + $(INCL)optlist.h $(INCL)patchlevel.h $(INCL)pcconf.h \ + $(INCL)permonst.h $(INCL)prop.h $(INCL)rect.h \ + $(INCL)region.h $(INCL)savefile.h $(INCL)selvar.h \ + $(INCL)sfprocs.h $(INCL)sym.h $(INCL)rm.h \ + $(INCL)sp_lev.h $(INCL)spell.h $(INCL)sndprocs.h \ + $(INCL)seffects.h $(INCL)stairs.h $(INCL)sys.h \ + $(INCL)tcap.h $(INCL)timeout.h $(INCL)tradstdc.h \ + $(INCL)trap.h $(INCL)unixconf.h $(INCL)vision.h \ + $(INCL)vmsconf.h $(INCL)wintty.h $(INCL)wincurs.h \ + $(INCL)winX.h $(INCL)winprocs.h $(INCL)wintype.h \ + $(INCL)you.h $(INCL)youprop.h $(INCL)weight.h LUA_FILES = $(DAT)asmodeus.lua $(DAT)baalz.lua $(DAT)bigrm-1.lua \ $(DAT)bigrm-10.lua $(DAT)bigrm-11.lua $(DAT)bigrm-12.lua \ @@ -861,31 +859,31 @@ COREOBJTTY = \ $(OTTY)dbridge.o $(OTTY)decl.o $(OTTY)detect.o $(OTTY)dig.o \ $(OTTY)display.o $(OTTY)do.o $(OTTY)do_name.o $(OTTY)do_wear.o \ $(OTTY)dog.o $(OTTY)dogmove.o $(OTTY)dokick.o $(OTTY)dothrow.o \ - $(OTTY)drawing.o $(OTTY)dungeon.o $(OTTY)eat.o $(OTTY)end.o \ - $(OTTY)engrave.o $(OTTY)exper.o $(OTTY)explode.o $(OTTY)extralev.o \ - $(OTTY)files.o $(OTTY)fountain.o $(OTTY)getpos.o $(OTTY)glyphs.o \ - $(OTTY)iactions.o \ - $(OTTY)hack.o $(OTTY)insight.o $(OTTY)invent.o $(OTTY)isaac64.o \ - $(OTTY)light.o $(OTTY)lock.o $(OTTY)mail.o $(OTTY)makemon.o \ - $(OTTY)mcastu.o $(OTTY)mhitm.o $(OTTY)mhitu.o $(OTTY)minion.o \ - $(OTTY)mklev.o $(OTTY)mkmap.o $(OTTY)mkmaze.o $(OTTY)mkobj.o \ - $(OTTY)mkroom.o $(OTTY)mon.o $(OTTY)mondata.o $(OTTY)monmove.o \ - $(OTTY)monst.o $(OTTY)mplayer.o $(OTTY)mthrowu.o $(OTTY)muse.o \ - $(OTTY)music.o $(OTTY)o_init.o $(OTTY)objects.o $(OTTY)objnam.o \ - $(OTTY)options.o $(OTTY)pager.o $(OTTY)pickup.o $(OTTY)pline.o \ - $(OTTY)polyself.o $(OTTY)potion.o $(OTTY)pray.o $(OTTY)priest.o \ - $(OTTY)quest.o $(OTTY)questpgr.o $(OTTY)read.o $(OTTY)rect.o \ - $(OTTY)region.o $(OTTY)report.o $(OTTY)restore.o $(OTTY)rip.o \ - $(OTTY)rnd.o $(OTTY)role.o $(OTTY)rumors.o $(OTTY)save.o \ - $(OTTY)selvar.o $(OTTY)sfbase.o $(OTTY)sfstruct.o $(OTTY)shk.o \ - $(OTTY)shknam.o $(OTTY)sit.o $(OTTY)sounds.o $(OTTY)sp_lev.o \ - $(OTTY)spell.o $(OTTY)stairs.o $(OTTY)steal.o $(OTTY)steed.o \ - $(OTTY)strutil.o $(OTTY)symbols.o $(OTTY)sys.o $(OTTY)teleport.o \ - $(OTTY)timeout.o $(OTTY)topten.o $(OTTY)track.o $(OTTY)trap.o \ - $(OTTY)u_init.o $(OTTY)uhitm.o $(OTTY)utf8map.o $(OTTY)vault.o \ - $(OTTY)vision.o $(OTTY)weapon.o $(OTTY)were.o $(OTTY)wield.o \ - $(OTTY)windows.o $(OTTY)wizard.o $(OTTY)wizcmds.o $(OTTY)worm.o \ - $(OTTY)worn.o $(OTTY)write.o $(OTTY)zap.o + $(OTTY)drawing.o $(OTTY)dungeon.o $(OTTY)earlyarg.o $(OTTY)eat.o \ + $(OTTY)end.o $(OTTY)engrave.o $(OTTY)exper.o $(OTTY)explode.o \ + $(OTTY)extralev.o $(OTTY)files.o $(OTTY)fountain.o $(OTTY)getpos.o \ + $(OTTY)glyphs.o $(OTTY)iactions.o $(OTTY)hack.o $(OTTY)insight.o \ + $(OTTY)invent.o $(OTTY)isaac64.o $(OTTY)light.o $(OTTY)lock.o \ + $(OTTY)mail.o $(OTTY)makemon.o $(OTTY)mcastu.o $(OTTY)mhitm.o \ + $(OTTY)mhitu.o $(OTTY)minion.o $(OTTY)mklev.o $(OTTY)mkmap.o \ + $(OTTY)mkmaze.o $(OTTY)mkobj.o $(OTTY)mkroom.o $(OTTY)mon.o \ + $(OTTY)mondata.o $(OTTY)monmove.o $(OTTY)monst.o $(OTTY)mplayer.o \ + $(OTTY)mthrowu.o $(OTTY)muse.o $(OTTY)music.o $(OTTY)o_init.o \ + $(OTTY)objects.o $(OTTY)objnam.o $(OTTY)options.o $(OTTY)pager.o \ + $(OTTY)pickup.o $(OTTY)pline.o $(OTTY)polyself.o $(OTTY)potion.o \ + $(OTTY)pray.o $(OTTY)priest.o $(OTTY)quest.o $(OTTY)questpgr.o \ + $(OTTY)read.o $(OTTY)rect.o $(OTTY)region.o $(OTTY)report.o \ + $(OTTY)restore.o $(OTTY)rip.o $(OTTY)rnd.o $(OTTY)role.o \ + $(OTTY)rumors.o $(OTTY)save.o $(OTTY)selvar.o $(OTTY)sfbase.o \ + $(OTTY)sfstruct.o $(OTTY)shk.o $(OTTY)shknam.o $(OTTY)sit.o \ + $(OTTY)sounds.o $(OTTY)sp_lev.o $(OTTY)spell.o $(OTTY)stairs.o \ + $(OTTY)steal.o $(OTTY)steed.o $(OTTY)strutil.o $(OTTY)symbols.o \ + $(OTTY)sys.o $(OTTY)teleport.o $(OTTY)timeout.o $(OTTY)topten.o \ + $(OTTY)track.o $(OTTY)trap.o $(OTTY)u_init.o $(OTTY)uhitm.o \ + $(OTTY)utf8map.o $(OTTY)vault.o $(OTTY)vision.o $(OTTY)weapon.o \ + $(OTTY)were.o $(OTTY)wield.o $(OTTY)windows.o $(OTTY)wizard.o \ + $(OTTY)wizcmds.o $(OTTY)worm.o $(OTTY)worn.o $(OTTY)write.o \ + $(OTTY)zap.o OBJSTTY = $(MDLIBTTY) $(COREOBJTTY) $(REGEXTTY) $(RANDOMTTY) @@ -925,31 +923,31 @@ COREOBJGUI = \ $(OGUI)dbridge.o $(OGUI)decl.o $(OGUI)detect.o $(OGUI)dig.o \ $(OGUI)display.o $(OGUI)do.o $(OGUI)do_name.o $(OGUI)do_wear.o \ $(OGUI)dog.o $(OGUI)dogmove.o $(OGUI)dokick.o $(OGUI)dothrow.o \ - $(OGUI)drawing.o $(OGUI)dungeon.o $(OGUI)eat.o $(OGUI)end.o \ - $(OGUI)engrave.o $(OGUI)exper.o $(OGUI)explode.o $(OGUI)extralev.o \ - $(OGUI)files.o $(OGUI)fountain.o $(OGUI)getpos.o $(OGUI)glyphs.o \ - $(OGUI)iactions.o \ - $(OGUI)hack.o $(OGUI)insight.o $(OGUI)invent.o $(OGUI)isaac64.o \ - $(OGUI)light.o $(OGUI)lock.o $(OGUI)mail.o $(OGUI)makemon.o \ - $(OGUI)mcastu.o $(OGUI)mhitm.o $(OGUI)mhitu.o $(OGUI)minion.o \ - $(OGUI)mklev.o $(OGUI)mkmap.o $(OGUI)mkmaze.o $(OGUI)mkobj.o \ - $(OGUI)mkroom.o $(OGUI)mon.o $(OGUI)mondata.o $(OGUI)monmove.o \ - $(OGUI)monst.o $(OGUI)mplayer.o $(OGUI)mthrowu.o $(OGUI)muse.o \ - $(OGUI)music.o $(OGUI)o_init.o $(OGUI)objects.o $(OGUI)objnam.o \ - $(OGUI)options.o $(OGUI)pager.o $(OGUI)pickup.o $(OGUI)pline.o \ - $(OGUI)polyself.o $(OGUI)potion.o $(OGUI)pray.o $(OGUI)priest.o \ - $(OGUI)quest.o $(OGUI)questpgr.o $(OGUI)read.o $(OGUI)rect.o \ - $(OGUI)region.o $(OGUI)report.o $(OGUI)restore.o $(OGUI)rip.o \ - $(OGUI)rnd.o $(OGUI)role.o $(OGUI)rumors.o $(OGUI)save.o \ - $(OGUI)selvar.o $(OGUI)sfbase.o $(OGUI)sfstruct.o $(OGUI)shk.o \ - $(OGUI)shknam.o $(OGUI)sit.o $(OGUI)sounds.o $(OGUI)sp_lev.o \ - $(OGUI)spell.o $(OGUI)stairs.o $(OGUI)steal.o $(OGUI)steed.o \ - $(OGUI)strutil.o $(OGUI)symbols.o $(OGUI)sys.o $(OGUI)teleport.o \ - $(OGUI)timeout.o $(OGUI)topten.o $(OGUI)track.o $(OGUI)trap.o \ - $(OGUI)u_init.o $(OGUI)uhitm.o $(OGUI)utf8map.o $(OGUI)vault.o \ - $(OGUI)vision.o $(OGUI)weapon.o $(OGUI)were.o $(OGUI)wield.o \ - $(OGUI)windows.o $(OGUI)wizard.o $(OGUI)wizcmds.o $(OGUI)worm.o \ - $(OGUI)worn.o $(OGUI)write.o $(OGUI)zap.o + $(OGUI)drawing.o $(OGUI)dungeon.o $(OGUI)earlyarg.o $(OGUI)eat.o \ + $(OGUI)end.o $(OGUI)engrave.o $(OGUI)exper.o $(OGUI)explode.o \ + $(OGUI)extralev.o $(OGUI)files.o $(OGUI)fountain.o $(OGUI)getpos.o \ + $(OGUI)glyphs.o $(OGUI)iactions.o $(OGUI)hack.o $(OGUI)insight.o \ + $(OGUI)invent.o $(OGUI)isaac64.o $(OGUI)light.o $(OGUI)lock.o \ + $(OGUI)mail.o $(OGUI)makemon.o $(OGUI)mcastu.o $(OGUI)mhitm.o \ + $(OGUI)mhitu.o $(OGUI)minion.o $(OGUI)mklev.o $(OGUI)mkmap.o \ + $(OGUI)mkmaze.o $(OGUI)mkobj.o $(OGUI)mkroom.o $(OGUI)mon.o \ + $(OGUI)mondata.o $(OGUI)monmove.o $(OGUI)monst.o $(OGUI)mplayer.o \ + $(OGUI)mthrowu.o $(OGUI)muse.o $(OGUI)music.o $(OGUI)o_init.o \ + $(OGUI)objects.o $(OGUI)objnam.o $(OGUI)options.o $(OGUI)pager.o \ + $(OGUI)pickup.o $(OGUI)pline.o $(OGUI)polyself.o $(OGUI)potion.o \ + $(OGUI)pray.o $(OGUI)priest.o $(OGUI)quest.o $(OGUI)questpgr.o \ + $(OGUI)read.o $(OGUI)rect.o $(OGUI)region.o $(OGUI)report.o \ + $(OGUI)restore.o $(OGUI)rip.o $(OGUI)rnd.o $(OGUI)role.o \ + $(OGUI)rumors.o $(OGUI)save.o $(OGUI)selvar.o $(OGUI)sfbase.o \ + $(OGUI)sfstruct.o $(OGUI)shk.o $(OGUI)shknam.o $(OGUI)sit.o \ + $(OGUI)sounds.o $(OGUI)sp_lev.o $(OGUI)spell.o $(OGUI)stairs.o \ + $(OGUI)steal.o $(OGUI)steed.o $(OGUI)strutil.o $(OGUI)symbols.o \ + $(OGUI)sys.o $(OGUI)teleport.o $(OGUI)timeout.o $(OGUI)topten.o \ + $(OGUI)track.o $(OGUI)trap.o $(OGUI)u_init.o $(OGUI)uhitm.o \ + $(OGUI)utf8map.o $(OGUI)vault.o $(OGUI)vision.o $(OGUI)weapon.o \ + $(OGUI)were.o $(OGUI)wield.o $(OGUI)windows.o $(OGUI)wizard.o \ + $(OGUI)wizcmds.o $(OGUI)worm.o $(OGUI)worn.o $(OGUI)write.o \ + $(OGUI)zap.o OBJSGUI = $(MDLIBGUI) $(COREOBJGUI) $(REGEXGUI) $(RANDOMGUI) @@ -2029,7 +2027,7 @@ cpu.tag: ! IF "$(TARGET_CPU)"=="x64" @echo Windows x64 64-bit target build ! ELSE -! IF "$(TARGET_CPU)"=="arm64" +! IF "$(TARGET_CPU)"=="arm64" @echo Windows arm64 64-bit target build ! ELSE @echo Windows x86 32-bit target build @@ -3250,6 +3248,7 @@ $(OTTY)drawing.o: drawing.c $(CONFIG_H) $(INCL)defsym.h \ $(INCL)sym.h $(INCL)wintype.h $(OTTY)dungeon.o: dungeon.c $(HACK_H) $(INCL)dgn_file.h \ $(INCL)dlb.h +$(OTTY)earlyarg.o: earlyarg.c $(HACK_H) $(OTTY)eat.o: eat.c $(HACK_H) $(OTTY)end.o: end.c $(HACK_H) $(INCL)dlb.h $(OTTY)engrave.o: engrave.c $(HACK_H) @@ -3630,6 +3629,7 @@ $(OGUI)drawing.o: drawing.c $(CONFIG_H) $(INCL)defsym.h \ $(INCL)sym.h $(INCL)wintype.h $(OGUI)dungeon.o: dungeon.c $(HACK_H) $(INCL)dgn_file.h \ $(INCL)dlb.h +$(OGUI)earlyarg.o: earlyarg.c $(HACK_H) $(OGUI)eat.o: eat.c $(HACK_H) $(OGUI)end.o: end.c $(HACK_H) $(INCL)dlb.h $(OGUI)engrave.o: engrave.c $(HACK_H) diff --git a/sys/windows/vs/NetHack/NetHack.vcxproj b/sys/windows/vs/NetHack/NetHack.vcxproj index 72d8986b0..131a4cf05 100644 --- a/sys/windows/vs/NetHack/NetHack.vcxproj +++ b/sys/windows/vs/NetHack/NetHack.vcxproj @@ -113,6 +113,7 @@ + diff --git a/sys/windows/vs/NetHackW/NetHackW.vcxproj b/sys/windows/vs/NetHackW/NetHackW.vcxproj index ba3b21b1e..fce2d2a1d 100644 --- a/sys/windows/vs/NetHackW/NetHackW.vcxproj +++ b/sys/windows/vs/NetHackW/NetHackW.vcxproj @@ -135,6 +135,7 @@ + From 396a3735cab1545e45199654651145f1ca225367 Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 1 Apr 2026 08:48:05 -0400 Subject: [PATCH 378/442] follow-up for Xcode project --- sys/unix/NetHack.xcodeproj/project.pbxproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sys/unix/NetHack.xcodeproj/project.pbxproj b/sys/unix/NetHack.xcodeproj/project.pbxproj index 3c8b036a5..e379e51f8 100644 --- a/sys/unix/NetHack.xcodeproj/project.pbxproj +++ b/sys/unix/NetHack.xcodeproj/project.pbxproj @@ -209,6 +209,7 @@ F5857AA22DED026700A8CB4F /* version.c in Sources */ = {isa = PBXBuildFile; fileRef = 31B8A32721A238010055BD01 /* version.c */; }; F5857AA42DED032D00A8CB4F /* cfgfiles.c in Sources */ = {isa = PBXBuildFile; fileRef = F5857AA32DED032C00A8CB4F /* cfgfiles.c */; }; F5857AA62DED03BB00A8CB4F /* sfbase.c in Sources */ = {isa = PBXBuildFile; fileRef = F5857AA52DED03BB00A8CB4F /* sfbase.c */; }; + F5E66A222F7D4B30000E30B7 /* earlyarg.c in Sources */ = {isa = PBXBuildFile; fileRef = F5E66A212F7D4B30000E30B7 /* earlyarg.c */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -634,6 +635,7 @@ F54C1AE42E43D22A006CAA8E /* sfprocs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = sfprocs.h; path = ../../include/include/sfprocs.h; sourceTree = ""; }; F5857AA32DED032C00A8CB4F /* cfgfiles.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = cfgfiles.c; path = ../../src/cfgfiles.c; sourceTree = ""; }; F5857AA52DED03BB00A8CB4F /* sfbase.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = sfbase.c; path = ../../src/sfbase.c; sourceTree = ""; }; + F5E66A212F7D4B30000E30B7 /* earlyarg.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = earlyarg.c; path = ../../src/earlyarg.c; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ @@ -693,6 +695,7 @@ 3189576821A1FCC100FB2ABE = { isa = PBXGroup; children = ( + F5E66A212F7D4B30000E30B7 /* earlyarg.c */, F50818302F6F564300D2AAFA /* iactions.c */, F515A73B2DED0B47006E1F63 /* hacklib.c */, F5457B202DED146B00039D83 /* hacklib.c */, @@ -1876,6 +1879,7 @@ 31B8A41321A23F650055BD01 /* version.c in Sources */, 31B8A3BB21A238060055BD01 /* allmain.c in Sources */, 31B8A39521A238060055BD01 /* windows.c in Sources */, + F5E66A222F7D4B30000E30B7 /* earlyarg.c in Sources */, 31B8A38621A238060055BD01 /* mondata.c in Sources */, 31B8A41921A244940055BD01 /* objects.c in Sources */, 31B8A3AA21A238060055BD01 /* wizard.c in Sources */, From ab459265b01128f8bdec974f626c667fb7bad060 Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 1 Apr 2026 09:09:39 -0400 Subject: [PATCH 379/442] top line fixup --- src/earlyarg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/earlyarg.c b/src/earlyarg.c index e0fd2a56b..aee363bdc 100755 --- a/src/earlyarg.c +++ b/src/earlyarg.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 enumdmp.c $NHDT-Date: 1771213100 2026/02/15 19:38:20 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.286 $ */ +/* NetHack 3.7 earlyarg.c $NHDT-Date: 1771213100 2026/02/15 19:38:20 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.286 $ */ /* Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ From e377591240c61bd9ac12dbb98d8326043fecb217 Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 1 Apr 2026 10:59:22 -0400 Subject: [PATCH 380/442] follow-up: avoid issue with onefile concatenation --- include/mcastu.h | 6 ++++-- src/earlyarg.c | 10 +++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/include/mcastu.h b/include/mcastu.h index a4491bf75..53a37070e 100644 --- a/include/mcastu.h +++ b/include/mcastu.h @@ -9,8 +9,10 @@ #define MONSPELL(def, lvl, flags) MCAST_##def #elif defined(MCASTU_INIT) #define MONSPELL(def, lvl, flags) { lvl, flags } -#elif defined(DUMP_MCASTU_ENUM) -#define MONSPELL(def, lvl, flags) { MCAST_##def, #def } +#elif defined(DUMP_MCASTU_ENUM1) +#define MONSPELL(def, lvl, flags) MCAST_DUMPENUM_##def +#elif defined(DUMP_MCASTU_ENUM2) +#define MONSPELL(def, lvl, flags) { MCAST_DUMPENUM_##def, #def } #endif MONSPELL(PSI_BOLT, 0, MCF_HOSTILE|MCF_SIGHT), diff --git a/src/earlyarg.c b/src/earlyarg.c index aee363bdc..166510993 100755 --- a/src/earlyarg.c +++ b/src/earlyarg.c @@ -289,17 +289,17 @@ static struct enum_dump arti_enum_dump[] = { #undef DUMP_ARTI_ENUM /* the enums are not part of hack.h for this one */ -#define MCASTU_ENUM -enum mcast_spells { +#define DUMP_MCASTU_ENUM1 +enum mcast_dumpenum_spells { #include "mcastu.h" }; -#undef MCASTU_ENUM +#undef DUMP_MCASTU_ENUM1 -#define DUMP_MCASTU_ENUM +#define DUMP_MCASTU_ENUM2 static struct enum_dump mcastu_enum_dump[] = { #include "mcastu.h" }; -#undef DUMP_MCASTU_ENUM +#undef DUMP_MCASTU_ENUM2 #undef DUMP_ENUMS From d911e4c4d3fc9783627396dcc848e248dbb32d99 Mon Sep 17 00:00:00 2001 From: nhw_cron Date: Wed, 1 Apr 2026 09:16:07 -0400 Subject: [PATCH 381/442] This is cron-daily v1-Jan-12-2026. 000files updated: Files --- Files | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/Files b/Files index a02179034..abdb08205 100644 --- a/Files +++ b/Files @@ -291,24 +291,24 @@ allmain.c alloc.c apply.c artifact.c attrib.c ball.c bones.c botl.c calendar.c cfgfiles.c cmd.c coloratt.c date.c dbridge.c decl.c detect.c dig.c display.c dlb.c do.c do_name.c do_wear.c dog.c dogmove.c -dokick.c dothrow.c drawing.c dungeon.c eat.c end.c -engrave.c exper.c explode.c extralev.c files.c fountain.c -getpos.c glyphs.c hack.c hacklib.c iactions.c insight.c -invent.c isaac64.c light.c lock.c mail.c makemon.c -mcastu.c mdlib.c mhitm.c mhitu.c minion.c mklev.c -mkmap.c mkmaze.c mkobj.c mkroom.c mon.c mondata.c -monmove.c monst.c mplayer.c mthrowu.c muse.c music.c -nhlobj.c nhlsel.c nhlua.c nhmd4.c o_init.c objects.c -objnam.c options.c pager.c pickup.c pline.c polyself.c -potion.c pray.c priest.c quest.c questpgr.c read.c -rect.c region.c report.c restore.c rip.c rnd.c -role.c rumors.c save.c selvar.c sfbase.c sfstruct.c -shk.c shknam.c sit.c sounds.c sp_lev.c spell.c -stairs.c steal.c steed.c strutil.c symbols.c sys.c -teleport.c timeout.c topten.c track.c trap.c u_init.c -uhitm.c utf8map.c vault.c version.c vision.c weapon.c -were.c wield.c windows.c wizard.c wizcmds.c worm.c -worn.c write.c zap.c +dokick.c dothrow.c drawing.c dungeon.c earlyarg.c eat.c +end.c engrave.c exper.c explode.c extralev.c files.c +fountain.c getpos.c glyphs.c hack.c hacklib.c iactions.c +insight.c invent.c isaac64.c light.c lock.c mail.c +makemon.c mcastu.c mdlib.c mhitm.c mhitu.c minion.c +mklev.c mkmap.c mkmaze.c mkobj.c mkroom.c mon.c +mondata.c monmove.c monst.c mplayer.c mthrowu.c muse.c +music.c nhlobj.c nhlsel.c nhlua.c nhmd4.c o_init.c +objects.c objnam.c options.c pager.c pickup.c pline.c +polyself.c potion.c pray.c priest.c quest.c questpgr.c +read.c rect.c region.c report.c restore.c rip.c +rnd.c role.c rumors.c save.c selvar.c sfbase.c +sfstruct.c shk.c shknam.c sit.c sounds.c sp_lev.c +spell.c stairs.c steal.c steed.c strutil.c symbols.c +sys.c teleport.c timeout.c topten.c track.c trap.c +u_init.c uhitm.c utf8map.c vault.c version.c vision.c +weapon.c were.c wield.c windows.c wizard.c wizcmds.c +worm.c worn.c write.c zap.c submodules: (files in top directory) From 542d1db5f4524e4043887297da34c8faae4e6717 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Wed, 1 Apr 2026 19:24:40 +0300 Subject: [PATCH 382/442] Another fix for #K4317 - monster grudge Undead monsters created by the level creation routine do not grudge other (zombifiable) monsters created during the level creation. This of course doesn't prevent the grudge happening with monsters created during gameplay. Invalidates saves and bones. --- include/monst.h | 3 ++- include/patchlevel.h | 2 +- src/makemon.c | 1 + src/mon.c | 2 ++ 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/monst.h b/include/monst.h index f1fb3b9c2..8f189b4b7 100644 --- a/include/monst.h +++ b/include/monst.h @@ -163,7 +163,8 @@ struct monst { Bitfield(mspotted, 1); /* mon is currently seen by hero */ Bitfield(mwandexp, 1); /* mon has experience with wands */ - /* 6 spare bits */ + Bitfield(mgenmklev, 1); /* made by the level generation */ + /* 5 spare bits */ unsigned long mstrategy; /* for monsters with mflag3: current strategy */ #ifdef NHSTDC diff --git a/include/patchlevel.h b/include/patchlevel.h index 0d814189e..d05c2226d 100644 --- a/include/patchlevel.h +++ b/include/patchlevel.h @@ -17,7 +17,7 @@ * Incrementing EDITLEVEL can be used to force invalidation of old bones * and save files. */ -#define EDITLEVEL 134 +#define EDITLEVEL 135 /* * Development status possibilities. diff --git a/src/makemon.c b/src/makemon.c index 4f12e12cb..6f158ee01 100644 --- a/src/makemon.c +++ b/src/makemon.c @@ -1295,6 +1295,7 @@ makemon( place_monster(mtmp, x, y); mtmp->mcansee = mtmp->mcanmove = TRUE; + mtmp->mgenmklev = gi.in_mklev; mtmp->seen_resistance = M_SEEN_NOTHING; mtmp->mpeaceful = (mmflags & MM_ANGRY) ? FALSE : peace_minded(ptr); if ((mmflags & MM_MINVIS) != 0) /* for ^G */ diff --git a/src/mon.c b/src/mon.c index 627e673bd..c0d92e6c3 100644 --- a/src/mon.c +++ b/src/mon.c @@ -2391,6 +2391,8 @@ mm_2way_aggression(struct monst *magr, struct monst *mdef) them waking up early (e.g. because a zombie decided to attack the Wizard of Yendor). */ if (zombie_maker(magr) && zombie_form(mdef->data) != NON_PM) { + if (magr->mgenmklev && mdef->mgenmklev) + return 0L; if (!Is_stronghold(&u.uz) && !unique_corpstat(magr->data) && !unique_corpstat(mdef->data)) return (ALLOW_M | ALLOW_TM); From 3b4d4cd261a2b82eb972f06c06b04f1717b34007 Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 1 Apr 2026 12:56:37 -0400 Subject: [PATCH 383/442] expand range of oc_weight --- include/objclass.h | 2 +- include/patchlevel.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/objclass.h b/include/objclass.h index f7896487e..fa210f519 100644 --- a/include/objclass.h +++ b/include/objclass.h @@ -91,7 +91,7 @@ struct objclass { uchar oc_color; /* color of the object */ short oc_prob; /* probability, used in mkobj() */ - unsigned short oc_weight; /* encumbrance (1 cn = 0.1 lb.) */ + unsigned oc_weight; /* encumbrance (1 cn = 0.1 lb.) */ short oc_cost; /* base cost in shops */ /* Check the AD&D rules! The FIRST is small monster damage. */ /* for weapons, and tools, rocks, and gems useful as weapons */ diff --git a/include/patchlevel.h b/include/patchlevel.h index d05c2226d..ee8e27342 100644 --- a/include/patchlevel.h +++ b/include/patchlevel.h @@ -17,7 +17,7 @@ * Incrementing EDITLEVEL can be used to force invalidation of old bones * and save files. */ -#define EDITLEVEL 135 +#define EDITLEVEL 136 /* * Development status possibilities. From fc6c7d70be970557ef1e0ac0c39a85c2bde1360d Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Thu, 2 Apr 2026 12:23:42 +0300 Subject: [PATCH 384/442] Give spellcast attack to monster priest and wizard Acolytes grow up to priests, apprentices to wizards, but they did not cast spells after they grew up. Give the monster priests and wizards the same spellcasting attack as all the other priest and wizard -type monsters. This lack of magical ability goes back at least to 3.3.1; I didn't bother checking back further. --- doc/fixes3-7-0.txt | 1 + include/monsters.h | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 506dcdd5a..1d40de239 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1591,6 +1591,7 @@ change some command keys, 'v' is now chronicle, 'V' is versionshort, one orc-town shaman has a higher level, affecting spellcasting hero has a small chance of catching items thrown at them wizard mode: history menu for #wizwish and WIZKIT +monster priests and wizards did not cast spells Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/include/monsters.h b/include/monsters.h index bf4d30c3d..c7605d3b6 100644 --- a/include/monsters.h +++ b/include/monsters.h @@ -3395,8 +3395,8 @@ /* monster priests are separate monsters (above; "aligned cleric") */ MON(NAMS("priest", "priestess", "cleric"), S_HUMAN, LVL(10, 12, 10, 2, 0), G_NOGEN, - A(ATTK(AT_WEAP, AD_PHYS, 1, 6), - NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_MAGC, AD_CLRC, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_HUMANOID, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_STRONG | M2_COLLECT, @@ -3451,8 +3451,8 @@ 12, HI_DOMESTIC, VALKYRIE), MON(NAM("wizard"), S_HUMAN, LVL(10, 12, 10, 3, 0), G_NOGEN, - A(ATTK(AT_WEAP, AD_PHYS, 1, 6), - NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_MAGC, AD_SPEL, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_HUMANOID, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_STRONG | M2_COLLECT | M2_MAGIC, From b2a80ab87ae55841eda029722fa0c1acc0f942f6 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Thu, 2 Apr 2026 15:40:03 +0300 Subject: [PATCH 385/442] Prevent phaseable monster hiding deep in undiggable walls The outer edge of a random dungeon level can have undiggable walls. Phaseable monsters, such as earth elementals, could hide deep inside that boundary. Turn the walls beyond the first layer of non-diggable walls also non-phaseable. --- doc/fixes3-7-0.txt | 1 + src/mkmaze.c | 16 +++++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 1d40de239..47d4f53f7 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1592,6 +1592,7 @@ one orc-town shaman has a higher level, affecting spellcasting hero has a small chance of catching items thrown at them wizard mode: history menu for #wizwish and WIZKIT monster priests and wizards did not cast spells +prevent phaseable monsters hiding deep inside nondiggable walls Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/mkmaze.c b/src/mkmaze.c index 9c665a37e..573eada04 100644 --- a/src/mkmaze.c +++ b/src/mkmaze.c @@ -1426,8 +1426,9 @@ get_level_extends( *bottom = ymax; } -/* put a non-diggable boundary around the initial portion of a level map. - * assumes that no level will initially put things beyond the isok() range. +/* put a non-diggable/non-phaseable boundary around the initial portion + * of a level map. assumes that no level will initially put things + * beyond the isok() range. * * we can't bound unconditionally on the last line with something in it, * because that something might be a niche which was already reachable, @@ -1449,9 +1450,14 @@ bound_digging(void) for (x = 0; x < COLNO; x++) for (y = 0; y < ROWNO; y++) - if (IS_STWALL(levl[x][y].typ) - && (y <= ymin || y >= ymax || x <= xmin || x >= xmax)) - levl[x][y].wall_info |= W_NONDIGGABLE; + if (IS_STWALL(levl[x][y].typ)) { + /* undiggable walls at edges, ... */ + if (y <= ymin || y >= ymax || x <= xmin || x >= xmax) + levl[x][y].wall_info |= W_NONDIGGABLE; + /* one tile past that, everything is also unphaseable */ + if (y < ymin || y > ymax || x < xmin || x > xmax) + levl[x][y].wall_info |= W_NONPASSWALL; + } } void From 8c55e7df1c5dbe2c70548ae91f699fdb3d0e983e Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Fri, 3 Apr 2026 12:59:09 +0300 Subject: [PATCH 386/442] Code style nit --- src/mhitu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mhitu.c b/src/mhitu.c index 129637152..6d78ed5dc 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -2363,7 +2363,8 @@ assess_dmg(struct monst *mtmp, int tmp) callback (optional). Callback returns 0 if the attack is active */ -boolean ranged_attk_assessed( +boolean +ranged_attk_assessed( struct monst *mtmp, boolean (*assessfunc)(struct monst *, int)) { From 9bdc41b2634fd56b279fb92eac76b1c02cdcf6e8 Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 3 Apr 2026 08:37:14 -0700 Subject: [PATCH 387/442] more mhitu.c style/formatting --- src/mhitu.c | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/src/mhitu.c b/src/mhitu.c index 6d78ed5dc..71d90a8ec 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mhitu.c $NHDT-Date: 1762750699 2025/11/09 20:58:19 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.334 $ */ +/* NetHack 3.7 mhitu.c $NHDT-Date: 1775259433 2026/04/03 15:37:13 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.341 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -12,13 +12,13 @@ staticfn void missmu(struct monst *, boolean, struct attack *); staticfn void mswings(struct monst *, struct obj *, boolean); staticfn void wildmiss(struct monst *, struct attack *); staticfn void calc_mattacku_vars(struct monst *, boolean *, boolean *, - boolean *, boolean *); + boolean *, boolean *); staticfn void summonmu(struct monst *, boolean); staticfn int hitmu(struct monst *, struct attack *); staticfn int gulpmu(struct monst *, struct attack *); staticfn int explmu(struct monst *, struct attack *, boolean); staticfn void mayberem(struct monst *, const char *, struct obj *, - const char *); + const char *); staticfn int assess_dmg(struct monst *, int); staticfn int passiveum(struct permonst *, struct monst *, struct attack *); @@ -94,7 +94,7 @@ missmu(struct monst *mtmp, boolean nearmiss, struct attack *mattk) pline_mon(mtmp, "%s pretends to be friendly.", Monnam(mtmp)); else pline_mon(mtmp, "%s %smisses!", Monnam(mtmp), - (nearmiss && flags.verbose) ? "just " : ""); + (nearmiss && flags.verbose) ? "just " : ""); stop_occupation(); } @@ -142,7 +142,9 @@ mswings( /* return how a poison attack was delivered */ const char * -mpoisons_subj(struct monst *mtmp, struct attack *mattk) +mpoisons_subj( + struct monst *mtmp, + struct attack *mattk) { if (mattk->aatyp == AT_WEAP) { struct obj *mwep = (mtmp == &gy.youmonst) ? uwep : MON_WEP(mtmp); @@ -210,9 +212,9 @@ wildmiss(struct monst *mtmp, struct attack *mattk) || nolimbs(mtmp->data)) ? "lunges" : "swings"; - if (compat) + if (compat) { pline("%s tries to touch you and misses!", Monst_name); - else + } else { switch (rn2(3)) { case 0: pline("%s %s wildly and misses!", Monst_name, swings); @@ -230,7 +232,7 @@ wildmiss(struct monst *mtmp, struct attack *mattk) pline("%s %s wildly!", Monst_name, swings); break; } - + } } else if (unotthere) { /* Displaced */ /* give 'displaced' message even if hero is Blind */ if (compat) @@ -899,7 +901,8 @@ mattacku(struct monst *mtmp) mon_currwep = MON_WEP(mtmp); if (mon_currwep) { boolean bash = (is_pole(mon_currwep) - && !is_art(mon_currwep, ART_SNICKERSNEE) + && !is_art(mon_currwep, + ART_SNICKERSNEE) && m_next2u(mtmp)); hittmp = hitval(mon_currwep, &gy.youmonst); @@ -1041,7 +1044,9 @@ diseasemu(struct permonst *mdat) /* check whether slippery clothing protects from hug or wrap attack */ boolean -u_slip_free(struct monst *mtmp, struct attack *mattk) +u_slip_free( + struct monst *mtmp, + struct attack *mattk) { struct obj *obj; @@ -1583,7 +1588,10 @@ gulpmu(struct monst *mtmp, struct attack *mattk) /* monster explodes in your face */ staticfn int -explmu(struct monst *mtmp, struct attack *mattk, boolean ufound) +explmu( + struct monst *mtmp, + struct attack *mattk, + boolean ufound) { boolean kill_agr = TRUE; boolean not_affected; @@ -2365,8 +2373,8 @@ assess_dmg(struct monst *mtmp, int tmp) boolean ranged_attk_assessed( -struct monst *mtmp, -boolean (*assessfunc)(struct monst *, int)) + struct monst *mtmp, + boolean (*assessfunc)(struct monst *, int)) { int i; struct permonst *ptr = mtmp->data; @@ -2383,7 +2391,9 @@ boolean (*assessfunc)(struct monst *, int)) /* can be used as ranged_attk_assessed() callback. Returns TRUE if monster is avoiding use of this attack */ boolean -mon_avoiding_this_attack(struct monst *mtmp, int attkidx) +mon_avoiding_this_attack( + struct monst *mtmp, + int attkidx) { struct permonst *ptr = mtmp->data; int typ = -1; @@ -2628,4 +2638,6 @@ cloneu(void) return mon; } +#undef ld + /*mhitu.c*/ From 7120a7190e8dc859e7257625a8c8d8420951b3b7 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 4 Apr 2026 07:52:06 -0400 Subject: [PATCH 388/442] some more discarded-qualifiers fixes --- sys/unix/Makefile.utl | 6 ++++-- sys/windows/Makefile.nmake | 6 ++++-- util/sfctool.c | 8 ++++---- util/sftags.c | 18 ++++++++++-------- 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/sys/unix/Makefile.utl b/sys/unix/Makefile.utl index 84f73b4f6..862a319da 100644 --- a/sys/unix/Makefile.utl +++ b/sys/unix/Makefile.utl @@ -394,8 +394,10 @@ $(TARGETPFX)sf-version.o: ../src/version.c $(HACK_H) $(TARGETPFX)sf-worm.o: ../src/worm.c $(HACK_H) $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/worm.c -sftags: sftags.o $(HACKLIB) - $(CLINK) $(LFLAGS) -o $@ sftags.o $(HACKLIB) $(LIBS) +sftags: sftags.o $(HACKLIB) $(TARGETPFX)sf-alloc.o $(TARGETPFX)panic.o + $(CLINK) $(LFLAGS) -o $@ sftags.o \ + $(TARGETPFX)sf-alloc.o $(TARGETPFX)panic.o \ + $(HACKLIB) $(LIBS) sftags.o: sftags.c $(HACK_H) $(CC) $(CFLAGS) -c sftags.c ../include/sfproto.h: sf.tags sftags diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index 9fd673698..2097d7183 100644 --- a/sys/windows/Makefile.nmake +++ b/sys/windows/Makefile.nmake @@ -2649,8 +2649,10 @@ $(OUTL)sf-sys.o: $(SRC)sys.c $(HACK_H) $(OUTL)sf-windsys.o: $(MSWSYS)windsys.c $(HACK_H) $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(MSWSYS)windsys.c -$(UTIL)sftags.exe: $(OUTL)sftags.o $(OUTLHACKLIB) - $(link) $(LFLAGS) /OUT:$@ $(OUTL)sftags.o $(OUTLHACKLIB) +$(UTIL)sftags.exe: $(OUTL)sftags.o $(OUTL)sf-alloc.o $(OUTL)panic.o $(OUTLHACKLIB) + $(link) $(LFLAGS) /OUT:$@ $(OUTL)sftags.o \ + $(OUTL)sf-alloc.o $(OUTL)panic.o \ + $(OUTLHACKLIB) $(OUTL)sftags.o: $(UTIL)sftags.c $(HACK_H) $(Q)$(CC) $(CFLAGS) -Fo$@ -c $(UTIL)sftags.c $(INCL)sfproto.h: $(UTIL)sftags.exe $(UTIL)sf.tags diff --git a/util/sfctool.c b/util/sfctool.c index 8bfc3ccde..ab1ad4d97 100644 --- a/util/sfctool.c +++ b/util/sfctool.c @@ -96,7 +96,7 @@ int util_strncmpi(const char *s1, const char *s2, size_t sz); #ifdef UNIX #define nethack_exit exit ATTRNORETURN void nh_terminate(int) NORETURN; /* bwrite() calls this */ -static void chdirx(const char *); +static void chdirx(const char *, boolean); #else ATTRNORETURN extern void nethack_exit(int) NORETURN; #ifdef WIN32 @@ -184,7 +184,7 @@ main(int argc, char *argv[]) folderbuf[1] = '/'; folderbuf[2] = '\0'; #ifdef CHDIR - chdirx(HACKDIR); + chdirx(HACKDIR, FALSE); #endif #endif #ifdef UNIX @@ -809,8 +809,8 @@ nethack_exit(int code) #ifdef UNIX #ifdef CHDIR -static void -chdirx(const char *dir) +void +chdirx(const char *dir, boolean wr UNUSED) { if (dir) { #ifdef SECURE diff --git a/util/sftags.c b/util/sftags.c index e13ea94ce..9d204cdb3 100644 --- a/util/sftags.c +++ b/util/sftags.c @@ -548,8 +548,8 @@ static void parseExtensionFields (struct tagstruct *tmptag, char *buf) if (colon == (char *)0) { tmptag->tagtype = *field; } else { - const char *key = field; - const char *value = colon + 1; + char *key = field; + char *value = colon + 1; *colon = '\0'; if ((strcmp (key, "struct") == 0) || (strcmp (key, "union") == 0)) { @@ -1844,14 +1844,15 @@ static char * bfsize(const char *str) { static char buf[128]; - const char *c1; - char *c2, *subst; + char *copy_str, *c1, *c2, *subst; + char *retval = buf; if (!str) return (char *)0; + copy_str = dupstr(str); /* kludge */ - subst = strstr(str, ",$/"); + subst = strstr(copy_str, ",$/"); if (subst != 0) { subst++; *subst++ = ' '; @@ -1859,7 +1860,7 @@ bfsize(const char *str) } c2 = buf; - c1 = str; + c1 = copy_str; while (*c1) { if (*c1 == ',') break; @@ -1873,9 +1874,10 @@ bfsize(const char *str) } *c2 = '\0'; } else { - return (char *)0; + retval = (char *) 0; } - return buf; + free((genericptr_t) copy_str); + return retval; } /* Read one line from input, up to and including the next newline From 5f1163c11a7c4d25beb038363f3c05a3da857432 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sat, 4 Apr 2026 15:39:43 +0300 Subject: [PATCH 389/442] Change the tips internal data struct Previously adding any new tips invalidated saves; now you can have up to the number of bits in a long without change in savefile. Invalidates saves. --- include/context.h | 2 +- include/patchlevel.h | 2 +- src/hack.c | 6 +++--- src/weapon.c | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/context.h b/include/context.h index 589a80691..c207c5a8d 100644 --- a/include/context.h +++ b/include/context.h @@ -168,7 +168,7 @@ struct context_info { boolean bypasses; /* bypass flag is set on at least one fobj */ boolean door_opened; /* set to true if door was opened during test_move */ boolean resume_wish; /* game was exited while in wish prompt */ - boolean tips[NUM_TIPS]; + unsigned long tips; struct dig_info digging; struct victual_info victual; struct engrave_info engraving; diff --git a/include/patchlevel.h b/include/patchlevel.h index ee8e27342..5ee218653 100644 --- a/include/patchlevel.h +++ b/include/patchlevel.h @@ -17,7 +17,7 @@ * Incrementing EDITLEVEL can be used to force invalidation of old bones * and save files. */ -#define EDITLEVEL 136 +#define EDITLEVEL 137 /* * Development status possibilities. diff --git a/src/hack.c b/src/hack.c index 360167541..ebdc126c2 100644 --- a/src/hack.c +++ b/src/hack.c @@ -1834,8 +1834,8 @@ handle_tip(int tip) if (!flags.tips) return FALSE; - if (tip >= 0 && tip < NUM_TIPS && !svc.context.tips[tip]) { - svc.context.tips[tip] = TRUE; + if (tip >= 0 && tip < NUM_TIPS && !(svc.context.tips & (1 << tip))) { + svc.context.tips |= (1 << tip); /* the "Tip:" prefix is a hint to use of OPTIONS=!tips to suppress */ switch (tip) { case TIP_ENHANCE: @@ -1886,7 +1886,7 @@ swim_move_danger(coordxy x, coordxy y) || liquid_wall) { if (svc.context.nopick) { /* moving with m-prefix */ - svc.context.tips[TIP_SWIM] = TRUE; + svc.context.tips |= (1 << TIP_SWIM); return FALSE; } else if (ParanoidSwim || liquid_wall) { You("avoid %s into the %s.", diff --git a/src/weapon.c b/src/weapon.c index bd21b93b2..81da05613 100644 --- a/src/weapon.c +++ b/src/weapon.c @@ -1330,7 +1330,7 @@ enhance_weapon_skill(void) boolean speedy = FALSE; /* player knows about #enhance, don't show tip anymore */ - svc.context.tips[TIP_ENHANCE] = TRUE; + svc.context.tips |= (1 << TIP_ENHANCE); if (wizard && y_n("Advance skills without practice?") == 'y') speedy = TRUE; From c5efbf6cf77572596612eaf4e8ec9531187fb5e6 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sat, 4 Apr 2026 16:08:58 +0300 Subject: [PATCH 390/442] Split level rnd mon generation into separate function --- src/allmain.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/allmain.c b/src/allmain.c index e63db5826..efa2e42e7 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -13,7 +13,7 @@ staticfn void moveloop_preamble(boolean); staticfn void u_calc_moveamt(int); - +staticfn void maybe_generate_rnd_mon(void); staticfn void maybe_do_tutorial(void); #ifdef POSITIONBAR staticfn void do_positionbar(void); @@ -156,6 +156,16 @@ u_calc_moveamt(int wtcap) u.umovement = 0; } +/* small chance of generating a new random monster */ +staticfn void +maybe_generate_rnd_mon(void) +{ + if (!rn2(u.uevent.udemigod ? 25 + : (depth(&u.uz) > depth(&stronghold_level)) ? 50 + : 70)) + (void) makemon((struct permonst *) 0, 0, 0, NO_MM_FLAGS); +} + #if defined(MICRO) || defined(WIN32) static int mvl_abort_lev; #endif @@ -226,11 +236,7 @@ moveloop_core(void) /* occasionally add another monster; since this takes place after movement has been allotted, the new monster effectively loses its first turn */ - if (!rn2(u.uevent.udemigod ? 25 - : (depth(&u.uz) > depth(&stronghold_level)) ? 50 - : 70)) - (void) makemon((struct permonst *) 0, 0, 0, - NO_MM_FLAGS); + maybe_generate_rnd_mon(); u_calc_moveamt(mvl_wtcap); settrack(); From 87b3549134530dc0d730438776cd8ff45fc7905b Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sat, 4 Apr 2026 16:57:32 +0300 Subject: [PATCH 391/442] Blessed potion of see invisible Blessed potion of see invisible was guaranteed to give see invisible intrinsic, making it far too easy to acquire. It now has 1/10 chance of giving it permanently, somewhat similarly to potion of invisibility. --- doc/fixes3-7-0.txt | 1 + src/potion.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 47d4f53f7..25782946e 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1593,6 +1593,7 @@ hero has a small chance of catching items thrown at them wizard mode: history menu for #wizwish and WIZKIT monster priests and wizards did not cast spells prevent phaseable monsters hiding deep inside nondiggable walls +blessed potion of see invisible does not guarantee the intrinsic Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/potion.c b/src/potion.c index 10fd55118..9db0d3471 100644 --- a/src/potion.c +++ b/src/potion.c @@ -863,7 +863,7 @@ peffect_see_invisible(struct obj *otmp) */ make_blinded(0L, TRUE); } - if (otmp->blessed) + if (otmp->blessed && !rn2(10)) HSee_invisible |= FROMOUTSIDE; else incr_itimeout(&HSee_invisible, rn1(100, 750)); From 9c8c17b70cc429034edd3a2a7caa639688dcd0cc Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sat, 4 Apr 2026 17:58:24 +0300 Subject: [PATCH 392/442] Tweak the blessed potion of see invisible Blessed potion guarantees the intrinsic, if you're invisible and can see invisible before quaffing. --- src/potion.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/potion.c b/src/potion.c index 9db0d3471..d56b64155 100644 --- a/src/potion.c +++ b/src/potion.c @@ -841,6 +841,7 @@ staticfn void peffect_see_invisible(struct obj *otmp) { int msg = Invisible && !Blind; + int permchance = 10 - (HInvis ? 3 : 0) - (HSee_invisible ? 6 : 0); gp.potion_unkn++; if (otmp->cursed) @@ -863,7 +864,7 @@ peffect_see_invisible(struct obj *otmp) */ make_blinded(0L, TRUE); } - if (otmp->blessed && !rn2(10)) + if (otmp->blessed && !rn2(permchance)) HSee_invisible |= FROMOUTSIDE; else incr_itimeout(&HSee_invisible, rn1(100, 750)); From 92340a6827cf4118ece99cc31c84cd7298e30587 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 4 Apr 2026 13:44:23 -0400 Subject: [PATCH 393/442] consolidate some arg processing Unix and Windows had diverged significantly for command line options handling. This: 1. uses the the Unix processing as a baseline. 2. consolidates the code in earlyarg.c, where it can be a common copy to be shared. 3. start converting the Windows command line argument processing to the Unix code that now resides in earlyarg.c. --- include/extern.h | 8 +- include/hack.h | 1 + src/cfgfiles.c | 4 +- src/earlyarg.c | 389 ++++++++++++++++++++++++++++++++++++++- sys/share/pcmain.c | 2 +- sys/unix/unixmain.c | 369 +------------------------------------ sys/windows/consoletty.c | 24 +-- sys/windows/windmain.c | 85 ++++++--- 8 files changed, 480 insertions(+), 402 deletions(-) diff --git a/include/extern.h b/include/extern.h index cf20ca090..8ed1b0f0f 100644 --- a/include/extern.h +++ b/include/extern.h @@ -922,6 +922,10 @@ extern const char *endgamelevelname(char *, int); /* ### earlyarg.c ### */ extern int argcheck(int, char **, enum earlyarg); +extern void early_options(int *argc_p, char ***argv_p, char **hackdir_p); +#ifdef WIN32 +int windows_early_options(const char *); +#endif /* ### eat.c ### */ @@ -2344,9 +2348,9 @@ extern int dohistory(void); /* ### xxmain.c ### */ -#if defined(MICRO) || defined(WIN32) +#if defined(UNIX) || defined(MICRO) || defined(WIN32) #ifdef CHDIR -extern void chdirx(char *, boolean); +extern void chdirx(const char *, boolean); #endif /* CHDIR */ extern boolean authorize_wizard_mode(void); extern boolean authorize_explore_mode(void); diff --git a/include/hack.h b/include/hack.h index 0a025a902..bb9f9d642 100644 --- a/include/hack.h +++ b/include/hack.h @@ -817,6 +817,7 @@ struct sinfo { interface to suppress menu commands in similar conditions; readchar() always resets it to 'otherInp' prior to returning */ int input_state; /* whether next key pressed will be entering a command */ + int early_options; /* inside early_options processing */ #ifdef TTY_GRAPHICS /* resize_pending only matters when handling a SIGWINCH signal for tty; getting_char is used along with that and also separately for UNIX; diff --git a/src/cfgfiles.c b/src/cfgfiles.c index 2084866d0..1b4715a4f 100644 --- a/src/cfgfiles.c +++ b/src/cfgfiles.c @@ -1907,9 +1907,9 @@ assure_syscf_file(void) #else fd = open(SYSCF_FILE, O_RDONLY); #endif -#else +#else /* VMS */ fd = open(SYSCF_FILE, O_RDONLY, 0); -#endif +#endif /* VMS */ if (fd >= 0) { /* readable */ close(fd); diff --git a/src/earlyarg.c b/src/earlyarg.c index 166510993..14a44f770 100755 --- a/src/earlyarg.c +++ b/src/earlyarg.c @@ -3,11 +3,22 @@ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" +#include "dlb.h" staticfn void debug_fields(char *); #ifndef NODUMPENUMS staticfn void dump_enums(void); #endif +ATTRNORETURN staticfn void opt_terminate(void) NORETURN; +ATTRNORETURN staticfn void opt_usage(const char *) NORETURN; +ATTRNORETURN staticfn void scores_only(int, char **, const char *) NORETURN; +staticfn char *lopt(char *, int, const char *, const char *, int *, char ***); +staticfn void consume_arg(int, int *, char ***); +staticfn void consume_two_args(int, int *, char ***); + +#ifdef UNIX +extern boolean whoami(void); +#endif /* * Argument processing helpers - for xxmain() to share @@ -40,9 +51,383 @@ static const struct early_opt earlyopts[] = { #endif }; -#ifdef WIN32 -extern int windows_early_options(const char *); +static char ArgVal_novalue[] = "[nothing]"; /* note: not 'const' */ + +enum cmdlinearg { + ArgValRequired = 0, + ArgValOptional = 1, + ArgValDisallowed = 2, + ArgVal_mask = (1 | 2), + ArgNamOneLetter = 4, + ArgNam_mask = 4, + ArgErrSilent = 0, + ArgErrComplain = 8, + ArgErr_mask = 8 +}; + +/* approximate 'getopt_long()' for one option; all the comments refer to + "-windowtype" but the code isn't specific to that */ +staticfn char * +lopt(char *arg, /* command line token; beginning matches 'optname' */ + int lflags, /* cmdlinearg | errorhandling */ + const char *optname, /* option's name; "-windowtype" in examples below */ + const char *origarg, /* 'arg' might have had a dash prefix removed */ + int *argc_p, /* argc that can have changes passed to caller */ + char ***argv_p) /* argv[] ditto */ +{ + int argc = *argc_p; + char **argv = *argv_p; + char *p, *nextarg = (argc > 1 && argv[1][0] != '-') ? argv[1] : 0; + int l, opttype = (lflags & ArgVal_mask); + boolean oneletterok = ((lflags & ArgNam_mask) == ArgNamOneLetter), + complain = ((lflags & ArgErr_mask) == ArgErrComplain); + + /* first letter must match */ + if (arg[1] != optname[1]) { + loptbail: + if (complain) + config_error_add("Unknown option: %.60s", origarg); + return (char *) 0; + loptnotallowed: + if (complain) + config_error_add("Value not allowed: %.60s", origarg); + return (char *) 0; + loptrequired: + if (complain) + config_error_add("Missing required value: %.60s", origarg); + return (char *) 0; + } + + if ((p = strchr(arg, '=')) == 0) + p = strchr(arg, ':'); + if (p && opttype == ArgValDisallowed) + goto loptnotallowed; + + l = (int) (p ? (long) (p - arg) : (long) strlen(arg)); + if ((l > 2 || oneletterok) && !strncmp(arg, optname, l)) { + /* "-windowtype[=foo]" */ + if (p) + ++p; /* past '=' or ':' */ + else if (opttype == ArgValRequired) + p = eos(arg); /* we have "-w[indowtype]" w/o "=foo" + * so we'll take foo from next element */ + else + return ArgVal_novalue; + } else if (oneletterok) { + /* "-w..." but not "-w[indowtype[=foo]]" */ + if (!p) { + p = &arg[2]; /* past 'w' of "-wfoo" */ +#if 0 /* -x:value could work but is not supported (callers don't expect it) \ + */ + } else if (p == arg + 2) { + ++p; /* past ':' of "-w:foo" */ #endif + } else { + /* "-w...=foo" but not "-w[indowtype]=foo" */ + goto loptbail; + } + } else { + goto loptbail; + } + if (!p || !*p) { + /* "-w[indowtype]" w/o '='/':' if there is a next element, use + it for "foo"; if not, supply a non-Null bogus value */ + if (nextarg + && (opttype == ArgValRequired || opttype == ArgValOptional)) + p = nextarg, --(*argc_p), ++(*argv_p); + else if (opttype == ArgValRequired) + goto loptrequired; + else + p = ArgVal_novalue; /* there is no next element */ + } + return p; +} +/* move argv[ndx] to end of argv[] array, then reduce argc to hide it; + prevents process_options() from encountering it after early_options() + has processed it; elements get reordered but all remain intact */ +staticfn void +consume_arg(int ndx, int *ac_p, char ***av_p) +{ + char *gone, **av = *av_p; + int i, ac = *ac_p; + + /* "-one -two -three -four" -> "-two -three -four -one" */ + if (ac > 2) { + gone = av[ndx]; + for (i = ndx + 1; i < ac; ++i) + av[i - 1] = av[i]; + av[ac - 1] = gone; + } + --(*ac_p); +} + +/* consume two tokens for '-argname value' w/o '=' or ':' */ +staticfn void +consume_two_args(int ndx, int *ac_p, char ***av_p) +{ + /* when consuming "-two arg" from "-two arg -three -four", + the *ac_p manipulation results in "-three -four -two arg" + rather than the "-three -four arg -two" that would happen + with just two ordinary consume_arg() calls */ + consume_arg(ndx, ac_p, av_p); + ++(*ac_p); /* bring the final slot back into view */ + consume_arg(ndx, ac_p, av_p); + --(*ac_p); /* take away restored slot */ +} + +/* process some command line arguments before loading options */ +void +early_options(int *argc_p, char ***argv_p, char **hackdir_p) +{ + char **argv, *arg, *origarg; + int argc, oldargc, ndx = 0, consumed = 0; + + config_error_init(FALSE, "command line", FALSE); + + /* treat "nethack ?" as a request for usage info; due to shell + processing, player likely has to use "nethack \?" or "nethack '?'" + [won't work if used as "nethack -dpath ?" or "nethack -d path ?"] */ + if (*argc_p > 1 && !strcmp((*argv_p)[1], "?")) + opt_usage(*hackdir_p); /* doesn't return */ + + /* + * Both *argc_p and *argv_p account for the program name as (*argv_p)[0]; + * local argc and argv implicitly discard that (by starting 'ndx' at 1). + * argcheck() doesn't mind, prscore() (via scores_only()) does (for the + * number of args it gets passed, not for the value of argv[0]). + */ + for (ndx = 1; ndx < *argc_p; ndx += (consumed ? 0 : 1)) { + consumed = 0; + argc = *argc_p - ndx; + argv = *argv_p + ndx; + + arg = origarg = argv[0]; + /* skip any args intended for deferred options */ + if (*arg != '-') + continue; + /* allow second dash if arg name is longer than one character */ + if (arg[0] == '-' && arg[1] == '-' && arg[2] != '\0' + && (arg[3] != '\0' && arg[3] != '=' && arg[3] != ':')) + ++arg; + + switch (arg[1]) { /* char after leading dash */ + case 'b': +#ifdef CRASHREPORT + // --bidshow + if (argcheck(argc, argv, ARG_BIDSHOW) == 2) { + opt_terminate(); + /*NOTREACHED*/ + } +#endif + break; + case 'd': + if (argcheck(argc, argv, ARG_DEBUG) == 1) { + consume_arg(ndx, argc_p, argv_p), consumed = 1; +#ifndef NODUMPENUMS + } else if (argcheck(argc, argv, ARG_DUMPENUMS) == 2) { + opt_terminate(); + /*NOTREACHED*/ +#endif + } else if (argcheck(argc, argv, ARG_DUMPMONGEN) == 2) { + opt_terminate(); + /*NOTREACHED*/ + } else if (argcheck(argc, argv, ARG_DUMPWEIGHTS) == 2) { + opt_terminate(); + /*NOTREACHED*/ + } else { +#ifdef CHDIR + oldargc = argc; + arg = lopt(arg, + (ArgValRequired | ArgNamOneLetter | ArgErrSilent), + "-directory", origarg, &argc, &argv); + if (!arg) + error("Flag -d must be followed by a directory name."); + if (*arg != 'e') { /* avoid matching -decgraphics or -debug */ + *hackdir_p = arg; + if (oldargc == argc) + consume_arg(ndx, argc_p, argv_p), consumed = 1; + else + consume_two_args(ndx, argc_p, argv_p), consumed = 2; + } +#endif /* CHDIR */ + } + break; + case 'h': + case '?': + if (lopt(arg, ArgValDisallowed, "-help", origarg, &argc, &argv) + || lopt(arg, ArgValDisallowed | ArgNamOneLetter, "-?", + origarg, &argc, &argv)) + opt_usage(*hackdir_p); /* doesn't return */ + break; + case 'n': + oldargc = argc; + if (!strcmp(arg, "-no-nethackrc")) /* no abbreviation allowed */ + arg = nhStr("/dev/null"); + else + arg = lopt(arg, (ArgValRequired | ArgErrComplain), + "-nethackrc", origarg, &argc, &argv); + if (arg) { + gc.cmdline_rcfile = dupstr(arg); + if (oldargc == argc) + consume_arg(ndx, argc_p, argv_p), consumed = 1; + else + consume_two_args(ndx, argc_p, argv_p), consumed = 2; + } + break; + case 's': + if (argcheck(argc, argv, ARG_SHOWPATHS) == 2) { + gd.deferred_showpaths = TRUE; + gd.deferred_showpaths_dir = *hackdir_p; + config_error_done(); + return; + } + /* check for "-s" request to show scores */ + if (lopt(arg, + ((ArgValDisallowed | ArgErrComplain) + /* only accept one-letter if there is just one + dash; reject "--s" because prscore() via + scores_only() doesn't understand it */ + | ((origarg[1] != '-') ? ArgNamOneLetter : 0)), + /* [ought to omit val-disallowed and accept + --scores=foo since -s foo and -sfoo are + allowed, but -s form can take more than one + space-separated argument and --scores=foo + isn't suited for that] */ + "-scores", origarg, &argc, &argv)) { + /* at this point, argv[0] contains "-scores" or a leading + substring of it; prscore() (via scores_only()) expects + that to be in argv[1] so we adjust the pointer to make + that be the case; if there are any non-early args waiting + to be passed along to process_options(), the resulting + argv[0] will be one of those rather than the program + name but prscore() doesn't care */ + scores_only(argc + 1, argv - 1, *hackdir_p); + /*NOTREACHED*/ + } + break; + case 'u': +#if defined(UNIX) + if (lopt(arg, ArgValDisallowed, "-usage", origarg, &argc, &argv)) + opt_usage(*hackdir_p); +#elif defined(WIN32) || defined(MSDOS) || defined(AMIGA) + if (arg[2]) { + (void) strncpy(svp.plname, arg + 2, sizeof(svp.plname) - 1); + } else if (ndx + 1 < *argc_p) { + const char *nextarg = (*argv_p)[ndx + 1]; + + if (nextarg[0] != '-') { + (void) strncpy(svp.plname, nextarg, sizeof(svp.plname) - 1); + } else { + raw_print("Player name expected after -u\n"); + } + } +#endif + break; + case 'v': + if (argcheck(argc, argv, ARG_VERSION) == 2) { + opt_terminate(); + /*NOTREACHED*/ + } + break; + case 'w': /* windowtype: "-wfoo" or "-w[indowtype]=foo" + * or "-w[indowtype]:foo" or "-w[indowtype] foo" */ + arg = + lopt(arg, (ArgValRequired | ArgNamOneLetter | ArgErrComplain), + "-windowtype", origarg, &argc, &argv); + if (gc.cmdline_windowsys) + free((genericptr_t) gc.cmdline_windowsys); + gc.cmdline_windowsys = arg ? dupstr(arg) : NULL; + break; + #if !defined(UNIX) && !defined(VMS) + case 'D': + wizard = TRUE, discover = FALSE; + break; + case 'X': + discover = TRUE, wizard = FALSE; + break; +#endif + default: + break; + } + } + /* empty or "N errors on command line" */ + config_error_done(); + return; +} +/* for command-line options that perform some immediate action and then + terminate the program without starting play, like 'nethack --version' + or 'nethack -s Zelda'; do some cleanup before that termination */ +ATTRNORETURN staticfn void +opt_terminate(void) +{ + program_state.early_options = 0; + config_error_done(); /* free memory allocated by config_error_init() */ + + nh_terminate(EXIT_SUCCESS); + /*NOTREACHED*/ +} + +ATTRNORETURN staticfn void +opt_usage(const char *hackdir) +{ +#ifdef CHDIR + chdirx(hackdir, TRUE); +#else + nhUse(hackdir); +#endif + dlb_init(); + + genl_display_file(USAGEHELP, TRUE); + opt_terminate(); +} +/* show the sysconf file name, playground directory, run-time configuration + file name, dumplog file name if applicable, and some other things */ +ATTRNORETURN void +after_opt_showpaths(const char *dir) +{ +#ifdef CHDIR + chdirx(dir, FALSE); +#else + nhUse(dir); +#endif + opt_terminate(); + /*NOTREACHED*/ +} + +/* handle "-s [character-names]" to show all the entries + in the high scores file ('record') belonging to particular characters; + nethack will end after doing so without starting play */ +ATTRNORETURN staticfn void +scores_only(int argc, char **argv, const char *dir) +{ + /* do this now rather than waiting for final termination, in case there + is an error summary coming */ + config_error_done(); + +#ifdef CHDIR + chdirx(dir, FALSE); +#else + nhUse(dir); +#endif +#ifdef SYSCF + iflags.initoptions_noterminate = TRUE; + initoptions(); /* sysconf options affect whether panictrace is enabled */ + iflags.initoptions_noterminate = FALSE; +#endif +#ifdef PANICTRACE + ARGV0 = gh.hname; /* save for possible stack trace */ +#ifndef NO_SIGNAL + panictrace_setsignals(TRUE); +#endif +#endif +#ifdef UNIX + (void) whoami(); /* set up default plname[] */ +#endif + prscore(argc, argv); + + nh_terminate(EXIT_SUCCESS); /* bypass opt_terminate() */ + /*NOTREACHED*/ +} /* * Returns: diff --git a/sys/share/pcmain.c b/sys/share/pcmain.c index fdfc599c6..39e5aec4e 100644 --- a/sys/share/pcmain.c +++ b/sys/share/pcmain.c @@ -671,7 +671,7 @@ nhusage(void) #ifdef CHDIR void -chdirx(char *dir, boolean wr) +chdirx(const char *dir, boolean wr) { #ifdef AMIGA static char thisdir[] = ""; diff --git a/sys/unix/unixmain.c b/sys/unix/unixmain.c index 6fb14c417..65a5401c4 100644 --- a/sys/unix/unixmain.c +++ b/sys/unix/unixmain.c @@ -26,17 +26,11 @@ extern struct passwd *getpwuid(int); #endif extern struct passwd *getpwnam(const char *); #ifdef CHDIR -static void chdirx(const char *, boolean); +void chdirx(const char *, boolean); #endif /* CHDIR */ -static boolean whoami(void); -static char *lopt(char *, int, const char *, const char *, int *, char ***); +boolean whoami(void); static void process_options(int, char **); -static void consume_arg(int, int *, char ***); -static void consume_two_args(int, int *, char ***); -static void early_options(int *, char ***, char **); -ATTRNORETURN static void opt_terminate(void) NORETURN; -ATTRNORETURN static void opt_usage(const char *) NORETURN; -ATTRNORETURN static void scores_only(int, char **, const char *) NORETURN; + #ifdef SND_LIB_INTEGRATED uint32_t soundlibchoice = soundlib_nosound; #endif @@ -128,12 +122,14 @@ main(int argc, char *argv[]) if (!dir) dir = nh_getenv("HACKDIR"); #endif /* CHDIR */ + program_state.early_options = 1; #ifdef ENHANCED_SYMBOLS if (argcheck(argc, argv, ARG_DUMPGLYPHIDS) == 2) exit(EXIT_SUCCESS); #endif /* handle -dalthackdir, -s , --version, --showpaths */ early_options(&argc, &argv, &dir); + program_state.early_options = 0; #ifdef CHDIR /* * Change directories before we initialize the window system so @@ -325,93 +321,6 @@ main(int argc, char *argv[]) return 0; } -static char ArgVal_novalue[] = "[nothing]"; /* note: not 'const' */ - -enum cmdlinearg { - ArgValRequired = 0, ArgValOptional = 1, - ArgValDisallowed = 2, ArgVal_mask = (1 | 2), - ArgNamOneLetter = 4, ArgNam_mask = 4, - ArgErrSilent = 0, ArgErrComplain = 8, ArgErr_mask = 8 -}; - -/* approximate 'getopt_long()' for one option; all the comments refer to - "-windowtype" but the code isn't specific to that */ -static char * -lopt( - char *arg, /* command line token; beginning matches 'optname' */ - int lflags, /* cmdlinearg | errorhandling */ - const char *optname, /* option's name; "-windowtype" in examples below */ - const char *origarg, /* 'arg' might have had a dash prefix removed */ - int *argc_p, /* argc that can have changes passed to caller */ - char ***argv_p) /* argv[] ditto */ -{ - int argc = *argc_p; - char **argv = *argv_p; - char *p, *nextarg = (argc > 1 && argv[1][0] != '-') ? argv[1] : 0; - int l, opttype = (lflags & ArgVal_mask); - boolean oneletterok = ((lflags & ArgNam_mask) == ArgNamOneLetter), - complain = ((lflags & ArgErr_mask) == ArgErrComplain); - - /* first letter must match */ - if (arg[1] != optname[1]) { - loptbail: - if (complain) - config_error_add("Unknown option: %.60s", origarg); - return (char *) 0; - loptnotallowed: - if (complain) - config_error_add("Value not allowed: %.60s", origarg); - return (char *) 0; - loptrequired: - if (complain) - config_error_add("Missing required value: %.60s", origarg); - return (char *) 0; - } - - if ((p = strchr(arg, '=')) == 0) - p = strchr(arg, ':'); - if (p && opttype == ArgValDisallowed) - goto loptnotallowed; - - l = (int) (p ? (long) (p - arg) : (long) strlen(arg)); - if ((l > 2 || oneletterok) && !strncmp(arg, optname, l)) { - /* "-windowtype[=foo]" */ - if (p) - ++p; /* past '=' or ':' */ - else if (opttype == ArgValRequired) - p = eos(arg); /* we have "-w[indowtype]" w/o "=foo" - * so we'll take foo from next element */ - else - return ArgVal_novalue; - } else if (oneletterok) { - /* "-w..." but not "-w[indowtype[=foo]]" */ - if (!p) { - p = &arg[2]; /* past 'w' of "-wfoo" */ -#if 0 /* -x:value could work but is not supported (callers don't expect it) */ - } else if (p == arg + 2) { - ++p; /* past ':' of "-w:foo" */ -#endif - } else { - /* "-w...=foo" but not "-w[indowtype]=foo" */ - goto loptbail; - } - } else { - goto loptbail; - } - if (!p || !*p) { - /* "-w[indowtype]" w/o '='/':' if there is a next element, use - it for "foo"; if not, supply a non-Null bogus value */ - if (nextarg && (opttype == ArgValRequired - || opttype == ArgValOptional)) - p = nextarg, --(*argc_p), ++(*argv_p); - else if (opttype == ArgValRequired) - goto loptrequired; - else - p = ArgVal_novalue; /* there is no next element */ - } - return p; -} - /* caveat: argv elements might be arbitrarily long */ static void process_options(int argc, char *argv[]) @@ -573,272 +482,8 @@ process_options(int argc, char *argv[]) return; } -/* move argv[ndx] to end of argv[] array, then reduce argc to hide it; - prevents process_options() from encountering it after early_options() - has processed it; elements get reordered but all remain intact */ -static void -consume_arg(int ndx, int *ac_p, char ***av_p) -{ - char *gone, **av = *av_p; - int i, ac = *ac_p; - - /* "-one -two -three -four" -> "-two -three -four -one" */ - if (ac > 2) { - gone = av[ndx]; - for (i = ndx + 1; i < ac; ++i) - av[i - 1] = av[i]; - av[ac - 1] = gone; - } - --(*ac_p); -} - -/* consume two tokens for '-argname value' w/o '=' or ':' */ -static void -consume_two_args(int ndx, int *ac_p, char ***av_p) -{ - /* when consuming "-two arg" from "-two arg -three -four", - the *ac_p manipulation results in "-three -four -two arg" - rather than the "-three -four arg -two" that would happen - with just two ordinary consume_arg() calls */ - consume_arg(ndx, ac_p, av_p); - ++(*ac_p); /* bring the final slot back into view */ - consume_arg(ndx, ac_p, av_p); - --(*ac_p); /* take away restored slot */ -} - -/* process some command line arguments before loading options */ -static void -early_options(int *argc_p, char ***argv_p, char **hackdir_p) -{ - char **argv, *arg, *origarg; - int argc, oldargc, ndx = 0, consumed = 0; - - config_error_init(FALSE, "command line", FALSE); - - /* treat "nethack ?" as a request for usage info; due to shell - processing, player likely has to use "nethack \?" or "nethack '?'" - [won't work if used as "nethack -dpath ?" or "nethack -d path ?"] */ - if (*argc_p > 1 && !strcmp((*argv_p)[1], "?")) - opt_usage(*hackdir_p); /* doesn't return */ - - /* - * Both *argc_p and *argv_p account for the program name as (*argv_p)[0]; - * local argc and argv implicitly discard that (by starting 'ndx' at 1). - * argcheck() doesn't mind, prscore() (via scores_only()) does (for the - * number of args it gets passed, not for the value of argv[0]). - */ - for (ndx = 1; ndx < *argc_p; ndx += (consumed ? 0 : 1)) { - consumed = 0; - argc = *argc_p - ndx; - argv = *argv_p + ndx; - - arg = origarg = argv[0]; - /* skip any args intended for deferred options */ - if (*arg != '-') - continue; - /* allow second dash if arg name is longer than one character */ - if (arg[0] == '-' && arg[1] == '-' && arg[2] != '\0' - && (arg[3] != '\0' && arg[3] != '=' && arg[3] != ':')) - ++arg; - - switch (arg[1]) { /* char after leading dash */ - case 'b': -#ifdef CRASHREPORT - // --bidshow - if (argcheck(argc, argv, ARG_BIDSHOW) == 2){ - opt_terminate(); - /*NOTREACHED*/ - } -#endif - break; - case 'd': - if (argcheck(argc, argv, ARG_DEBUG) == 1) { - consume_arg(ndx, argc_p, argv_p), consumed = 1; -#ifndef NODUMPENUMS - } else if (argcheck(argc, argv, ARG_DUMPENUMS) == 2) { - opt_terminate(); - /*NOTREACHED*/ -#endif - } else if (argcheck(argc, argv, ARG_DUMPMONGEN) == 2) { - opt_terminate(); - /*NOTREACHED*/ - } else if (argcheck(argc, argv, ARG_DUMPWEIGHTS) == 2) { - opt_terminate(); - /*NOTREACHED*/ - } else { #ifdef CHDIR - oldargc = argc; - arg = lopt(arg, - (ArgValRequired | ArgNamOneLetter | ArgErrSilent), - "-directory", origarg, &argc, &argv); - if (!arg) - error("Flag -d must be followed by a directory name."); - if (*arg != 'e') { /* avoid matching -decgraphics or -debug */ - *hackdir_p = arg; - if (oldargc == argc) - consume_arg(ndx, argc_p, argv_p), consumed = 1; - else - consume_two_args(ndx, argc_p, argv_p), consumed = 2; - } -#endif /* CHDIR */ - } - break; - case 'h': - case '?': - if (lopt(arg, ArgValDisallowed, "-help", origarg, &argc, &argv) - || lopt(arg, ArgValDisallowed | ArgNamOneLetter, "-?", - origarg, &argc, &argv)) - opt_usage(*hackdir_p); /* doesn't return */ - break; - case 'n': - oldargc = argc; - if (!strcmp(arg, "-no-nethackrc")) /* no abbreviation allowed */ - arg = nhStr("/dev/null"); - else - arg = lopt(arg, (ArgValRequired | ArgErrComplain), - "-nethackrc", origarg, &argc, &argv); - if (arg) { - gc.cmdline_rcfile = dupstr(arg); - if (oldargc == argc) - consume_arg(ndx, argc_p, argv_p), consumed = 1; - else - consume_two_args(ndx, argc_p, argv_p), consumed = 2; - } - break; - case 's': - if (argcheck(argc, argv, ARG_SHOWPATHS) == 2) { - gd.deferred_showpaths = TRUE; - gd.deferred_showpaths_dir = *hackdir_p; - config_error_done(); - return; - } - /* check for "-s" request to show scores */ - if (lopt(arg, ((ArgValDisallowed | ArgErrComplain) - /* only accept one-letter if there is just one - dash; reject "--s" because prscore() via - scores_only() doesn't understand it */ - | ((origarg[1] != '-') ? ArgNamOneLetter : 0)), - /* [ought to omit val-disallowed and accept - --scores=foo since -s foo and -sfoo are - allowed, but -s form can take more than one - space-separated argument and --scores=foo - isn't suited for that] */ - "-scores", origarg, &argc, &argv)) { - /* at this point, argv[0] contains "-scores" or a leading - substring of it; prscore() (via scores_only()) expects - that to be in argv[1] so we adjust the pointer to make - that be the case; if there are any non-early args waiting - to be passed along to process_options(), the resulting - argv[0] will be one of those rather than the program - name but prscore() doesn't care */ - scores_only(argc + 1, argv - 1, *hackdir_p); - /*NOTREACHED*/ - } - break; - case 'u': - if (lopt(arg, ArgValDisallowed, "-usage", origarg, &argc, &argv)) - opt_usage(*hackdir_p); - break; - case 'v': - if (argcheck(argc, argv, ARG_VERSION) == 2) { - opt_terminate(); - /*NOTREACHED*/ - } - break; - case 'w': /* windowtype: "-wfoo" or "-w[indowtype]=foo" - * or "-w[indowtype]:foo" or "-w[indowtype] foo" */ - arg = lopt(arg, - (ArgValRequired | ArgNamOneLetter | ArgErrComplain), - "-windowtype", origarg, &argc, &argv); - if (gc.cmdline_windowsys) - free((genericptr_t) gc.cmdline_windowsys); - gc.cmdline_windowsys = arg ? dupstr(arg) : NULL; - break; - default: - break; - } - } - /* empty or "N errors on command line" */ - config_error_done(); - return; -} - -/* for command-line options that perform some immediate action and then - terminate the program without starting play, like 'nethack --version' - or 'nethack -s Zelda'; do some cleanup before that termination */ -ATTRNORETURN static void -opt_terminate(void) -{ - config_error_done(); /* free memory allocated by config_error_init() */ - - nh_terminate(EXIT_SUCCESS); - /*NOTREACHED*/ -} - -ATTRNORETURN static void -opt_usage(const char *hackdir) -{ -#ifdef CHDIR - chdirx(hackdir, TRUE); -#else - nhUse(hackdir); -#endif - dlb_init(); - - genl_display_file(USAGEHELP, TRUE); - opt_terminate(); -} - -/* show the sysconf file name, playground directory, run-time configuration - file name, dumplog file name if applicable, and some other things */ -ATTRNORETURN void -after_opt_showpaths(const char *dir) -{ -#ifdef CHDIR - chdirx(dir, FALSE); -#else - nhUse(dir); -#endif - opt_terminate(); - /*NOTREACHED*/ -} - -/* handle "-s [character-names]" to show all the entries - in the high scores file ('record') belonging to particular characters; - nethack will end after doing so without starting play */ -ATTRNORETURN static void -scores_only(int argc, char **argv, const char *dir) -{ - /* do this now rather than waiting for final termination, in case there - is an error summary coming */ - config_error_done(); - -#ifdef CHDIR - chdirx(dir, FALSE); -#else - nhUse(dir); -#endif -#ifdef SYSCF - iflags.initoptions_noterminate = TRUE; - initoptions(); /* sysconf options affect whether panictrace is enabled */ - iflags.initoptions_noterminate = FALSE; -#endif -#ifdef PANICTRACE - ARGV0 = gh.hname; /* save for possible stack trace */ -#ifndef NO_SIGNAL - panictrace_setsignals(TRUE); -#endif -#endif - (void) whoami(); /* set up default plname[] */ - - prscore(argc, argv); - - nh_terminate(EXIT_SUCCESS); /* bypass opt_terminate() */ - /*NOTREACHED*/ -} - -#ifdef CHDIR -static void +void chdirx(const char *dir, boolean wr) { if (dir /* User specified directory? */ @@ -905,7 +550,7 @@ chdirx(const char *dir, boolean wr) #endif /* CHDIR */ /* returns True iff we set plname[] to username which contains a hyphen */ -static boolean +boolean whoami(void) { /* diff --git a/sys/windows/consoletty.c b/sys/windows/consoletty.c index ed44f1271..34489430f 100644 --- a/sys/windows/consoletty.c +++ b/sys/windows/consoletty.c @@ -2590,19 +2590,19 @@ void nethack_enter_consoletty(void) /* clear the entire console buffer */ - int size = console.orig_csbi.dwSize.X * console.orig_csbi.dwSize.Y; - DWORD unused; - set_console_cursor(0, 0); - FillConsoleOutputAttribute( - console.hConOut, CONSOLE_CLEAR_ATTRIBUTE, - size, console.cursor, &unused); + //int size = console.orig_csbi.dwSize.X * console.orig_csbi.dwSize.Y; + //DWORD unused; + //set_console_cursor(0, 0); + // FillConsoleOutputAttribute( + // console.hConOut, CONSOLE_CLEAR_ATTRIBUTE, + // size, console.cursor, &unused); - FillConsoleOutputCharacter( - console.hConOut, CONSOLE_CLEAR_CHARACTER, - size, console.cursor, &unused); + // FillConsoleOutputCharacter( + // console.hConOut, CONSOLE_CLEAR_CHARACTER, + // size, console.cursor, &unused); - set_console_cursor(1, 0); - SetConsoleCursorPosition(console.hConOut, console.cursor); + //set_console_cursor(1, 0); + //SetConsoleCursorPosition(console.hConOut, console.cursor); /* At this point early_raw_print will work */ @@ -2757,7 +2757,7 @@ VA_DECL(const char *, fmt) VA_START(fmt); VA_INIT(fmt, const char *); (void) vsnprintf(buf, sizeof buf, fmt, VA_ARGS); - if (redirect_stdout) + if (redirect_stdout || program_state.early_options) fprintf(stdout, "%s", buf); else { #ifdef TTY_GRAPHICS diff --git a/sys/windows/windmain.c b/sys/windows/windmain.c index a2fe2da5e..9e34152ac 100644 --- a/sys/windows/windmain.c +++ b/sys/windows/windmain.c @@ -19,7 +19,6 @@ #endif static void nhusage(void); -static void early_options(int argc, char **argv); char *exename(void); boolean fakeconsole(void); void freefakeconsole(void); @@ -157,6 +156,7 @@ MAIN(int argc, char *argv[]) HWND hwnd; HDC hdc; int bpp; + char *dir = NULL; #ifdef _MSC_VER _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); @@ -167,12 +167,10 @@ MAIN(int argc, char *argv[]) * Get a set of valid safe windowport function * pointers during early startup initialization. */ - safe_routines(); +// safe_routines(); #endif /* WIN32CON */ -#ifndef MSWIN_GRAPHICS - early_init(argc, argv); /* already in WinMain for MSWIN_GRAPHICS */ -#endif + /* setting iflags.colorcount has to be after early_init() * because it zeros out all of iflags */ hwnd = GetDesktopWindow(); @@ -207,6 +205,35 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ #endif gh.hname = "NetHack"; /* used for syntax messages */ +#ifndef MSWIN_GRAPHICS + early_init(argc, argv); /* already in WinMain for MSWIN_GRAPHICS */ +#endif + set_default_prefix_locations(argv[0]); /* must be re-done after initoptions_init() + * which clears out gp.fqn_prefix[] */ + copy_sysconf_content(); + copy_symbols_content(); + /* Now that sysconf has had a chance to set the TROUBLEPREFIX, don't + allow it to be changed from here on out. */ + fqn_prefix_locked[TROUBLEPREFIX] = TRUE; + copy_config_content(); + + // if (iflags.windowtype_deferred && gc.chosen_windowtype[0]) + // windowtype = gc.chosen_windowtype; + // windowtype = gc.chosen_windowtype; + +#if !defined(MSWIN_GRAPHICS) + consoletty_open(1); + nethack_enter_consoletty(); +#endif + + if (!windowtype) { +#ifdef MSWIN_GRAPHICS + windowtype = "mswin"; +#else + windowtype = "tty"; +#endif + } + choose_windows(windowtype); /* sets all the window port function pointers */ #if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) /* Save current directory and make sure it gets restored when @@ -216,7 +243,14 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ error("NetHack: current directory path too long"); #endif initoptions_init(); // This allows OPTIONS in syscf on Windows. - set_default_prefix_locations(argv[0]); + set_default_prefix_locations(argv[0]); /* must be re-done after initoptions_init() + * which clears out gp.fqn_prefix[] */ + iflags.windowtype_deferred = TRUE; + + program_state.early_options = 1; + early_options(&argc, &argv, &dir); + program_state.early_options = 0; + initoptions(); #if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) chdir(gf.fqn_prefix[HACKPREFIX]); @@ -226,18 +260,6 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ getreturn_enabled = TRUE; check_recordfile((char *) 0); - iflags.windowtype_deferred = TRUE; - copy_sysconf_content(); - copy_symbols_content(); - early_options(argc, argv); - initoptions(); - - /* Now that sysconf has had a chance to set the TROUBLEPREFIX, don't - allow it to be changed from here on out. */ - fqn_prefix_locked[TROUBLEPREFIX] = TRUE; - - copy_config_content(); - /* did something earlier flag a need to exit without starting a game? */ if (windows_startup_state > 0) { raw_printf("Exiting."); @@ -287,10 +309,10 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ Strcpy(default_window_sys, "curses"); #endif /* CURSES */ #endif /* TTY */ - if (iflags.windowtype_deferred && gc.chosen_windowtype[0]) - windowtype = gc.chosen_windowtype; + // if (iflags.windowtype_deferred && gc.chosen_windowtype[0]) + // windowtype = gc.chosen_windowtype; } - choose_windows(windowtype); + // choose_windows(windowtype); #if defined(SND_LIB_FMOD) assign_soundlib(soundlib_fmod); #elif defined(SND_LIB_WINDSOUND) @@ -451,6 +473,7 @@ attempt_restore: RESTORE_WARNING_UNREACHABLE_CODE +#if 0 static void early_options(int argc, char *argv[]) { @@ -671,6 +694,7 @@ nhusage(void) (void) printf("%s\n", buf1); #undef ADD_USAGE } +#endif /* 0 */ /* copy file if destination does not exist */ void @@ -1297,4 +1321,23 @@ other_self_recover_prompt(void) } return retval; } + +#ifdef CHDIR +void +chdirx(const char *dir, boolean wr) +{ + static char thisdir[] = "."; + + if (dir && chdir(dir) < 0) { + error("Cannot chdir to %s.", dir); + } + + /* warn the player if we can't write the record file */ + /* perhaps we should also test whether . is writable */ + /* unfortunately the access system-call is worthless */ + if (wr) + check_recordfile(dir ? dir : thisdir); +} +#endif /* CHDIR */ + /*windmain.c*/ From acb85b18cfbee62f0f804853cfd4d9c9655a1c4e Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 5 Apr 2026 11:36:19 -0400 Subject: [PATCH 394/442] Change Windows startup remove the safeproc pseudo-windowport routines from almost a decade ago. A very early pass is made through the config file, seeking out just the interface-related OPTIONS=windowport and OPTIONS=soundlib and ignoring all other options in the config file during that early pass, so the windowport can be activated without the NetHack core initialization in place that some of the other rcfile OPTIONS require. Bundles the existing rcfile processing code into rcfile(). New functions to control which rcfile options will be disregarded in the early config file pass, and which will be processed: set_all_options_disregarded(); set_all_options_heeded(); disregard_this_option(opt_xx); heed_this_option(opt_xx); Windows calls rcfile_interface_options(), which is a bundling of a series of function calls to achieve the desired result. void rcfile_interface_options(void) { allopt_array_init(); set_all_options_disregarded(); heed_this_option(opt_windowtype); heed_this_option(opt_soundlib); rcfile(); set_all_options_heeded(); disregard_this_option(opt_windowtype); disregard_this_option(opt_soundlib); } --- include/decl.h | 3 +- include/extern.h | 7 + include/global.h | 12 +- include/hack.h | 16 - include/optlist.h | 45 +- include/windconf.h | 2 + include/winprocs.h | 75 --- src/cfgfiles.c | 79 +++ src/decl.c | 2 +- src/options.c | 279 ++++++----- sys/windows/GNUmakefile | 10 +- sys/windows/Makefile.nmake | 12 +- sys/windows/consoletty.c | 32 +- sys/windows/vs/NetHack/NetHack.vcxproj | 3 +- sys/windows/vs/NetHackW/NetHackW.vcxproj | 3 +- sys/windows/vs/sfctool/sfctool.vcxproj | 14 +- sys/windows/vs/sftags/sftags.vcxproj | 14 +- sys/windows/windmain.c | 149 +++--- sys/windows/windsys.c | 18 - win/share/safeproc.c | 596 ----------------------- win/tty/wintty.c | 5 +- win/win32/NetHackW.c | 30 -- win/win32/mswproc.c | 3 - 23 files changed, 450 insertions(+), 959 deletions(-) delete mode 100644 win/share/safeproc.c diff --git a/include/decl.h b/include/decl.h index a34550ef9..f66c0f5a8 100644 --- a/include/decl.h +++ b/include/decl.h @@ -725,7 +725,8 @@ struct instance_globals_o { /* options.c */ - int opt_phase; /* builtin_opt, syscf_, rc_file_, environ_, play_opt */ + /* builtin_opt, syscf_, rc_file_, environ_, play_opt */ + enum option_phases opt_phase; boolean opt_initial; boolean opt_from_file; boolean opt_need_redraw; /* for doset() */ diff --git a/include/extern.h b/include/extern.h index 8ed1b0f0f..b3206b015 100644 --- a/include/extern.h +++ b/include/extern.h @@ -335,6 +335,8 @@ extern boolean parse_conf_file(FILE *fp, boolean (*proc)(char *arg)); extern void set_configfile_name(const char *); extern char *get_configfile(void); extern const char *get_default_configfile(void); +extern void rcfile(void); +extern void rcfile_interface_options(void); /* ### coloratt.c ### */ @@ -2320,6 +2322,10 @@ extern int msgtype_type(const char *, boolean) NONNULLARG1; extern void hide_unhide_msgtypes(boolean, int); extern void msgtype_free(void); extern void options_free_window_colors(void); +extern void set_all_options_heeded(void); +extern void set_all_options_disregarded(void); +extern void heed_this_option(enum opt); +extern void disregard_this_option(enum opt); #ifdef TTY_PERM_INVENT extern void check_perm_invent_again(void); #endif @@ -2345,6 +2351,7 @@ extern int dowhatdoes(void); extern char *dowhatdoes_core(char, char *) NONNULLARG2; /*might return NULL*/ extern int dohelp(void); extern int dohistory(void); +void allopt_array_init(void); /* ### xxmain.c ### */ diff --git a/include/global.h b/include/global.h index 21b00fe46..1439044f1 100644 --- a/include/global.h +++ b/include/global.h @@ -102,8 +102,6 @@ typedef unsigned readLenType; #endif #define BOOL_RANDOM (-1) -enum optchoice { opt_in, opt_out}; - /* * type nhsym: loadable symbols go into this type */ @@ -153,6 +151,16 @@ typedef uchar nhsym; #endif /* __GNUC__ || _MSC_VER */ #endif /* !__clang__ */ +#define SET__IS_VALUE_VALID(s) ((s < set_in_sysconf) || (s > set_wiznofuz)) +#include "optlist.h" +enum opt { + opt_prefix_only = -1, +#define NHOPT_ENUM +#include "optlist.h" +#undef NHOPT_ENUM + OPTCOUNT +}; + /* * Automatic inclusions for the subsidiary files. * Please don't change the order. It does matter. diff --git a/include/hack.h b/include/hack.h index bb9f9d642..f655a8961 100644 --- a/include/hack.h +++ b/include/hack.h @@ -702,22 +702,6 @@ enum nhcb_calls { NUM_NHCB }; -/* - * option setting restrictions - */ - -enum optset_restrictions { - set_in_sysconf = 0, /* system config file option only */ - set_in_config = 1, /* config file option only */ - set_viaprog = 2, /* may be set via extern program, not seen in game */ - set_gameview = 3, /* may be set via extern program, displayed in game */ - set_in_game = 4, /* may be set via extern program or set in the game */ - set_wizonly = 5, /* may be set in the game if wizmode */ - set_wiznofuz = 6, /* wizard-mode only, but not by fuzzer */ - set_hidden = 7 /* placeholder for prefixed entries, never show it */ -}; -#define SET__IS_VALUE_VALID(s) ((s < set_in_sysconf) || (s > set_wiznofuz)) - struct plinemsg_type { xint16 msgtype; /* one of MSGTYP_foo */ struct nhregex *regex; diff --git a/include/optlist.h b/include/optlist.h index b920048ac..9322bee7e 100644 --- a/include/optlist.h +++ b/include/optlist.h @@ -4,10 +4,6 @@ #ifndef OPTLIST_H #define OPTLIST_H -#ifdef OPTIONS_C -static int optfn_boolean(int, int, boolean, char *, char *); -#endif - /* * NOTE: If you add (or delete) an option, please review: * doc/options.txt @@ -16,6 +12,37 @@ static int optfn_boolean(int, int, boolean, char *, char *); * updates that should accompany your change. */ +#define BACKWARD_COMPAT + +extern int optfn_boolean(int, int, boolean, char *, char *); +enum optchoice { opt_in, opt_out }; + +/* + * option setting restrictions + */ +enum optset_restrictions { + set_in_sysconf = 0, /* system config file option only */ + set_in_config = 1, /* config file option only */ + set_viaprog = 2, /* may be set via extern program, not seen in game */ + set_gameview = 3, /* may be set via extern program, displayed in game */ + set_in_game = 4, /* may be set via extern program or set in the game */ + set_wizonly = 5, /* may be set in the game if wizmode */ + set_wiznofuz = 6, /* wizard-mode only, but not by fuzzer */ + set_hidden = 7 /* placeholder for prefixed entries, never show it */ +}; + +/* these aren't the same as set_xxx */ +enum option_phases { + phase_not_set = 0, + builtin_opt = 1, /* compiled-in default value of an option */ + syscf_opt, /* sysconf setting of an option, overrides builtin */ + rc_file_opt, /* player's run-time config file setting, overrides syscf */ + environ_opt, /* player's environment NETHACKOPTIONS, overrides rc_file */ + cmdline_opt, /* program invocation command-line, overrides environ */ + play_opt, /* 'O' command, interactively set so overrides all */ + num_opt_phases +}; + enum OptType { BoolOpt, CompOpt, OthrOpt }; enum Y_N { No, Yes }; enum Off_On { Off, On }; @@ -45,7 +72,7 @@ struct allopt_t { const char *alias; const char *descr; const char *prefixgw; - boolean initval, has_handler, dupdetected; + boolean initval, has_handler, dupdetected, disregarded; }; #endif /* OPTLIST_H */ @@ -74,16 +101,16 @@ static int optfn_##a(int, int, boolean, char *, char *); #elif defined(NHOPT_PARSE) #define NHOPTB(a, sec, b, c, s, i, n, v, d, al, bp, termp, desc) \ { #a, OptS_##sec, 0, b, opt_##a, s, BoolOpt, n, v, d, No, termp, c, \ - bp, &optfn_boolean, al, desc, (const char *) 0, i, 0, 0 }, + bp, &optfn_boolean, al, desc, (const char *) 0, i, 0, 0 , 0 }, #define NHOPTC(a, sec, b, c, s, n, v, d, h, al, z) \ { #a, OptS_##sec, 0, b, opt_##a, s, CompOpt, n, v, d, No, 0, c, \ - (boolean *) 0, &optfn_##a, al, z, (const char *) 0, Off, h, 0 }, + (boolean *) 0, &optfn_##a, al, z, (const char *) 0, Off, h, 0, 0 }, #define NHOPTP(a, sec, b, c, s, n, v, d, h, al, z) \ { #a, OptS_##sec, 0, b, pfx_##a, s, CompOpt, n, v, d, Yes, 0, c, \ - (boolean *) 0, &pfxfn_##a, al, z, #a, Off, h, 0 }, + (boolean *) 0, &pfxfn_##a, al, z, #a, Off, h, 0, 0 }, #define NHOPTO(m, sec, a, b, c, s, n, v, d, al, z) \ { m, OptS_##sec, 0, b, opt_##a, s, OthrOpt, n, v, d, No, 0, c, \ - (boolean *) 0, &optfn_##a, al, z, (const char *) 0, On, On, 0 }, + (boolean *) 0, &optfn_##a, al, z, (const char *) 0, On, On, 0, 0 }, /* this is not reliable because TILES_IN_GLYPHMAP might be defined * in a multi-interface binary but not apply to the current interface */ diff --git a/include/windconf.h b/include/windconf.h index bd30f8a6b..b7330668c 100644 --- a/include/windconf.h +++ b/include/windconf.h @@ -32,6 +32,8 @@ #define OPTIONS_AT_RUNTIME /* build info done at runtime not text file */ +#define EARLY_CONFIGFILE_PASS + /* * ----------------------------------------------------------------- * The remaining code shouldn't need modification. diff --git a/include/winprocs.h b/include/winprocs.h index 688d76138..014ee1e09 100644 --- a/include/winprocs.h +++ b/include/winprocs.h @@ -415,79 +415,4 @@ struct chain_procs { }; #endif /* WINCHAIN */ -#ifdef SAFEPROCS -/* - * window port routines available in sys/share/safeproc.c - */ -extern struct window_procs *get_safe_procs(int); -extern void safe_init_nhwindows(int *, char **); -extern void safe_player_selection(void); -extern void safe_askname(void); -extern void safe_get_nh_event(void); -extern void safe_exit_nhwindows(const char *); -extern void safe_suspend_nhwindows(const char *); -extern void safe_resume_nhwindows(void); -extern winid safe_create_nhwindow(int); -extern void safe_clear_nhwindow(winid); -extern void safe_display_nhwindow(winid, boolean); -extern void safe_destroy_nhwindow(winid); -extern void safe_curs(winid, int, int); -extern void safe_putstr(winid, int, const char *); -extern void safe_putmixed(winid, int, const char *); -extern void safe_display_file(const char *, boolean); -extern void safe_start_menu(winid, unsigned long); -extern void safe_add_menu(winid, const glyph_info *, const ANY_P *, - char, char, int, int, const char *, - unsigned int); -extern void safe_end_menu(winid, const char *); -extern int safe_select_menu(winid, int, MENU_ITEM_P **); -extern char safe_message_menu(char, int, const char *); -extern void safe_mark_synch(void); -extern void safe_wait_synch(void); -#ifdef CLIPPING -extern void safe_cliparound(int, int); -#endif -#ifdef POSITIONBAR -extern void safe_update_positionbar(char *); -#endif -extern void safe_print_glyph(winid, coordxy, coordxy, - const glyph_info *, const glyph_info *); -extern void safe_raw_print(const char *); -extern void safe_raw_print_bold(const char *); -extern int safe_nhgetch(void); -extern int safe_nh_poskey(coordxy *, coordxy *, int *); -extern void safe_nhbell(void); -extern int safe_doprev_message(void); -extern char safe_yn_function(const char *, const char *, char); -extern void safe_getlin(const char *, char *); -extern int safe_get_ext_cmd(void); -extern void safe_number_pad(int); -extern void safe_delay_output(void); -#ifdef CHANGE_COLOR -extern void safe_change_color(int, long, int); -#ifdef MAC -extern void safe_change_background(int); -extern short safe_set_font_name(winid, char *); -#endif -extern char *safe_get_color_string(void); -#endif -extern void safe_outrip(winid, int, time_t); -extern void safe_preference_update(const char *); -extern char *safe_getmsghistory(boolean); -extern void safe_putmsghistory(const char *, boolean); -extern void safe_status_init(void); -extern void safe_status_finish(void); -extern void safe_status_enablefield(int, const char *, const char *, - boolean); -extern void safe_status_update(int, genericptr_t, int, int, int, - unsigned long *); -extern boolean safe_can_suspend(void); -extern void stdio_raw_print(const char *); -extern void stdio_nonl_raw_print(const char *); -extern void stdio_raw_print_bold(const char *); -extern void stdio_wait_synch(void); -extern void safe_update_inventory(int); -extern win_request_info *safe_ctrl_nhwindow(winid, int, win_request_info *); -extern int stdio_nhgetch(void); -#endif /* SAFEPROCS */ #endif /* WINPROCS_H */ diff --git a/src/cfgfiles.c b/src/cfgfiles.c index 1b4715a4f..439747127 100644 --- a/src/cfgfiles.c +++ b/src/cfgfiles.c @@ -1881,6 +1881,85 @@ vconfig_error_add(const char *str, va_list the_args) config_erradd(buf); } +void +rcfile(void) +{ + char *opts = 0, *xtraopts = 0; + const char *envname, *namesrc, *nameval; + + go.opt_phase = environ_opt; + /* getenv() instead of nhgetenv(): let total length of options be long; + parseoptions() will check each individually */ + envname = "NETHACKOPTIONS"; + opts = getenv(envname); + if (!opts) { + /* fall back to original name; discouraged */ + envname = "HACKOPTIONS"; + opts = getenv(envname); + } + + if (gc.cmdline_rcfile) { + namesrc = "command line"; + nameval = gc.cmdline_rcfile; + xtraopts = opts; + if (opts && (*opts == '/' || *opts == '\\' || *opts == '@')) + xtraopts = 0; /* NETHACKOPTIONS is a file name; ignore it */ + } else if (opts && (*opts == '/' || *opts == '\\' || *opts == '@')) { + /* NETHACKOPTIONS is a file name; use that instead of the default */ + if (*opts == '@') + ++opts; /* @filename */ + namesrc = envname; + nameval = opts; + xtraopts = 0; + } else { + /* either no NETHACKOPTIONS or it wasn't a file name; + read the default configuration file */ + nameval = namesrc = 0; + xtraopts = opts; + } + + go.opt_phase = rc_file_opt; + /* seemingly arbitrary name length restriction is to prevent error + messages, if any were to be delivered while accessing the file, + from potentially overflowing buffers */ + if (nameval && (int) strlen(nameval) >= BUFSZ / 2) { + config_error_init(TRUE, namesrc, FALSE); + config_error_add( + "nethackrc file name \"%.40s\"... too long; using default", + nameval); + config_error_done(); + nameval = namesrc = 0; /* revert to default nethackrc */ + } + + config_error_init(TRUE, nameval, nameval ? CONFIG_ERROR_SECURE : FALSE); + (void) read_config_file(nameval, set_in_config); + config_error_done(); + if (xtraopts) { + /* NETHACKOPTIONS is present and not a file name */ + go.opt_phase = environ_opt; + config_error_init(FALSE, envname, FALSE); + (void) parseoptions(xtraopts, TRUE, FALSE); + config_error_done(); + } + + if (gc.cmdline_rcfile) + free((genericptr_t) gc.cmdline_rcfile), gc.cmdline_rcfile = 0; + /*[end of nethackrc handling]*/ +} + +void +rcfile_interface_options(void) +{ + allopt_array_init(); + set_all_options_disregarded(); + heed_this_option(opt_windowtype); + heed_this_option(opt_soundlib); + rcfile(); + set_all_options_heeded(); + disregard_this_option(opt_windowtype); + disregard_this_option(opt_soundlib); +} + #ifdef SYSCF #ifdef SYSCF_FILE void diff --git a/src/decl.c b/src/decl.c index cc1e8a3b6..f229de737 100644 --- a/src/decl.c +++ b/src/decl.c @@ -592,7 +592,7 @@ static const struct instance_globals_o g_init_o = { /* o_init.c */ DUMMY, /* oclass_prob_totals */ /* options.c */ - 0, /* opt_phase */ + phase_not_set, /* opt_phase */ FALSE, /* opt_initial */ FALSE, /* opt_from_file */ FALSE, /* opt_need_redraw */ diff --git a/src/options.c b/src/options.c index c22105c03..8bfe77d97 100644 --- a/src/options.c +++ b/src/options.c @@ -56,21 +56,13 @@ NEARDATA struct accessibility_data a11y; #include "optlist.h" #undef NHOPT_PROTO -#define NHOPT_ENUM -enum opt { - opt_prefix_only = -1, -#include "optlist.h" - OPTCOUNT -}; -#undef NHOPT_ENUM - #define NHOPT_PARSE static struct allopt_t allopt_init[] = { #include "optlist.h" {(const char *) 0, OptS_Advanced, 0, 0, 0, set_in_sysconf, BoolOpt, No, No, No, No, Term_False, 0, (boolean *) 0, (int (*)(int, int, boolean, char *, char *)) 0, - (char *) 0, (const char *) 0, (const char *) 0, 0, 0, 0 } + (char *) 0, (const char *) 0, (const char *) 0, 0, 0, 0, TRUE } }; #undef NHOPT_PARSE @@ -94,16 +86,6 @@ enum optn_result { enum requests { do_nothing, do_init, do_set, do_handler, get_val, get_cnf_val }; -/* these aren't the same as set_xxx in optlist.h */ -enum option_phases { - builtin_opt=1,/* compiled-in default value of an option */ - syscf_opt, /* sysconf setting of an option, overrides builtin */ - rc_file_opt, /* player's run-time config file setting, overrides syscf */ - environ_opt, /* player's environment NETHACKOPTIONS, overrides rc_file */ - cmdline_opt, /* program invocation command-line, overrides environ */ - play_opt, /* 'O' command, interactively set so overrides all */ - num_opt_phases -}; static struct allopt_t allopt[SIZE(allopt_init)]; @@ -124,6 +106,7 @@ extern char ttycolors[CLR_MAX]; /* in sys/msdos/video.c */ static char empty_optstr[] = { '\0' }; static boolean duplicate, using_alias; static boolean give_opt_msg = TRUE; +static boolean restricted_options_mode = FALSE; enum { MAX_ROLEOPT = 4 }; /* 4: role,race,gend,algn */ static boolean opt_set_in_config[OPTCOUNT]; @@ -357,6 +340,7 @@ static const menu_cmd_t default_menu_cmd_info[] = { static const char n_currently_set[] = "(%d currently set)"; +staticfn void allopt_array_init(void); staticfn void nmcpy(char *, const char *, int); staticfn void escapes(const char *, char *); staticfn void rejectoption(const char *); @@ -595,7 +579,7 @@ parseoptions( * placed that number into each option's allopt[n].minmatch. * */ - if (!got_match) + if (!got_match && allopt[i].name) got_match = match_optname(opts, allopt[i].name, allopt[i].minmatch, TRUE); if (got_match) { @@ -634,7 +618,8 @@ parseoptions( /* allow optfn's to test whether they were called from parseoptions() */ program_state.in_parseoptions++; - if (got_match && matchidx >= 0) { + if (got_match && (matchidx >= 0 && matchidx < OPTCOUNT) + && !allopt[matchidx].disregarded) { duplicate = duplicate_opt_detection(matchidx); if (duplicate && !allopt[matchidx].dupeok) complain_about_duplicate(matchidx); @@ -684,7 +669,7 @@ parseoptions( } } - if (optresult == optn_silenterr) + if (optresult == optn_silenterr || restricted_options_mode) return FALSE; if (pfx_match && optresult == optn_err) { char pfxbuf[BUFSZ], *pfxp; @@ -4971,17 +4956,20 @@ optfn_windowtype( * _end_ because comma-separated option strings are processed from * right to left. */ - if (iflags.windowtype_locked) - return optn_ok; + if (!iflags.window_inited) { + if (iflags.windowtype_locked) + return optn_ok; - if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE)) - != empty_optstr) { - nmcpy(gc.chosen_windowtype, op, WINTYPELEN); - if (!iflags.windowtype_deferred) { - choose_windows(gc.chosen_windowtype); + if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE)) + != empty_optstr) { + nmcpy(gc.chosen_windowtype, op, WINTYPELEN); + if (!iflags.windowtype_deferred) { + choose_windows(gc.chosen_windowtype); + } + } else { + return optn_err; } - } else - return optn_err; + } return optn_ok; } if (req == get_val || req == get_cnf_val) { @@ -7066,29 +7054,14 @@ txt2key(char *txt) void initoptions(void) { - int i; - /* * Most places that call initoptions_init()/initoptions() would * have the calls next to each other, so instead of adding * initoptions_init() everywhere, just add it where it's needed in * a non-adjacent place and call it here for all the other cases. */ - if(go.opt_phase != builtin_opt) + if (go.opt_phase != builtin_opt) initoptions_init(); - - /* - * Call each option function with an init flag and give it a chance - * to make any preparations that it might require. We do this - * whether or not the option itself is ever specified; that's - * irrelevant for the init call. Doing this allows the prep code for - * option settings to remain adjacent to, and in the same function as, - * the code that processes those options. - */ - for (i = 0; i < OPTCOUNT; ++i) { - if (allopt[i].optfn) - (*allopt[i].optfn)(i, do_init, FALSE, empty_optstr, empty_optstr); - } #ifdef SYSCF /* someday there may be other SYSCF alternatives besides text file */ #ifdef SYSCF_FILE @@ -7130,9 +7103,7 @@ initoptions_init(void) go.opt_phase = builtin_opt; /* Did I need to move this here? */ /* initialize the function pointers for saving the game */ sf_init(); - memcpy(allopt, allopt_init, sizeof(allopt)); - determine_ambiguities(); - + allopt_array_init(); /* if windowtype has been specified on the command line, set it up early so windowtype-specific options use it as their base */ if (gc.cmdline_windowsys) { @@ -7286,6 +7257,28 @@ initoptions_init(void) /* since this is done before init_objects(), do partial init here */ objects[SLIME_MOLD].oc_name_idx = SLIME_MOLD; nmcpy(svp.pl_fruit, OBJ_NAME(objects[SLIME_MOLD]), PL_FSIZ); + +#ifdef SYSCF +/* someday there may be other SYSCF alternatives besides text file */ +#ifdef SYSCF_FILE + /* If SYSCF_FILE is specified, it _must_ exist... */ + assure_syscf_file(); + config_error_init(TRUE, SYSCF_FILE, FALSE); + + /* ... and _must_ parse correctly. */ + go.opt_phase = syscf_opt; + if (!read_config_file(SYSCF_FILE, set_in_sysconf)) { + if (config_error_done() && !iflags.initoptions_noterminate) + nh_terminate(EXIT_FAILURE); + } + config_error_done(); + /* + * TODO [maybe]: parse the sysopt entries which are space-separated + * lists of usernames into arrays with one name per element. + */ +#endif +#endif /* SYSCF */ + initoptions_finish(); } /* @@ -7306,72 +7299,69 @@ initoptions_init(void) */ void initoptions_finish(void) -{ - nhsym sym = 0; - char *opts = 0, *xtraopts = 0; -#ifndef MAC - const char *envname, *namesrc, *nameval; +{ nhsym sym = 0; - /* getenv() instead of nhgetenv(): let total length of options be long; - parseoptions() will check each individually */ - envname = "NETHACKOPTIONS"; - opts = getenv(envname); - if (!opts) { - /* fall back to original name; discouraged */ - envname = "HACKOPTIONS"; - opts = getenv(envname); + rcfile(); + + (void) fruitadd(svp.pl_fruit, (struct fruit *) 0); + /* + * Remove "slime mold" from list of object names. This will + * prevent it from being wished unless it's actually present + * as a named (or default) fruit. Wishing for "fruit" will + * result in the player's preferred fruit. [Once upon a time + * the override value used was "\033" which prevented wishing + * for the slime mold object at all except by asking for a + * specific named fruit.] Note that there are multiple fruit + * object types (apple, melon, &c) but the "fruit" object is + * slime mold or whatever custom name player assigns to that. + */ + obj_descr[SLIME_MOLD].oc_name = "fruit"; + + sym = get_othersym(SYM_BOULDER, + Is_rogue_level(&u.uz) ? ROGUESET : PRIMARYSET); + if (sym) + gs.showsyms[SYM_BOULDER + SYM_OFF_X] = sym; + reglyph_darkroom(); + reset_glyphmap(gm_optionchange); +#ifdef STATUS_HILITES + /* + * A multi-interface binary might only support status highlighting + * for some of the interfaces; check whether we asked for it but are + * using one which doesn't. + * + * Option processing can take place before a user-decided WindowPort + * is even initialized, so check for that too. + */ + if (!WINDOWPORT(safestartup)) { + if (iflags.hilite_delta && !wc2_supported("statushilites")) { + raw_printf("Status highlighting not supported for %s interface.", + windowprocs.name); + iflags.hilite_delta = 0; + } } +#endif + update_rest_on_space(); - if (gc.cmdline_rcfile) { - namesrc = "command line"; - nameval = gc.cmdline_rcfile; - xtraopts = opts; - if (opts && (*opts == '/' || *opts == '\\' || *opts == '@')) - xtraopts = 0; /* NETHACKOPTIONS is a file name; ignore it */ - } else if (opts && (*opts == '/' || *opts == '\\' || *opts == '@')) { - /* NETHACKOPTIONS is a file name; use that instead of the default */ - if (*opts == '@') - ++opts; /* @filename */ - namesrc = envname; - nameval = opts; - xtraopts = 0; - } else -#endif /* !MAC */ - /*else*/ { - /* either no NETHACKOPTIONS or it wasn't a file name; - read the default configuration file */ - nameval = namesrc = 0; - xtraopts = opts; - } + /* these can't rely on compile-time initialization for their defaults + because a multi-interface binary might need different values for + different interfaces; if neither tiled_map nor ascii_map pass the + wc_supported() test, assume ascii_map */ + if (iflags.wc_tiled_map && !wc_supported("tiled_map")) + iflags.wc_tiled_map = FALSE, iflags.wc_ascii_map = TRUE; + else if (iflags.wc_ascii_map && !wc_supported("ascii_map") + && wc_supported("tiled_map")) + iflags.wc_ascii_map = FALSE, iflags.wc_tiled_map = TRUE; - /* seemingly arbitrary name length restriction is to prevent error - messages, if any were to be delivered while accessing the file, - from potentially overflowing buffers */ - if (nameval && (int) strlen(nameval) >= BUFSZ / 2) { - go.opt_phase = rc_file_opt; - config_error_init(TRUE, namesrc, FALSE); - config_error_add( - "nethackrc file name \"%.40s\"... too long; using default", - nameval); - config_error_done(); - nameval = namesrc = 0; /* revert to default nethackrc */ - } - - config_error_init(TRUE, nameval, nameval ? CONFIG_ERROR_SECURE : FALSE); - (void) read_config_file(nameval, set_in_config); - config_error_done(); - if (xtraopts) { - /* NETHACKOPTIONS is present and not a file name */ - go.opt_phase = environ_opt; - config_error_init(FALSE, envname, FALSE); - (void) parseoptions(xtraopts, TRUE, FALSE); - config_error_done(); - } - - if (gc.cmdline_rcfile) - free((genericptr_t) gc.cmdline_rcfile), gc.cmdline_rcfile = 0; - /*[end of nethackrc handling]*/ +#ifdef ENHANCED_SYMBOLS + if (glyphid_cache_status()) + free_glyphid_cache(); + apply_customizations(gc.currentgraphics, do_custom_symbols); +#endif + go.opt_initial = FALSE; + return; +} +#if 0 (void) fruitadd(svp.pl_fruit, (struct fruit *) 0); /* * Remove "slime mold" from list of object names. This will @@ -7449,6 +7439,37 @@ initoptions_finish(void) } return; } +#endif +void +allopt_array_init(void) +{ + int i; + static boolean options_array_inited_already = FALSE; + + if (!options_array_inited_already) { + memcpy(allopt, allopt_init, sizeof(allopt)); + determine_ambiguities(); + for (i = 0; allopt[i].name; i++) { + if (allopt[i].addr) + *(allopt[i].addr) = allopt[i].initval; + } + set_all_options_heeded(); + /* + * Call each option function with an init flag and give it a chance + * to make any preparations that it might require. We do this + * whether or not the option itself is ever specified; that's + * irrelevant for the init call. Doing this allows the prep code for + * option settings to remain adjacent to, and in the same function as, + * the code that processes those options. + */ + for (i = 0; i < OPTCOUNT; ++i) { + if (allopt[i].optfn) + (*allopt[i].optfn)(i, do_init, FALSE, empty_optstr, + empty_optstr); + } + options_array_inited_already = TRUE; + } +} /* ******************************************* @@ -8966,6 +8987,7 @@ doset(void) /* changing options via menu by Per Liboriussen */ if (opt_indx < -1) opt_indx++; /* -1 offset for select_menu() */ opt_indx -= indexoffset; + assert(IndexOk(opt_indx, allopt)); if (allopt[opt_indx].opttyp == BoolOpt) { /* boolean option */ Sprintf(buf, "%s%s", *allopt[opt_indx].addr ? "!" : "", @@ -10220,6 +10242,41 @@ enhance_menu_text( return; } +void +set_all_options_heeded(void) +{ + int i; + + for (i = 0; i < OPTCOUNT; i++) + allopt[i].disregarded = FALSE; + restricted_options_mode = FALSE; +} + +void +set_all_options_disregarded(void) +{ + int i; + + for (i = 0; i < OPTCOUNT ; i++) + allopt[i].disregarded = TRUE; + restricted_options_mode = TRUE; +} + +void +heed_this_option(enum opt optidx) +{ + if (optidx >= 0 && optidx < (enum opt) OPTCOUNT) + allopt[optidx].disregarded = FALSE; +} +void +disregard_this_option(enum opt optidx) +{ + if (optidx >= 0 && optidx < (enum opt) OPTCOUNT) + allopt[optidx].disregarded = TRUE; + if (!restricted_options_mode) + restricted_options_mode = TRUE; +} + #undef OPTIONS_HEADING #undef CONFIG_SLOT diff --git a/sys/windows/GNUmakefile b/sys/windows/GNUmakefile index 3ea5c777b..4bc3df970 100644 --- a/sys/windows/GNUmakefile +++ b/sys/windows/GNUmakefile @@ -1239,14 +1239,14 @@ COREOBJS = $(addsuffix .o, allmain alloc apply artifact attrib ball bones botl \ nhlobj nhlsel nhlua windsound o_init objects objnam options \ pager pickup pline polyself potion pray priest quest questpgr \ random read rect region report restore rip rnd role rumors \ - safeproc save sfbase sfstruct shk shknam sit selvar sounds sp_lev \ + save sfbase sfstruct shk shknam sit selvar sounds sp_lev \ spell stairs steal steed strutil \ symbols sys teleport timeout topten track trap u_init uhitm utf8map \ vault version vision weapon were wield windmain windows windsys wizard \ wizcmds worm worn write zap $(SOUNDLIBOBJS)) -CFLAGSW = $(CFLAGS) $(CFLAGSXTRA) $(SOUNDLIBINCL) $(COMMONDEF) $(DLBFLG) -DTILES -D_WINDOWS -DMSWIN_GRAPHICS -DSAFEPROCS -DNOTTYGRAPHICS $(SOUNDLIBDEFS) -CPPFLAGSW = $(CFLAGS) $(CPPFLAGSXTRA) $(COMMONDEF) $(DLBFLG) -DTILES -D_WINDOWS -DMSWIN_GRAPHICS -DSAFEPROCS -DNOTTYGRAPHICS $(SOUNDLIBDEFS) +CFLAGSW = $(CFLAGS) $(CFLAGSXTRA) $(SOUNDLIBINCL) $(COMMONDEF) $(DLBFLG) -DTILES -D_WINDOWS -DMSWIN_GRAPHICS -DNOTTYGRAPHICS $(SOUNDLIBDEFS) +CPPFLAGSW = $(CFLAGS) $(CPPFLAGSXTRA) $(COMMONDEF) $(DLBFLG) -DTILES -D_WINDOWS -DMSWIN_GRAPHICS -DNOTTYGRAPHICS $(SOUNDLIBDEFS) ONHW = $(O)nethackw NHWONLY = $(addsuffix .o, mhaskyn mhdlg mhfont mhinput mhmain mhmap mhmenu \ @@ -1348,8 +1348,8 @@ CLEAN_FILE += $(NHWTARGETS) $(NHWOBJS) $(NHWRES) $(BMPS) $(WAV) #========================================== # nethack #========================================== -CFLAGSNH = $(CFLAGSU) $(CFLAGSXTRA) $(SOUNDLIBINCL) -DNO_TILE_C -DSAFEPROCS -D_LIB -DWIN32CON $(SOUNDLIBDEFS) -CPPFLAGSNH = $(CFLAGSU) $(CPPFLAGSXTRA) -DNO_TILE_C -DSAFEPROCS -D_LIB -DWIN32CON $(SOUNDLIBDEFS) +CFLAGSNH = $(CFLAGSU) $(CFLAGSXTRA) $(SOUNDLIBINCL) -DNO_TILE_C -D_LIB -DWIN32CON $(SOUNDLIBDEFS) +CPPFLAGSNH = $(CFLAGSU) $(CPPFLAGSXTRA) -DNO_TILE_C -D_LIB -DWIN32CON $(SOUNDLIBDEFS) ONH = $(O)nethack diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index 2097d7183..fe481ecc2 100644 --- a/sys/windows/Makefile.nmake +++ b/sys/windows/Makefile.nmake @@ -847,8 +847,7 @@ REGEXTTY = $(OTTY)cppregex.o LUAOBJTTY = $(OTTY)nhlua.o $(OTTY)nhlsel.o $(OTTY)nhlobj.o VVOBJTTY = $(OTTY)version.o -SOBJTTY = $(OTTY)windmain.o $(OTTY)windsys.o $(OTTY)win10.o \ - $(OTTY)safeproc.o +SOBJTTY = $(OTTY)windmain.o $(OTTY)windsys.o $(OTTY)win10.o TTYOBJTTY = $(OTTY)topl.o $(OTTY)getline.o $(OTTY)wintty.o @@ -908,8 +907,7 @@ REGEXGUI = $(OGUI)cppregex.o LUAOBJGUI = $(OGUI)nhlua.o $(OGUI)nhlsel.o $(OGUI)nhlobj.o VVOBJGUI = $(OGUI)version.o -SOBJGUI = $(OGUI)windmain.o $(OGUI)windsys.o $(OGUI)win10.o \ - $(OGUI)safeproc.o +SOBJGUI = $(OGUI)windmain.o $(OGUI)windsys.o $(OGUI)win10.o GUIOBJ = $(OGUI)mhaskyn.o $(OGUI)mhdlg.o \ $(OGUI)mhfont.o $(OGUI)mhinput.o $(OGUI)mhmain.o $(OGUI)mhmap.o \ @@ -1364,8 +1362,8 @@ INCLUSIONS= /I$(R_INCL) /I$(R_MSWSYS) $(R_LUAINCL) $(R_SOUNDLIBINCL) # Util and console builds #========================================== -CFLAGS = $(ctmpflags) $(INCLUSIONS) $(DLBDEF) -DSAFEPROCS $(SOUNDLIBDEFS) -CPPFLAGS = $(cpptmpflags) $(INCLUSIONS) $(DLBDEF) -DSAFEPROCS $(SOUNDLIBDEFS) +CFLAGS = $(ctmpflags) $(INCLUSIONS) $(DLBDEF) $(SOUNDLIBDEFS) +CPPFLAGS = $(cpptmpflags) $(INCLUSIONS) $(DLBDEF) $(SOUNDLIBDEFS) LFLAGS = $(lflags) $(conlibs) $(MACHINE) #========================================== @@ -2390,14 +2388,12 @@ $(OTTY)windsys.o: $(MSWSYS)windsys.c $(WINDHDR) $(HACK_H) $(OTTY)windsound.o: $(WINDSOUNDDIR)windsound.c $(HACK_H) #$(OTTY)sample.o: $(SOUNDDIR)sample.c $(HACK_H) $(OTTY)windmain.o: $(MSWSYS)windmain.c $(WINDHDR) $(HACK_H) -$(OTTY)safeproc.o: $(WSHR)safeproc.c $(WINDHDR) $(HACK_H) $(OGUI)consoletty.o: $(MSWSYS)consoletty.c $(WINDHDR) $(HACK_H) $(TILE_H) $(OGUI)win10.o: $(MSWSYS)win10.c $(WINDHDR) $(HACK_H) $(OGUI)windsys.o: $(MSWSYS)windsys.c $(WINDHDR) $(HACK_H) $(OGUI)windsound.o: $(WINDSOUNDDIR)windsound.c $(HACK_H) $(OGUI)windmain.o: $(MSWSYS)windmain.c $(WINDHDR) $(HACK_H) -$(OGUI)safeproc.o: $(WSHR)safeproc.c $(WINDHDR) $(HACK_H) #=================================================================== # win/win32 dependencies diff --git a/sys/windows/consoletty.c b/sys/windows/consoletty.c index 34489430f..bceb98132 100644 --- a/sys/windows/consoletty.c +++ b/sys/windows/consoletty.c @@ -156,7 +156,6 @@ static boolean check_font_widths(void); #endif static void set_known_good_console_font(void); static void restore_original_console_font(void); -extern void safe_routines(void); void tty_ibmgraphics_fixup(void); #ifdef VIRTUAL_TERMINAL_SEQUENCES extern void (*ibmgraphics_mode_callback)(void); /* symbols.c */ @@ -1189,8 +1188,6 @@ consoletty_open(int mode UNUSED) void consoletty_exit(void) { - /* go back to using the safe routines */ - safe_routines(); free_custom_colors(); free((genericptr_t) console.front_buffer); free((genericptr_t) console.back_buffer); @@ -2546,9 +2543,13 @@ void early_raw_print(const char *s) void nethack_enter_consoletty(void) { + int width; #ifdef VIRTUAL_TERMINAL_SEQUENCES char buf[BUFSZ], *bp, *localestr; BOOL apisuccess; +// DWORD unused; +// int i = 0; + #endif /* VIRTUAL_TERMINAL_SEQUENCES */ #if 0 /* set up state needed by early_raw_print() */ @@ -2562,13 +2563,38 @@ void nethack_enter_consoletty(void) GetWindowLong(console.hWnd, GWL_STYLE) & ~WS_MAXIMIZEBOX & ~WS_SIZEBOX); #endif + + CONSOLE_SCREEN_BUFFER_INFO csbi; + if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) { + /* srWindow identifies the visible area; dwSize identifies the buffer + */ + width = csbi.srWindow.Right - csbi.srWindow.Left + 1; + fprintf(stdout, "width = %d\n", width); + } + console.hConOut = GetStdHandle(STD_OUTPUT_HANDLE); nhassert(console.hConOut != NULL); // NOTE: this assert will not print GetConsoleScreenBufferInfo(console.hConOut, &console.orig_csbi); + //COORD screencheck = GetLargestConsoleWindowSize(console.hConOut); + + GetConsoleMode(console.hConOut, &console.orig_out_cmode); + console.out_cmode = console.orig_out_cmode; + console.out_cmode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + SetConsoleMode(console.hConOut, console.out_cmode); +#if 0 + /* tests */ + WriteConsoleA(console.hConOut, "\033[8;;133t",9, &unused, NULL); + for (i = 0; i < 13; ++i) { + WriteConsoleA(console.hConOut, "0123456789", 10, &unused, NULL); + } + WriteConsoleA(console.hConOut, "\033[3;133ftest", 12, &unused, NULL); + GetConsoleScreenBufferInfo(console.hConOut, &console.orig_csbi); /* Testing of widths != COLNO has not turned up any problems. Need * to do a bit more testing and then we are likely to enable having * console width match window width. */ +#endif + #if 0 console.width = console.orig_csbi.srWindow.Right - console.orig_csbi.srWindow.Left + 1; diff --git a/sys/windows/vs/NetHack/NetHack.vcxproj b/sys/windows/vs/NetHack/NetHack.vcxproj index 131a4cf05..c6f2df87c 100644 --- a/sys/windows/vs/NetHack/NetHack.vcxproj +++ b/sys/windows/vs/NetHack/NetHack.vcxproj @@ -74,7 +74,7 @@ Speed true $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);%(AdditionalIncludeDirectories) - WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;SND_SOUNDEFFECTS_AUTOMAP;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) + WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;SND_SOUNDEFFECTS_AUTOMAP;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) stdclatest @@ -218,7 +218,6 @@ - diff --git a/sys/windows/vs/NetHackW/NetHackW.vcxproj b/sys/windows/vs/NetHackW/NetHackW.vcxproj index fce2d2a1d..cc3ec2563 100644 --- a/sys/windows/vs/NetHackW/NetHackW.vcxproj +++ b/sys/windows/vs/NetHackW/NetHackW.vcxproj @@ -87,7 +87,7 @@ Disabled true $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);%(AdditionalIncludeDirectories) - TILES;_WINDOWS;DLB;MSWIN_GRAPHICS;SAFEPROCS;NOTTYGRAPHICS;SND_LIB_WINDSOUND;USER_SOUNDS;SND_SOUNDEFFECTS_AUTOMAP;HAS_STDINT_H;HAS_INTTYPES_H;PDC_WIDE;%(PreprocessorDefinitions) + TILES;_WINDOWS;DLB;MSWIN_GRAPHICS;NOTTYGRAPHICS;SND_LIB_WINDSOUND;USER_SOUNDS;SND_SOUNDEFFECTS_AUTOMAP;HAS_STDINT_H;HAS_INTTYPES_H;PDC_WIDE;%(PreprocessorDefinitions) stdclatest stdclatest stdclatest @@ -263,7 +263,6 @@ - diff --git a/sys/windows/vs/sfctool/sfctool.vcxproj b/sys/windows/vs/sfctool/sfctool.vcxproj index 5dff90066..ad1971969 100644 --- a/sys/windows/vs/sfctool/sfctool.vcxproj +++ b/sys/windows/vs/sfctool/sfctool.vcxproj @@ -150,7 +150,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 stdclatest true @@ -166,7 +166,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_NDEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_NDEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 stdclatest true @@ -186,7 +186,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 stdclatest true @@ -202,7 +202,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 stdclatest true @@ -218,7 +218,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_NDEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_NDEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 stdclatest true @@ -238,7 +238,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_NDEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_NDEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 stdclatest true @@ -258,4 +258,4 @@ - \ No newline at end of file + diff --git a/sys/windows/vs/sftags/sftags.vcxproj b/sys/windows/vs/sftags/sftags.vcxproj index 43f024c24..52c63d47a 100644 --- a/sys/windows/vs/sftags/sftags.vcxproj +++ b/sys/windows/vs/sftags/sftags.vcxproj @@ -110,7 +110,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 true true @@ -125,7 +125,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 true true @@ -144,7 +144,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 true true @@ -159,7 +159,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 true true @@ -174,7 +174,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 true true @@ -194,7 +194,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 true true @@ -223,4 +223,4 @@ - \ No newline at end of file + diff --git a/sys/windows/windmain.c b/sys/windows/windmain.c index 9e34152ac..c50a6d292 100644 --- a/sys/windows/windmain.c +++ b/sys/windows/windmain.c @@ -14,10 +14,6 @@ #include #include -#if !defined(SAFEPROCS) -#error You must #define SAFEPROCS to build windmain.c -#endif - static void nhusage(void); char *exename(void); boolean fakeconsole(void); @@ -67,7 +63,6 @@ char windows_yn_function(const char *, const char *, char); #ifdef WIN32CON extern int windows_console_custom_nhgetch(void); -void safe_routines(void); int tty_self_recover_prompt(void); #endif @@ -106,6 +101,13 @@ void update_file(const char *, const char *, const char *, const char *, BOOL); void windows_raw_print_bold(const char *); +staticfn void set_emergency_io(void); +staticfn void stdio_wait_synch(void); +staticfn void stdio_raw_print(const char *str); +staticfn void stdio_nonl_raw_print(const char *str); +staticfn void stdio_raw_print_bold(const char *str); +staticfn int stdio_nhgetch(void); + #ifdef PORT_HELP void port_help(void); #endif @@ -159,18 +161,9 @@ MAIN(int argc, char *argv[]) char *dir = NULL; #ifdef _MSC_VER - _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); + _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); #endif -#ifdef WIN32CON - /* - * Get a set of valid safe windowport function - * pointers during early startup initialization. - */ -// safe_routines(); -#endif /* WIN32CON */ - - /* setting iflags.colorcount has to be after early_init() * because it zeros out all of iflags */ hwnd = GetDesktopWindow(); @@ -208,8 +201,9 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ #ifndef MSWIN_GRAPHICS early_init(argc, argv); /* already in WinMain for MSWIN_GRAPHICS */ #endif - set_default_prefix_locations(argv[0]); /* must be re-done after initoptions_init() - * which clears out gp.fqn_prefix[] */ + set_default_prefix_locations( + argv[0]); /* must be re-done after initoptions_init() + * which clears out gp.fqn_prefix[] */ copy_sysconf_content(); copy_symbols_content(); /* Now that sysconf has had a chance to set the TROUBLEPREFIX, don't @@ -217,13 +211,20 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ fqn_prefix_locked[TROUBLEPREFIX] = TRUE; copy_config_content(); - // if (iflags.windowtype_deferred && gc.chosen_windowtype[0]) - // windowtype = gc.chosen_windowtype; - // windowtype = gc.chosen_windowtype; + // if (iflags.windowtype_deferred && gc.chosen_windowtype[0]) + // windowtype = gc.chosen_windowtype; + // windowtype = gc.chosen_windowtype; #if !defined(MSWIN_GRAPHICS) - consoletty_open(1); nethack_enter_consoletty(); + consoletty_open(1); +#endif + set_emergency_io(); + +#ifdef EARLY_CONFIGFILE_PASS + rcfile_interface_options(); + if (gc.chosen_windowtype && *gc.chosen_windowtype) + windowtype = gc.chosen_windowtype; #endif if (!windowtype) { @@ -233,7 +234,10 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ windowtype = "tty"; #endif } - choose_windows(windowtype); /* sets all the window port function pointers */ + choose_windows( + windowtype); /* sets all the window port function pointers */ + + init_nhwindows(&argc, argv); #if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) /* Save current directory and make sure it gets restored when @@ -243,8 +247,9 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ error("NetHack: current directory path too long"); #endif initoptions_init(); // This allows OPTIONS in syscf on Windows. - set_default_prefix_locations(argv[0]); /* must be re-done after initoptions_init() - * which clears out gp.fqn_prefix[] */ + set_default_prefix_locations( + argv[0]); /* must be re-done after initoptions_init() + * which clears out gp.fqn_prefix[] */ iflags.windowtype_deferred = TRUE; program_state.early_options = 1; @@ -286,7 +291,6 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ && (strstri(argv[0], "nethackw.exe") || GUILaunched)) iflags.windowtype_locked = TRUE; #endif - windowtype = default_window_sys; #ifdef DLB if (!dlb_init()) { @@ -301,18 +305,6 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ } #endif - if (!iflags.windowtype_locked) { -#if defined(TTY_GRAPHICS) - Strcpy(default_window_sys, "tty"); -#else -#if defined(CURSES_GRAPHICS) && !defined(MSWIN_GRAPHICS) - Strcpy(default_window_sys, "curses"); -#endif /* CURSES */ -#endif /* TTY */ - // if (iflags.windowtype_deferred && gc.chosen_windowtype[0]) - // windowtype = gc.chosen_windowtype; - } - // choose_windows(windowtype); #if defined(SND_LIB_FMOD) assign_soundlib(soundlib_fmod); #elif defined(SND_LIB_WINDSOUND) @@ -322,19 +314,14 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ u.uhp = 1; /* prevent RIP on early quits */ u.ux = 0; /* prevent flush_screen() */ - nethack_enter(argc, argv); iflags.use_background_glyph = FALSE; if (WINDOWPORT(mswin)) iflags.use_background_glyph = TRUE; -#ifdef WIN32CON - if (WINDOWPORT(tty)) - consoletty_open(1); -#endif #ifdef WINCHAIN commit_windowchain(); #endif - init_nhwindows(&argc, argv); +// init_nhwindows(&argc, argv); #ifdef WIN32CON if (WINDOWPORT(tty)) @@ -837,20 +824,6 @@ copy_hack_content(void) gf.fqn_prefix[DATAPREFIX], OPTIONFILE, FALSE); } -#ifdef WIN32CON -void -safe_routines(void) -{ - /* - * Get a set of valid safe windowport function - * pointers during early startup initialization. - */ - if (!WINDOWPORT(safestartup)) - windowprocs = *get_safe_procs(1); - if (!GUILaunched) - windowprocs.win_nhgetch = windows_console_custom_nhgetch; -} -#endif #ifdef PORT_HELP void @@ -1205,9 +1178,7 @@ tty_self_recover_prompt(void) c = 'n'; ct = 0; saved_procs = windowprocs; - if (!WINDOWPORT(safestartup)) - windowprocs = *get_safe_procs(2); /* arg 2 uses no-newline variant */ - windowprocs.win_nhgetch = windows_console_custom_nhgetch; + raw_print("\n"); raw_print("\n"); raw_print("\n"); @@ -1340,4 +1311,60 @@ chdirx(const char *dir, boolean wr) } #endif /* CHDIR */ -/*windmain.c*/ +void +set_emergency_io(void) +{ + windowprocs.win_raw_print = stdio_raw_print; + windowprocs.win_raw_print_bold = stdio_raw_print_bold; + windowprocs.win_nhgetch = stdio_nhgetch; + windowprocs.win_wait_synch = stdio_wait_synch; +} + + +/* Add to your code: windowprocs.win_raw_print = stdio_wait_synch; */ +void +stdio_wait_synch(void) +{ + char valid[] = { ' ', '\n', '\r', '\033', '\0' }; + + fprintf(stdout, "--More--"); + (void) fflush(stdout); + while (!strchr(valid, nhgetch())) + ; +} + +/* Add to your code: windowprocs.win_raw_print = stdio_raw_print; */ +void +stdio_raw_print(const char *str) +{ + if (str) + fprintf(stdout, "%s\n", str); + return; +} + +/* no newline variation, add to your code: + windowprocs.win_raw_print = stdio_nonl_raw_print; */ +void +stdio_nonl_raw_print(const char *str) +{ + if (str) + fprintf(stdout, "%s", str); + return; +} + +/* Add to your code: windowprocs.win_raw_print_bold = stdio_raw_print_bold; */ +void +stdio_raw_print_bold(const char *str) +{ + stdio_raw_print(str); + return; +} + +/* Add to your code: windowprocs.win_nhgetch = stdio_nhgetch; */ +int +stdio_nhgetch(void) +{ + return getchar(); +} + + /*windmain.c*/ diff --git a/sys/windows/windsys.c b/sys/windows/windsys.c index 88f1a69bf..aca0cfe9e 100644 --- a/sys/windows/windsys.c +++ b/sys/windows/windsys.c @@ -73,7 +73,6 @@ static HWND GetConsoleHwnd(void); extern void backsp(void); #endif int windows_console_custom_nhgetch(void); -extern void safe_routines(void); int windows_early_options(const char *window_opt); unsigned long sys_random_seed(void); #if 0 @@ -293,10 +292,6 @@ win32_abort(void) exit_nhwindows((char *) 0); iflags.window_inited = FALSE; } -#ifdef WIN32CON - if (!WINDOWPORT(mswin) && !WINDOWPORT(safestartup)) - safe_routines(); -#endif if (wizard) { raw_print("Execute debug breakpoint wizard?"); if ((c = nhgetch()) == 'y' || c == 'Y') @@ -522,15 +517,6 @@ nethack_exit(int code) * GUILaunched is defined and set in consoletty.c. */ - -#ifdef WIN32CON - if (!GUILaunched) { - windowprocs = *get_safe_procs(1); - /* use our custom version which works - a little cleaner than the stdio one */ - windowprocs.win_nhgetch = windows_console_custom_nhgetch; - } else -#endif if (getreturn_enabled) { raw_print("\n"); if (iflags.window_inited) @@ -580,10 +566,6 @@ getreturn(const char *str) initializing the window port */ void nethack_enter_windows(void) { -#ifdef WIN32CON - if (WINDOWPORT(tty)) - nethack_enter_consoletty(); -#endif } /* CP437 to Unicode mapping according to the Unicode Consortium */ diff --git a/win/share/safeproc.c b/win/share/safeproc.c deleted file mode 100644 index 19e9aa9b6..000000000 --- a/win/share/safeproc.c +++ /dev/null @@ -1,596 +0,0 @@ -/* NetHack 3.7 safeproc.c */ -/* Copyright (c) Michael Allison, 2018 */ -/* NetHack may be freely redistributed. See license for details. */ - -/* must #define SAFEPROCS in xxxconf.h or via CFLAGS or this won't compile */ -#include "hack.h" - -/* - * *********************************************************** - * This is a complete WindowPort implementation that can be - * assigned to the windowproc function pointers very early - * in the startup initialization, perhaps immediately even. - * It requires only the following call: - * windowprocs = *get_safe_procs(0); - * - * The game startup can trigger functions in other modules - * that make assumptions on a WindowPort being available - * and bad things can happen if any function pointers are - * null at that time. - * - * Some ports prior to 3.6.2 made attempts to early init - * various pieces of one of their WindowPorts, but that - * caused conflicts if that particular WindowPort wasn't - * the one that the user ended up selecting in their - * config file later. The WindowPort interfaced was designed - * to allow multiple WindowPorts to be linked into the same - * game binary. - * - * The base functions established by a call to get_safe_procs() - * accomplish the goal of preventing crashes, but not much - * else. - * - * There are also a few additional functions provided in here - * that can be selected optionally to provide some startup - * functionality for getting messages out to the user about - * issues that are being experienced during startup in - * general or during options parsing. The ones in here are - * deliberately free from any platforms or OS specific code. - * Please leave them using stdio C routines as much as - * possible. That isn't to say you can't do fancier functions - * prior to initialization of the primary WindowPort, but you - * can provide those platform-specific functions elsewhere, - * and assign them the same way that these more generic versions - * are assigned. - * - * The additional platform-independent, but more functional - * routines provided in here should be assigned after the - * windowprocs = *get_safe_procs(n) - * call. - * - * Usage: - * - * windowprocs = *get_safe_procs(0); - * initializes a set of winprocs function pointers that ensure - * none of the function pointers are left null, but that's all - * it does. - * - * windowprocs = *get_safe_procs(1); - * initializes a set of winprocs functions pointers that ensure - * none of the function pointers are left null, but also - * provides some basic output and input functionality using - * nothing other than C stdio routines (no platform-specific - * or OS-specific code). - * - * *********************************************************** - */ - -void safe_dismiss_nhwindow(winid); -void safe_putstr(winid, int, const char *); -void win_safe_init(int); -void safe_number_pad(int); - -struct window_procs safe_procs = { - WPID(safestartup), - (0 -#ifdef TTY_PERM_INVENT - | WC_PERM_INVENT -#endif -#ifdef MSDOS - | WC_TILED_MAP | WC_ASCII_MAP -#endif -#if defined(WIN32CON) - | WC_MOUSE_SUPPORT -#endif - | WC_COLOR | WC_HILITE_PET | WC_INVERSE | WC_EIGHT_BIT_IN), - (0 -#if defined(SELECTSAVED) - | WC2_SELECTSAVED -#endif -#if defined(STATUS_HILITES) - | WC2_HILITE_STATUS | WC2_HITPOINTBAR | WC2_FLUSH_STATUS - | WC2_RESET_STATUS -#endif - | WC2_DARKGRAY | WC2_SUPPRESS_HIST | WC2_URGENT_MESG | WC2_STATUSLINES - | WC2_U_UTF8STR | WC2_PETATTR -#if !defined(NO_TERMS) || defined(WIN32CON) - | WC2_EXTRACOLORS -#endif - ), - {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* color availability */ - safe_init_nhwindows, safe_player_selection, safe_askname, - safe_get_nh_event, - safe_exit_nhwindows, safe_suspend_nhwindows, safe_resume_nhwindows, - safe_create_nhwindow, safe_clear_nhwindow, safe_display_nhwindow, - safe_destroy_nhwindow, safe_curs, safe_putstr, safe_putmixed, - safe_display_file, safe_start_menu, safe_add_menu, safe_end_menu, - safe_select_menu, safe_message_menu, - safe_mark_synch, - safe_wait_synch, -#ifdef CLIPPING - safe_cliparound, -#endif -#ifdef POSITIONBAR - safe_update_positionbar, -#endif - safe_print_glyph, safe_raw_print, safe_raw_print_bold, safe_nhgetch, - safe_nh_poskey, safe_nhbell, safe_doprev_message, safe_yn_function, - safe_getlin, safe_get_ext_cmd, safe_number_pad, safe_delay_output, -#ifdef CHANGE_COLOR /* the Mac uses a palette device */ - safe_change_color, -#ifdef MAC - safe_change_background, set_safe_font_name, -#endif - safe_get_color_string, -#endif - safe_outrip, - safe_preference_update, - safe_getmsghistory, safe_putmsghistory, - safe_status_init, - safe_status_finish, safe_status_enablefield, - safe_status_update, - safe_can_suspend, - safe_update_inventory, - safe_ctrl_nhwindow, -}; - -struct window_procs * -get_safe_procs(int optn) -{ - if (optn) { - /* include the slightly more functional stdc versions */ - safe_procs.win_raw_print = stdio_raw_print; - safe_procs.win_raw_print_bold = stdio_raw_print_bold; - safe_procs.win_nhgetch = stdio_nhgetch; - safe_procs.win_wait_synch = stdio_wait_synch; - if (optn == 2) - safe_procs.win_raw_print = stdio_nonl_raw_print; - } - return &safe_procs; -} - -/*ARGSUSED*/ -void -safe_init_nhwindows(int *argcp UNUSED, char **argv UNUSED) -{ - return; -} - -void -safe_player_selection(void) -{ - return; -} - -void -safe_askname(void) -{ - return; -} - -void -safe_get_nh_event(void) -{ - return; -} - -void -safe_suspend_nhwindows(const char *str UNUSED) -{ - return; -} - -void -safe_resume_nhwindows(void) -{ - return; -} - -void -safe_exit_nhwindows(const char *str UNUSED) -{ - return; -} - -winid -safe_create_nhwindow(int type UNUSED) -{ - return WIN_ERR; -} - -void -safe_clear_nhwindow(winid window UNUSED) -{ - return; -} - -/*ARGSUSED*/ -void -safe_display_nhwindow(winid window UNUSED, boolean blocking UNUSED) -{ - return; -} - -void -safe_dismiss_nhwindow(winid window UNUSED) -{ - return; -} - -void -safe_destroy_nhwindow(winid window UNUSED) -{ - return; -} - -void -safe_curs(winid window UNUSED, int x UNUSED, int y UNUSED) -{ - return; -} - -void -safe_putstr(winid window UNUSED, int attr UNUSED, const char *str UNUSED) -{ - return; -} - -void -safe_putmixed(winid window UNUSED, int attr UNUSED, const char *str UNUSED) -{ - return; -} - -void -safe_display_file(const char * fname UNUSED, boolean complain UNUSED) -{ - return; -} - -void -safe_start_menu(winid window UNUSED, unsigned long mbehavior UNUSED) -{ - return; -} - -/*ARGSUSED*/ -/* - * Add a menu item to the beginning of the menu list. This list is reversed - * later. - */ -void -safe_add_menu( - winid window UNUSED, /* window to use, must be of type NHW_MENU */ - const glyph_info *glyphinfo UNUSED, /* glyph plus glyph info */ - const anything *identifier UNUSED, /* what to return if selected */ - char ch UNUSED, /* keyboard accelerator (0 = pick our own) */ - char gch UNUSED, /* group accelerator (0 = no group) */ - int attr UNUSED, /* attribute for string (like safe_putstr()) */ - int clr UNUSED, /* colour for string */ - const char *str UNUSED, /* menu string */ - unsigned int itemflags UNUSED) /* itemflags such as marked as selected */ -{ - return; -} - -/* - * End a menu in this window, window must a type NHW_MENU. - */ -void -safe_end_menu( - winid window UNUSED, /* menu to use */ - const char *prompt UNUSED) /* prompt to for menu */ -{ - return; -} - -int -safe_select_menu( - winid window UNUSED, - int how UNUSED, - menu_item **menu_list UNUSED) -{ - return 0; -} - -/* special hack for treating top line --More-- as a one item menu */ -char -safe_message_menu( - char let UNUSED, - int how UNUSED, - const char *mesg UNUSED) -{ - return '\033'; -} - -void -safe_mark_synch(void) -{ -} - -void -safe_wait_synch(void) -{ -} - -#ifdef CLIPPING -void -safe_cliparound(int x UNUSED, int y UNUSED) -{ -} -#endif /* CLIPPING */ - -/* - * safe_print_glyph - * - * Print the glyph to the output device. Don't flush the output device. - */ -void -safe_print_glyph( - winid window UNUSED, - coordxy x UNUSED, - coordxy y UNUSED, - const glyph_info *glyphinfo UNUSED, - const glyph_info *bkglyphinfo UNUSED) -{ - return; -} - -void -safe_raw_print(const char *str UNUSED) -{ - return; -} - -void -safe_raw_print_bold(const char *str UNUSED) -{ - return; -} - -int -safe_nhgetch(void) -{ - return '\033'; -} - -/* - * return a key, or 0, in which case a mouse button was pressed - * mouse events should be returned as character positions in the map window. - * Since normal tty's don't have mice, just return a key. - */ -/*ARGSUSED*/ -int -safe_nh_poskey(coordxy *x UNUSED, coordxy *y UNUSED, int *mod UNUSED) -{ - return '\033'; -} - -void -win_safe_init(int dir UNUSED) -{ - return; -} - -#ifdef POSITIONBAR -void -safe_update_positionbar(char *posbar UNUSED) -{ - return; -} -#endif /* POSITIONBAR */ - -/* - * safe_status_init() - * -- initialize the port-specific data structures. - */ -void -safe_status_init(void) -{ - return; -} - -boolean -safe_can_suspend(void) -{ - return FALSE; -} - -void -safe_nhbell(void) -{ - return; -} - -int -safe_doprev_message(void) -{ - return 0; -} - -char -safe_yn_function(const char *query UNUSED, - const char *resp UNUSED, char def UNUSED) -{ - return '\033'; -} - -/*ARGSUSED*/ -void -safe_getlin(const char* prompt UNUSED, char *outbuf) -{ - Strcpy(outbuf, "\033"); -} - -int -safe_get_ext_cmd(void) -{ - return '\033'; -} - -void -safe_number_pad(int mode UNUSED) -{ - return; -} - -void -safe_delay_output(void) -{ - return; -} - -void -safe_outrip(winid tmpwin UNUSED, int how UNUSED, time_t when UNUSED) -{ - return; -} - -/*ARGSUSED*/ -void -safe_preference_update(const char *pref UNUSED) -{ - return; -} - -char * -safe_getmsghistory(boolean init UNUSED) -{ - return (char *) 0; -} - -void -safe_putmsghistory( - const char *msg UNUSED, - boolean is_restoring UNUSED) -{ -} - -void -safe_status_finish(void) -{ -} - -void -safe_status_enablefield( - int fieldidx UNUSED, - const char *nm UNUSED, - const char *fmt UNUSED, - boolean enable UNUSED) -{ -} - -/* call once for each field, then call with BL_FLUSH to output the result */ -void -safe_status_update( - int idx UNUSED, - genericptr_t ptr UNUSED, - int chg UNUSED, - int percent UNUSED, - int color UNUSED, - unsigned long *colormasks UNUSED) -{ -} - -void -safe_update_inventory(int arg UNUSED) -{ - return; -} - -#ifdef WIN32CON -extern win_request_info *tty_ctrl_nhwindow(winid window UNUSED, - int request UNUSED, - win_request_info *wri UNUSED); -#endif - -win_request_info * -safe_ctrl_nhwindow( - winid window UNUSED, - int request UNUSED, - win_request_info *wri UNUSED) -{ -#ifdef WIN32CON - return (*tty_ctrl_nhwindow)(window, request, wri); -#else - return (win_request_info *) 0; -#endif -} - -/************************************************************** - * These are some optionally selectable routines that add - * some base functionality over the safe_* versions above. - * The safe_* versions are primarily designed to ensure that - * there are no null function pointers remaining at early - * game startup/initialization time. - * - * The slightly more functional versions in here should be kept - * free of platform-specific code or OS-specific code. If you - * want to use versions that involve platform-specific or - * OS-specific code, go right ahead but use your own replacement - * version of the functions in a platform-specific or - * OS-specific source file, not in here. - ***************************************************************/ - -/* Add to your code: windowprocs.win_raw_print = stdio_wait_synch; */ -void -stdio_wait_synch(void) -{ - char valid[] = {' ', '\n', '\r', '\033', '\0'}; - - fprintf(stdout, "--More--"); - (void) fflush(stdout); - while (!strchr(valid, nhgetch())) - ; -} - -/* Add to your code: windowprocs.win_raw_print = stdio_raw_print; */ -void -stdio_raw_print(const char *str) -{ - if (str) - fprintf(stdout, "%s\n", str); - return; -} - -/* no newline variation, add to your code: - windowprocs.win_raw_print = stdio_nonl_raw_print; */ -void -stdio_nonl_raw_print(const char *str) -{ - if (str) - fprintf(stdout, "%s", str); - return; -} - -/* Add to your code: windowprocs.win_raw_print_bold = stdio_raw_print_bold; */ -void -stdio_raw_print_bold(const char *str) -{ - stdio_raw_print(str); - return; -} - -/* Add to your code: windowprocs.win_nhgetch = stdio_nhgetch; */ -int -stdio_nhgetch(void) -{ - return getchar(); -} - -#ifdef CHANGE_COLOR -void -safe_change_color(int color UNUSED, long rgb UNUSED, int reverse UNUSED) -{ -} - -char * -safe_get_color_string(void) -{ - return (""); -} - - -#endif - -/* safeprocs.c */ diff --git a/win/tty/wintty.c b/win/tty/wintty.c index 61338542b..4cbe65a2a 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -3559,8 +3559,9 @@ assesstty( short *offx, short *offy, long *rows, long *cols, long *maxcol, long *minrow, long *maxrow) { - boolean inuse_only = (invmode & InvInUse) != 0, - show_gold = (invmode & InvShowGold) != 0 && !inuse_only; + boolean inuse_only = (invmode & (int) InvInUse) != 0, + show_gold = (invmode & (int) InvShowGold) != 0 + && !inuse_only; int perminv_minrow = tty_perminv_minrow + (show_gold ? 1 : 0); if (!ttyDisplay) { diff --git a/win/win32/NetHackW.c b/win/win32/NetHackW.c index 92b851c7a..56d8bb7c7 100644 --- a/win/win32/NetHackW.c +++ b/win/win32/NetHackW.c @@ -15,10 +15,6 @@ #include "mhmain.h" #include "mhmap.h" -#if !defined(SAFEPROCS) -#error You must #define SAFEPROCS to build NetHackW.c -#endif - /* Borland and MinGW redefine "boolean" in shlwapi.h, so just use the little bit we need */ typedef struct _DLLVERSIONINFO { @@ -99,32 +95,6 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, UNREFERENCED_PARAMETER(lpCmdLine); UNREFERENCED_PARAMETER(nCmdShow); - /* - * Get a set of valid safe windowport function - * pointers during early startup initialization. - * - * When get_safe_procs is called with 0 as the param, - * non-functional, but safe function pointers are set - * for all windowport routines. - * - * When get_safe_procs is called with 1 as the param, - * raw_print, raw_print_bold, and wait_synch, and nhgetch - * are set to use C stdio routines via stdio_raw_print, - * stdio_raw_print_bold, stdio_wait_synch, and - * stdio_nhgetch. - */ - windowprocs = *get_safe_procs(0); - - /* - * Now we are going to override a couple - * of the windowprocs functions so that - * error messages are handled in a suitable - * way for the graphical version. - */ - windowprocs.win_raw_print = mswin_raw_print; - windowprocs.win_raw_print_bold = mswin_raw_print_bold; - windowprocs.win_wait_synch = mswin_wait_synch; - win10_init(); early_init(0, NULL); /* Change as needed to support CRASHREPORT */ diff --git a/win/win32/mswproc.c b/win/win32/mswproc.c index abef776da..55ba2f696 100644 --- a/win/win32/mswproc.c +++ b/win/win32/mswproc.c @@ -734,9 +734,6 @@ mswin_exit_nhwindows(const char *str) /* Write Window settings to the registry */ mswin_write_reg(); - /* set things back to failsafes */ - windowprocs = *get_safe_procs(0); - /* and make sure there is still a way to communicate something */ windowprocs.win_raw_print = mswin_raw_print; windowprocs.win_raw_print_bold = mswin_raw_print_bold; From db1f230772ff490a29b623bf8fadfd054c9b427c Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 5 Apr 2026 12:07:57 -0400 Subject: [PATCH 395/442] Revert "Change Windows startup" This reverts commit acb85b18cfbee62f0f804853cfd4d9c9655a1c4e. Some optlist issues arose on some platforms, but not all. I need to investigate the cause of those. --- include/decl.h | 3 +- include/extern.h | 7 - include/global.h | 12 +- include/hack.h | 16 + include/optlist.h | 45 +- include/windconf.h | 2 - include/winprocs.h | 75 +++ src/cfgfiles.c | 79 --- src/decl.c | 2 +- src/options.c | 279 +++++------ sys/windows/GNUmakefile | 10 +- sys/windows/Makefile.nmake | 12 +- sys/windows/consoletty.c | 32 +- sys/windows/vs/NetHack/NetHack.vcxproj | 3 +- sys/windows/vs/NetHackW/NetHackW.vcxproj | 3 +- sys/windows/vs/sfctool/sfctool.vcxproj | 14 +- sys/windows/vs/sftags/sftags.vcxproj | 14 +- sys/windows/windmain.c | 149 +++--- sys/windows/windsys.c | 18 + win/share/safeproc.c | 596 +++++++++++++++++++++++ win/tty/wintty.c | 5 +- win/win32/NetHackW.c | 30 ++ win/win32/mswproc.c | 3 + 23 files changed, 959 insertions(+), 450 deletions(-) create mode 100644 win/share/safeproc.c diff --git a/include/decl.h b/include/decl.h index f66c0f5a8..a34550ef9 100644 --- a/include/decl.h +++ b/include/decl.h @@ -725,8 +725,7 @@ struct instance_globals_o { /* options.c */ - /* builtin_opt, syscf_, rc_file_, environ_, play_opt */ - enum option_phases opt_phase; + int opt_phase; /* builtin_opt, syscf_, rc_file_, environ_, play_opt */ boolean opt_initial; boolean opt_from_file; boolean opt_need_redraw; /* for doset() */ diff --git a/include/extern.h b/include/extern.h index b3206b015..8ed1b0f0f 100644 --- a/include/extern.h +++ b/include/extern.h @@ -335,8 +335,6 @@ extern boolean parse_conf_file(FILE *fp, boolean (*proc)(char *arg)); extern void set_configfile_name(const char *); extern char *get_configfile(void); extern const char *get_default_configfile(void); -extern void rcfile(void); -extern void rcfile_interface_options(void); /* ### coloratt.c ### */ @@ -2322,10 +2320,6 @@ extern int msgtype_type(const char *, boolean) NONNULLARG1; extern void hide_unhide_msgtypes(boolean, int); extern void msgtype_free(void); extern void options_free_window_colors(void); -extern void set_all_options_heeded(void); -extern void set_all_options_disregarded(void); -extern void heed_this_option(enum opt); -extern void disregard_this_option(enum opt); #ifdef TTY_PERM_INVENT extern void check_perm_invent_again(void); #endif @@ -2351,7 +2345,6 @@ extern int dowhatdoes(void); extern char *dowhatdoes_core(char, char *) NONNULLARG2; /*might return NULL*/ extern int dohelp(void); extern int dohistory(void); -void allopt_array_init(void); /* ### xxmain.c ### */ diff --git a/include/global.h b/include/global.h index 1439044f1..21b00fe46 100644 --- a/include/global.h +++ b/include/global.h @@ -102,6 +102,8 @@ typedef unsigned readLenType; #endif #define BOOL_RANDOM (-1) +enum optchoice { opt_in, opt_out}; + /* * type nhsym: loadable symbols go into this type */ @@ -151,16 +153,6 @@ typedef uchar nhsym; #endif /* __GNUC__ || _MSC_VER */ #endif /* !__clang__ */ -#define SET__IS_VALUE_VALID(s) ((s < set_in_sysconf) || (s > set_wiznofuz)) -#include "optlist.h" -enum opt { - opt_prefix_only = -1, -#define NHOPT_ENUM -#include "optlist.h" -#undef NHOPT_ENUM - OPTCOUNT -}; - /* * Automatic inclusions for the subsidiary files. * Please don't change the order. It does matter. diff --git a/include/hack.h b/include/hack.h index f655a8961..bb9f9d642 100644 --- a/include/hack.h +++ b/include/hack.h @@ -702,6 +702,22 @@ enum nhcb_calls { NUM_NHCB }; +/* + * option setting restrictions + */ + +enum optset_restrictions { + set_in_sysconf = 0, /* system config file option only */ + set_in_config = 1, /* config file option only */ + set_viaprog = 2, /* may be set via extern program, not seen in game */ + set_gameview = 3, /* may be set via extern program, displayed in game */ + set_in_game = 4, /* may be set via extern program or set in the game */ + set_wizonly = 5, /* may be set in the game if wizmode */ + set_wiznofuz = 6, /* wizard-mode only, but not by fuzzer */ + set_hidden = 7 /* placeholder for prefixed entries, never show it */ +}; +#define SET__IS_VALUE_VALID(s) ((s < set_in_sysconf) || (s > set_wiznofuz)) + struct plinemsg_type { xint16 msgtype; /* one of MSGTYP_foo */ struct nhregex *regex; diff --git a/include/optlist.h b/include/optlist.h index 9322bee7e..b920048ac 100644 --- a/include/optlist.h +++ b/include/optlist.h @@ -4,6 +4,10 @@ #ifndef OPTLIST_H #define OPTLIST_H +#ifdef OPTIONS_C +static int optfn_boolean(int, int, boolean, char *, char *); +#endif + /* * NOTE: If you add (or delete) an option, please review: * doc/options.txt @@ -12,37 +16,6 @@ * updates that should accompany your change. */ -#define BACKWARD_COMPAT - -extern int optfn_boolean(int, int, boolean, char *, char *); -enum optchoice { opt_in, opt_out }; - -/* - * option setting restrictions - */ -enum optset_restrictions { - set_in_sysconf = 0, /* system config file option only */ - set_in_config = 1, /* config file option only */ - set_viaprog = 2, /* may be set via extern program, not seen in game */ - set_gameview = 3, /* may be set via extern program, displayed in game */ - set_in_game = 4, /* may be set via extern program or set in the game */ - set_wizonly = 5, /* may be set in the game if wizmode */ - set_wiznofuz = 6, /* wizard-mode only, but not by fuzzer */ - set_hidden = 7 /* placeholder for prefixed entries, never show it */ -}; - -/* these aren't the same as set_xxx */ -enum option_phases { - phase_not_set = 0, - builtin_opt = 1, /* compiled-in default value of an option */ - syscf_opt, /* sysconf setting of an option, overrides builtin */ - rc_file_opt, /* player's run-time config file setting, overrides syscf */ - environ_opt, /* player's environment NETHACKOPTIONS, overrides rc_file */ - cmdline_opt, /* program invocation command-line, overrides environ */ - play_opt, /* 'O' command, interactively set so overrides all */ - num_opt_phases -}; - enum OptType { BoolOpt, CompOpt, OthrOpt }; enum Y_N { No, Yes }; enum Off_On { Off, On }; @@ -72,7 +45,7 @@ struct allopt_t { const char *alias; const char *descr; const char *prefixgw; - boolean initval, has_handler, dupdetected, disregarded; + boolean initval, has_handler, dupdetected; }; #endif /* OPTLIST_H */ @@ -101,16 +74,16 @@ static int optfn_##a(int, int, boolean, char *, char *); #elif defined(NHOPT_PARSE) #define NHOPTB(a, sec, b, c, s, i, n, v, d, al, bp, termp, desc) \ { #a, OptS_##sec, 0, b, opt_##a, s, BoolOpt, n, v, d, No, termp, c, \ - bp, &optfn_boolean, al, desc, (const char *) 0, i, 0, 0 , 0 }, + bp, &optfn_boolean, al, desc, (const char *) 0, i, 0, 0 }, #define NHOPTC(a, sec, b, c, s, n, v, d, h, al, z) \ { #a, OptS_##sec, 0, b, opt_##a, s, CompOpt, n, v, d, No, 0, c, \ - (boolean *) 0, &optfn_##a, al, z, (const char *) 0, Off, h, 0, 0 }, + (boolean *) 0, &optfn_##a, al, z, (const char *) 0, Off, h, 0 }, #define NHOPTP(a, sec, b, c, s, n, v, d, h, al, z) \ { #a, OptS_##sec, 0, b, pfx_##a, s, CompOpt, n, v, d, Yes, 0, c, \ - (boolean *) 0, &pfxfn_##a, al, z, #a, Off, h, 0, 0 }, + (boolean *) 0, &pfxfn_##a, al, z, #a, Off, h, 0 }, #define NHOPTO(m, sec, a, b, c, s, n, v, d, al, z) \ { m, OptS_##sec, 0, b, opt_##a, s, OthrOpt, n, v, d, No, 0, c, \ - (boolean *) 0, &optfn_##a, al, z, (const char *) 0, On, On, 0, 0 }, + (boolean *) 0, &optfn_##a, al, z, (const char *) 0, On, On, 0 }, /* this is not reliable because TILES_IN_GLYPHMAP might be defined * in a multi-interface binary but not apply to the current interface */ diff --git a/include/windconf.h b/include/windconf.h index b7330668c..bd30f8a6b 100644 --- a/include/windconf.h +++ b/include/windconf.h @@ -32,8 +32,6 @@ #define OPTIONS_AT_RUNTIME /* build info done at runtime not text file */ -#define EARLY_CONFIGFILE_PASS - /* * ----------------------------------------------------------------- * The remaining code shouldn't need modification. diff --git a/include/winprocs.h b/include/winprocs.h index 014ee1e09..688d76138 100644 --- a/include/winprocs.h +++ b/include/winprocs.h @@ -415,4 +415,79 @@ struct chain_procs { }; #endif /* WINCHAIN */ +#ifdef SAFEPROCS +/* + * window port routines available in sys/share/safeproc.c + */ +extern struct window_procs *get_safe_procs(int); +extern void safe_init_nhwindows(int *, char **); +extern void safe_player_selection(void); +extern void safe_askname(void); +extern void safe_get_nh_event(void); +extern void safe_exit_nhwindows(const char *); +extern void safe_suspend_nhwindows(const char *); +extern void safe_resume_nhwindows(void); +extern winid safe_create_nhwindow(int); +extern void safe_clear_nhwindow(winid); +extern void safe_display_nhwindow(winid, boolean); +extern void safe_destroy_nhwindow(winid); +extern void safe_curs(winid, int, int); +extern void safe_putstr(winid, int, const char *); +extern void safe_putmixed(winid, int, const char *); +extern void safe_display_file(const char *, boolean); +extern void safe_start_menu(winid, unsigned long); +extern void safe_add_menu(winid, const glyph_info *, const ANY_P *, + char, char, int, int, const char *, + unsigned int); +extern void safe_end_menu(winid, const char *); +extern int safe_select_menu(winid, int, MENU_ITEM_P **); +extern char safe_message_menu(char, int, const char *); +extern void safe_mark_synch(void); +extern void safe_wait_synch(void); +#ifdef CLIPPING +extern void safe_cliparound(int, int); +#endif +#ifdef POSITIONBAR +extern void safe_update_positionbar(char *); +#endif +extern void safe_print_glyph(winid, coordxy, coordxy, + const glyph_info *, const glyph_info *); +extern void safe_raw_print(const char *); +extern void safe_raw_print_bold(const char *); +extern int safe_nhgetch(void); +extern int safe_nh_poskey(coordxy *, coordxy *, int *); +extern void safe_nhbell(void); +extern int safe_doprev_message(void); +extern char safe_yn_function(const char *, const char *, char); +extern void safe_getlin(const char *, char *); +extern int safe_get_ext_cmd(void); +extern void safe_number_pad(int); +extern void safe_delay_output(void); +#ifdef CHANGE_COLOR +extern void safe_change_color(int, long, int); +#ifdef MAC +extern void safe_change_background(int); +extern short safe_set_font_name(winid, char *); +#endif +extern char *safe_get_color_string(void); +#endif +extern void safe_outrip(winid, int, time_t); +extern void safe_preference_update(const char *); +extern char *safe_getmsghistory(boolean); +extern void safe_putmsghistory(const char *, boolean); +extern void safe_status_init(void); +extern void safe_status_finish(void); +extern void safe_status_enablefield(int, const char *, const char *, + boolean); +extern void safe_status_update(int, genericptr_t, int, int, int, + unsigned long *); +extern boolean safe_can_suspend(void); +extern void stdio_raw_print(const char *); +extern void stdio_nonl_raw_print(const char *); +extern void stdio_raw_print_bold(const char *); +extern void stdio_wait_synch(void); +extern void safe_update_inventory(int); +extern win_request_info *safe_ctrl_nhwindow(winid, int, win_request_info *); +extern int stdio_nhgetch(void); +#endif /* SAFEPROCS */ #endif /* WINPROCS_H */ diff --git a/src/cfgfiles.c b/src/cfgfiles.c index 439747127..1b4715a4f 100644 --- a/src/cfgfiles.c +++ b/src/cfgfiles.c @@ -1881,85 +1881,6 @@ vconfig_error_add(const char *str, va_list the_args) config_erradd(buf); } -void -rcfile(void) -{ - char *opts = 0, *xtraopts = 0; - const char *envname, *namesrc, *nameval; - - go.opt_phase = environ_opt; - /* getenv() instead of nhgetenv(): let total length of options be long; - parseoptions() will check each individually */ - envname = "NETHACKOPTIONS"; - opts = getenv(envname); - if (!opts) { - /* fall back to original name; discouraged */ - envname = "HACKOPTIONS"; - opts = getenv(envname); - } - - if (gc.cmdline_rcfile) { - namesrc = "command line"; - nameval = gc.cmdline_rcfile; - xtraopts = opts; - if (opts && (*opts == '/' || *opts == '\\' || *opts == '@')) - xtraopts = 0; /* NETHACKOPTIONS is a file name; ignore it */ - } else if (opts && (*opts == '/' || *opts == '\\' || *opts == '@')) { - /* NETHACKOPTIONS is a file name; use that instead of the default */ - if (*opts == '@') - ++opts; /* @filename */ - namesrc = envname; - nameval = opts; - xtraopts = 0; - } else { - /* either no NETHACKOPTIONS or it wasn't a file name; - read the default configuration file */ - nameval = namesrc = 0; - xtraopts = opts; - } - - go.opt_phase = rc_file_opt; - /* seemingly arbitrary name length restriction is to prevent error - messages, if any were to be delivered while accessing the file, - from potentially overflowing buffers */ - if (nameval && (int) strlen(nameval) >= BUFSZ / 2) { - config_error_init(TRUE, namesrc, FALSE); - config_error_add( - "nethackrc file name \"%.40s\"... too long; using default", - nameval); - config_error_done(); - nameval = namesrc = 0; /* revert to default nethackrc */ - } - - config_error_init(TRUE, nameval, nameval ? CONFIG_ERROR_SECURE : FALSE); - (void) read_config_file(nameval, set_in_config); - config_error_done(); - if (xtraopts) { - /* NETHACKOPTIONS is present and not a file name */ - go.opt_phase = environ_opt; - config_error_init(FALSE, envname, FALSE); - (void) parseoptions(xtraopts, TRUE, FALSE); - config_error_done(); - } - - if (gc.cmdline_rcfile) - free((genericptr_t) gc.cmdline_rcfile), gc.cmdline_rcfile = 0; - /*[end of nethackrc handling]*/ -} - -void -rcfile_interface_options(void) -{ - allopt_array_init(); - set_all_options_disregarded(); - heed_this_option(opt_windowtype); - heed_this_option(opt_soundlib); - rcfile(); - set_all_options_heeded(); - disregard_this_option(opt_windowtype); - disregard_this_option(opt_soundlib); -} - #ifdef SYSCF #ifdef SYSCF_FILE void diff --git a/src/decl.c b/src/decl.c index f229de737..cc1e8a3b6 100644 --- a/src/decl.c +++ b/src/decl.c @@ -592,7 +592,7 @@ static const struct instance_globals_o g_init_o = { /* o_init.c */ DUMMY, /* oclass_prob_totals */ /* options.c */ - phase_not_set, /* opt_phase */ + 0, /* opt_phase */ FALSE, /* opt_initial */ FALSE, /* opt_from_file */ FALSE, /* opt_need_redraw */ diff --git a/src/options.c b/src/options.c index 8bfe77d97..c22105c03 100644 --- a/src/options.c +++ b/src/options.c @@ -56,13 +56,21 @@ NEARDATA struct accessibility_data a11y; #include "optlist.h" #undef NHOPT_PROTO +#define NHOPT_ENUM +enum opt { + opt_prefix_only = -1, +#include "optlist.h" + OPTCOUNT +}; +#undef NHOPT_ENUM + #define NHOPT_PARSE static struct allopt_t allopt_init[] = { #include "optlist.h" {(const char *) 0, OptS_Advanced, 0, 0, 0, set_in_sysconf, BoolOpt, No, No, No, No, Term_False, 0, (boolean *) 0, (int (*)(int, int, boolean, char *, char *)) 0, - (char *) 0, (const char *) 0, (const char *) 0, 0, 0, 0, TRUE } + (char *) 0, (const char *) 0, (const char *) 0, 0, 0, 0 } }; #undef NHOPT_PARSE @@ -86,6 +94,16 @@ enum optn_result { enum requests { do_nothing, do_init, do_set, do_handler, get_val, get_cnf_val }; +/* these aren't the same as set_xxx in optlist.h */ +enum option_phases { + builtin_opt=1,/* compiled-in default value of an option */ + syscf_opt, /* sysconf setting of an option, overrides builtin */ + rc_file_opt, /* player's run-time config file setting, overrides syscf */ + environ_opt, /* player's environment NETHACKOPTIONS, overrides rc_file */ + cmdline_opt, /* program invocation command-line, overrides environ */ + play_opt, /* 'O' command, interactively set so overrides all */ + num_opt_phases +}; static struct allopt_t allopt[SIZE(allopt_init)]; @@ -106,7 +124,6 @@ extern char ttycolors[CLR_MAX]; /* in sys/msdos/video.c */ static char empty_optstr[] = { '\0' }; static boolean duplicate, using_alias; static boolean give_opt_msg = TRUE; -static boolean restricted_options_mode = FALSE; enum { MAX_ROLEOPT = 4 }; /* 4: role,race,gend,algn */ static boolean opt_set_in_config[OPTCOUNT]; @@ -340,7 +357,6 @@ static const menu_cmd_t default_menu_cmd_info[] = { static const char n_currently_set[] = "(%d currently set)"; -staticfn void allopt_array_init(void); staticfn void nmcpy(char *, const char *, int); staticfn void escapes(const char *, char *); staticfn void rejectoption(const char *); @@ -579,7 +595,7 @@ parseoptions( * placed that number into each option's allopt[n].minmatch. * */ - if (!got_match && allopt[i].name) + if (!got_match) got_match = match_optname(opts, allopt[i].name, allopt[i].minmatch, TRUE); if (got_match) { @@ -618,8 +634,7 @@ parseoptions( /* allow optfn's to test whether they were called from parseoptions() */ program_state.in_parseoptions++; - if (got_match && (matchidx >= 0 && matchidx < OPTCOUNT) - && !allopt[matchidx].disregarded) { + if (got_match && matchidx >= 0) { duplicate = duplicate_opt_detection(matchidx); if (duplicate && !allopt[matchidx].dupeok) complain_about_duplicate(matchidx); @@ -669,7 +684,7 @@ parseoptions( } } - if (optresult == optn_silenterr || restricted_options_mode) + if (optresult == optn_silenterr) return FALSE; if (pfx_match && optresult == optn_err) { char pfxbuf[BUFSZ], *pfxp; @@ -4956,20 +4971,17 @@ optfn_windowtype( * _end_ because comma-separated option strings are processed from * right to left. */ - if (!iflags.window_inited) { - if (iflags.windowtype_locked) - return optn_ok; + if (iflags.windowtype_locked) + return optn_ok; - if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE)) - != empty_optstr) { - nmcpy(gc.chosen_windowtype, op, WINTYPELEN); - if (!iflags.windowtype_deferred) { - choose_windows(gc.chosen_windowtype); - } - } else { - return optn_err; + if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE)) + != empty_optstr) { + nmcpy(gc.chosen_windowtype, op, WINTYPELEN); + if (!iflags.windowtype_deferred) { + choose_windows(gc.chosen_windowtype); } - } + } else + return optn_err; return optn_ok; } if (req == get_val || req == get_cnf_val) { @@ -7054,14 +7066,29 @@ txt2key(char *txt) void initoptions(void) { + int i; + /* * Most places that call initoptions_init()/initoptions() would * have the calls next to each other, so instead of adding * initoptions_init() everywhere, just add it where it's needed in * a non-adjacent place and call it here for all the other cases. */ - if (go.opt_phase != builtin_opt) + if(go.opt_phase != builtin_opt) initoptions_init(); + + /* + * Call each option function with an init flag and give it a chance + * to make any preparations that it might require. We do this + * whether or not the option itself is ever specified; that's + * irrelevant for the init call. Doing this allows the prep code for + * option settings to remain adjacent to, and in the same function as, + * the code that processes those options. + */ + for (i = 0; i < OPTCOUNT; ++i) { + if (allopt[i].optfn) + (*allopt[i].optfn)(i, do_init, FALSE, empty_optstr, empty_optstr); + } #ifdef SYSCF /* someday there may be other SYSCF alternatives besides text file */ #ifdef SYSCF_FILE @@ -7103,7 +7130,9 @@ initoptions_init(void) go.opt_phase = builtin_opt; /* Did I need to move this here? */ /* initialize the function pointers for saving the game */ sf_init(); - allopt_array_init(); + memcpy(allopt, allopt_init, sizeof(allopt)); + determine_ambiguities(); + /* if windowtype has been specified on the command line, set it up early so windowtype-specific options use it as their base */ if (gc.cmdline_windowsys) { @@ -7257,28 +7286,6 @@ initoptions_init(void) /* since this is done before init_objects(), do partial init here */ objects[SLIME_MOLD].oc_name_idx = SLIME_MOLD; nmcpy(svp.pl_fruit, OBJ_NAME(objects[SLIME_MOLD]), PL_FSIZ); - -#ifdef SYSCF -/* someday there may be other SYSCF alternatives besides text file */ -#ifdef SYSCF_FILE - /* If SYSCF_FILE is specified, it _must_ exist... */ - assure_syscf_file(); - config_error_init(TRUE, SYSCF_FILE, FALSE); - - /* ... and _must_ parse correctly. */ - go.opt_phase = syscf_opt; - if (!read_config_file(SYSCF_FILE, set_in_sysconf)) { - if (config_error_done() && !iflags.initoptions_noterminate) - nh_terminate(EXIT_FAILURE); - } - config_error_done(); - /* - * TODO [maybe]: parse the sysopt entries which are space-separated - * lists of usernames into arrays with one name per element. - */ -#endif -#endif /* SYSCF */ - initoptions_finish(); } /* @@ -7299,69 +7306,72 @@ initoptions_init(void) */ void initoptions_finish(void) -{ nhsym sym = 0; +{ + nhsym sym = 0; + char *opts = 0, *xtraopts = 0; +#ifndef MAC + const char *envname, *namesrc, *nameval; - rcfile(); - - (void) fruitadd(svp.pl_fruit, (struct fruit *) 0); - /* - * Remove "slime mold" from list of object names. This will - * prevent it from being wished unless it's actually present - * as a named (or default) fruit. Wishing for "fruit" will - * result in the player's preferred fruit. [Once upon a time - * the override value used was "\033" which prevented wishing - * for the slime mold object at all except by asking for a - * specific named fruit.] Note that there are multiple fruit - * object types (apple, melon, &c) but the "fruit" object is - * slime mold or whatever custom name player assigns to that. - */ - obj_descr[SLIME_MOLD].oc_name = "fruit"; - - sym = get_othersym(SYM_BOULDER, - Is_rogue_level(&u.uz) ? ROGUESET : PRIMARYSET); - if (sym) - gs.showsyms[SYM_BOULDER + SYM_OFF_X] = sym; - reglyph_darkroom(); - reset_glyphmap(gm_optionchange); -#ifdef STATUS_HILITES - /* - * A multi-interface binary might only support status highlighting - * for some of the interfaces; check whether we asked for it but are - * using one which doesn't. - * - * Option processing can take place before a user-decided WindowPort - * is even initialized, so check for that too. - */ - if (!WINDOWPORT(safestartup)) { - if (iflags.hilite_delta && !wc2_supported("statushilites")) { - raw_printf("Status highlighting not supported for %s interface.", - windowprocs.name); - iflags.hilite_delta = 0; - } + /* getenv() instead of nhgetenv(): let total length of options be long; + parseoptions() will check each individually */ + envname = "NETHACKOPTIONS"; + opts = getenv(envname); + if (!opts) { + /* fall back to original name; discouraged */ + envname = "HACKOPTIONS"; + opts = getenv(envname); } -#endif - update_rest_on_space(); - /* these can't rely on compile-time initialization for their defaults - because a multi-interface binary might need different values for - different interfaces; if neither tiled_map nor ascii_map pass the - wc_supported() test, assume ascii_map */ - if (iflags.wc_tiled_map && !wc_supported("tiled_map")) - iflags.wc_tiled_map = FALSE, iflags.wc_ascii_map = TRUE; - else if (iflags.wc_ascii_map && !wc_supported("ascii_map") - && wc_supported("tiled_map")) - iflags.wc_ascii_map = FALSE, iflags.wc_tiled_map = TRUE; + if (gc.cmdline_rcfile) { + namesrc = "command line"; + nameval = gc.cmdline_rcfile; + xtraopts = opts; + if (opts && (*opts == '/' || *opts == '\\' || *opts == '@')) + xtraopts = 0; /* NETHACKOPTIONS is a file name; ignore it */ + } else if (opts && (*opts == '/' || *opts == '\\' || *opts == '@')) { + /* NETHACKOPTIONS is a file name; use that instead of the default */ + if (*opts == '@') + ++opts; /* @filename */ + namesrc = envname; + nameval = opts; + xtraopts = 0; + } else +#endif /* !MAC */ + /*else*/ { + /* either no NETHACKOPTIONS or it wasn't a file name; + read the default configuration file */ + nameval = namesrc = 0; + xtraopts = opts; + } -#ifdef ENHANCED_SYMBOLS - if (glyphid_cache_status()) - free_glyphid_cache(); - apply_customizations(gc.currentgraphics, do_custom_symbols); -#endif - go.opt_initial = FALSE; - return; -} + /* seemingly arbitrary name length restriction is to prevent error + messages, if any were to be delivered while accessing the file, + from potentially overflowing buffers */ + if (nameval && (int) strlen(nameval) >= BUFSZ / 2) { + go.opt_phase = rc_file_opt; + config_error_init(TRUE, namesrc, FALSE); + config_error_add( + "nethackrc file name \"%.40s\"... too long; using default", + nameval); + config_error_done(); + nameval = namesrc = 0; /* revert to default nethackrc */ + } + + config_error_init(TRUE, nameval, nameval ? CONFIG_ERROR_SECURE : FALSE); + (void) read_config_file(nameval, set_in_config); + config_error_done(); + if (xtraopts) { + /* NETHACKOPTIONS is present and not a file name */ + go.opt_phase = environ_opt; + config_error_init(FALSE, envname, FALSE); + (void) parseoptions(xtraopts, TRUE, FALSE); + config_error_done(); + } + + if (gc.cmdline_rcfile) + free((genericptr_t) gc.cmdline_rcfile), gc.cmdline_rcfile = 0; + /*[end of nethackrc handling]*/ -#if 0 (void) fruitadd(svp.pl_fruit, (struct fruit *) 0); /* * Remove "slime mold" from list of object names. This will @@ -7439,37 +7449,6 @@ initoptions_finish(void) } return; } -#endif -void -allopt_array_init(void) -{ - int i; - static boolean options_array_inited_already = FALSE; - - if (!options_array_inited_already) { - memcpy(allopt, allopt_init, sizeof(allopt)); - determine_ambiguities(); - for (i = 0; allopt[i].name; i++) { - if (allopt[i].addr) - *(allopt[i].addr) = allopt[i].initval; - } - set_all_options_heeded(); - /* - * Call each option function with an init flag and give it a chance - * to make any preparations that it might require. We do this - * whether or not the option itself is ever specified; that's - * irrelevant for the init call. Doing this allows the prep code for - * option settings to remain adjacent to, and in the same function as, - * the code that processes those options. - */ - for (i = 0; i < OPTCOUNT; ++i) { - if (allopt[i].optfn) - (*allopt[i].optfn)(i, do_init, FALSE, empty_optstr, - empty_optstr); - } - options_array_inited_already = TRUE; - } -} /* ******************************************* @@ -8987,7 +8966,6 @@ doset(void) /* changing options via menu by Per Liboriussen */ if (opt_indx < -1) opt_indx++; /* -1 offset for select_menu() */ opt_indx -= indexoffset; - assert(IndexOk(opt_indx, allopt)); if (allopt[opt_indx].opttyp == BoolOpt) { /* boolean option */ Sprintf(buf, "%s%s", *allopt[opt_indx].addr ? "!" : "", @@ -10242,41 +10220,6 @@ enhance_menu_text( return; } -void -set_all_options_heeded(void) -{ - int i; - - for (i = 0; i < OPTCOUNT; i++) - allopt[i].disregarded = FALSE; - restricted_options_mode = FALSE; -} - -void -set_all_options_disregarded(void) -{ - int i; - - for (i = 0; i < OPTCOUNT ; i++) - allopt[i].disregarded = TRUE; - restricted_options_mode = TRUE; -} - -void -heed_this_option(enum opt optidx) -{ - if (optidx >= 0 && optidx < (enum opt) OPTCOUNT) - allopt[optidx].disregarded = FALSE; -} -void -disregard_this_option(enum opt optidx) -{ - if (optidx >= 0 && optidx < (enum opt) OPTCOUNT) - allopt[optidx].disregarded = TRUE; - if (!restricted_options_mode) - restricted_options_mode = TRUE; -} - #undef OPTIONS_HEADING #undef CONFIG_SLOT diff --git a/sys/windows/GNUmakefile b/sys/windows/GNUmakefile index 4bc3df970..3ea5c777b 100644 --- a/sys/windows/GNUmakefile +++ b/sys/windows/GNUmakefile @@ -1239,14 +1239,14 @@ COREOBJS = $(addsuffix .o, allmain alloc apply artifact attrib ball bones botl \ nhlobj nhlsel nhlua windsound o_init objects objnam options \ pager pickup pline polyself potion pray priest quest questpgr \ random read rect region report restore rip rnd role rumors \ - save sfbase sfstruct shk shknam sit selvar sounds sp_lev \ + safeproc save sfbase sfstruct shk shknam sit selvar sounds sp_lev \ spell stairs steal steed strutil \ symbols sys teleport timeout topten track trap u_init uhitm utf8map \ vault version vision weapon were wield windmain windows windsys wizard \ wizcmds worm worn write zap $(SOUNDLIBOBJS)) -CFLAGSW = $(CFLAGS) $(CFLAGSXTRA) $(SOUNDLIBINCL) $(COMMONDEF) $(DLBFLG) -DTILES -D_WINDOWS -DMSWIN_GRAPHICS -DNOTTYGRAPHICS $(SOUNDLIBDEFS) -CPPFLAGSW = $(CFLAGS) $(CPPFLAGSXTRA) $(COMMONDEF) $(DLBFLG) -DTILES -D_WINDOWS -DMSWIN_GRAPHICS -DNOTTYGRAPHICS $(SOUNDLIBDEFS) +CFLAGSW = $(CFLAGS) $(CFLAGSXTRA) $(SOUNDLIBINCL) $(COMMONDEF) $(DLBFLG) -DTILES -D_WINDOWS -DMSWIN_GRAPHICS -DSAFEPROCS -DNOTTYGRAPHICS $(SOUNDLIBDEFS) +CPPFLAGSW = $(CFLAGS) $(CPPFLAGSXTRA) $(COMMONDEF) $(DLBFLG) -DTILES -D_WINDOWS -DMSWIN_GRAPHICS -DSAFEPROCS -DNOTTYGRAPHICS $(SOUNDLIBDEFS) ONHW = $(O)nethackw NHWONLY = $(addsuffix .o, mhaskyn mhdlg mhfont mhinput mhmain mhmap mhmenu \ @@ -1348,8 +1348,8 @@ CLEAN_FILE += $(NHWTARGETS) $(NHWOBJS) $(NHWRES) $(BMPS) $(WAV) #========================================== # nethack #========================================== -CFLAGSNH = $(CFLAGSU) $(CFLAGSXTRA) $(SOUNDLIBINCL) -DNO_TILE_C -D_LIB -DWIN32CON $(SOUNDLIBDEFS) -CPPFLAGSNH = $(CFLAGSU) $(CPPFLAGSXTRA) -DNO_TILE_C -D_LIB -DWIN32CON $(SOUNDLIBDEFS) +CFLAGSNH = $(CFLAGSU) $(CFLAGSXTRA) $(SOUNDLIBINCL) -DNO_TILE_C -DSAFEPROCS -D_LIB -DWIN32CON $(SOUNDLIBDEFS) +CPPFLAGSNH = $(CFLAGSU) $(CPPFLAGSXTRA) -DNO_TILE_C -DSAFEPROCS -D_LIB -DWIN32CON $(SOUNDLIBDEFS) ONH = $(O)nethack diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index fe481ecc2..2097d7183 100644 --- a/sys/windows/Makefile.nmake +++ b/sys/windows/Makefile.nmake @@ -847,7 +847,8 @@ REGEXTTY = $(OTTY)cppregex.o LUAOBJTTY = $(OTTY)nhlua.o $(OTTY)nhlsel.o $(OTTY)nhlobj.o VVOBJTTY = $(OTTY)version.o -SOBJTTY = $(OTTY)windmain.o $(OTTY)windsys.o $(OTTY)win10.o +SOBJTTY = $(OTTY)windmain.o $(OTTY)windsys.o $(OTTY)win10.o \ + $(OTTY)safeproc.o TTYOBJTTY = $(OTTY)topl.o $(OTTY)getline.o $(OTTY)wintty.o @@ -907,7 +908,8 @@ REGEXGUI = $(OGUI)cppregex.o LUAOBJGUI = $(OGUI)nhlua.o $(OGUI)nhlsel.o $(OGUI)nhlobj.o VVOBJGUI = $(OGUI)version.o -SOBJGUI = $(OGUI)windmain.o $(OGUI)windsys.o $(OGUI)win10.o +SOBJGUI = $(OGUI)windmain.o $(OGUI)windsys.o $(OGUI)win10.o \ + $(OGUI)safeproc.o GUIOBJ = $(OGUI)mhaskyn.o $(OGUI)mhdlg.o \ $(OGUI)mhfont.o $(OGUI)mhinput.o $(OGUI)mhmain.o $(OGUI)mhmap.o \ @@ -1362,8 +1364,8 @@ INCLUSIONS= /I$(R_INCL) /I$(R_MSWSYS) $(R_LUAINCL) $(R_SOUNDLIBINCL) # Util and console builds #========================================== -CFLAGS = $(ctmpflags) $(INCLUSIONS) $(DLBDEF) $(SOUNDLIBDEFS) -CPPFLAGS = $(cpptmpflags) $(INCLUSIONS) $(DLBDEF) $(SOUNDLIBDEFS) +CFLAGS = $(ctmpflags) $(INCLUSIONS) $(DLBDEF) -DSAFEPROCS $(SOUNDLIBDEFS) +CPPFLAGS = $(cpptmpflags) $(INCLUSIONS) $(DLBDEF) -DSAFEPROCS $(SOUNDLIBDEFS) LFLAGS = $(lflags) $(conlibs) $(MACHINE) #========================================== @@ -2388,12 +2390,14 @@ $(OTTY)windsys.o: $(MSWSYS)windsys.c $(WINDHDR) $(HACK_H) $(OTTY)windsound.o: $(WINDSOUNDDIR)windsound.c $(HACK_H) #$(OTTY)sample.o: $(SOUNDDIR)sample.c $(HACK_H) $(OTTY)windmain.o: $(MSWSYS)windmain.c $(WINDHDR) $(HACK_H) +$(OTTY)safeproc.o: $(WSHR)safeproc.c $(WINDHDR) $(HACK_H) $(OGUI)consoletty.o: $(MSWSYS)consoletty.c $(WINDHDR) $(HACK_H) $(TILE_H) $(OGUI)win10.o: $(MSWSYS)win10.c $(WINDHDR) $(HACK_H) $(OGUI)windsys.o: $(MSWSYS)windsys.c $(WINDHDR) $(HACK_H) $(OGUI)windsound.o: $(WINDSOUNDDIR)windsound.c $(HACK_H) $(OGUI)windmain.o: $(MSWSYS)windmain.c $(WINDHDR) $(HACK_H) +$(OGUI)safeproc.o: $(WSHR)safeproc.c $(WINDHDR) $(HACK_H) #=================================================================== # win/win32 dependencies diff --git a/sys/windows/consoletty.c b/sys/windows/consoletty.c index bceb98132..34489430f 100644 --- a/sys/windows/consoletty.c +++ b/sys/windows/consoletty.c @@ -156,6 +156,7 @@ static boolean check_font_widths(void); #endif static void set_known_good_console_font(void); static void restore_original_console_font(void); +extern void safe_routines(void); void tty_ibmgraphics_fixup(void); #ifdef VIRTUAL_TERMINAL_SEQUENCES extern void (*ibmgraphics_mode_callback)(void); /* symbols.c */ @@ -1188,6 +1189,8 @@ consoletty_open(int mode UNUSED) void consoletty_exit(void) { + /* go back to using the safe routines */ + safe_routines(); free_custom_colors(); free((genericptr_t) console.front_buffer); free((genericptr_t) console.back_buffer); @@ -2543,13 +2546,9 @@ void early_raw_print(const char *s) void nethack_enter_consoletty(void) { - int width; #ifdef VIRTUAL_TERMINAL_SEQUENCES char buf[BUFSZ], *bp, *localestr; BOOL apisuccess; -// DWORD unused; -// int i = 0; - #endif /* VIRTUAL_TERMINAL_SEQUENCES */ #if 0 /* set up state needed by early_raw_print() */ @@ -2563,38 +2562,13 @@ void nethack_enter_consoletty(void) GetWindowLong(console.hWnd, GWL_STYLE) & ~WS_MAXIMIZEBOX & ~WS_SIZEBOX); #endif - - CONSOLE_SCREEN_BUFFER_INFO csbi; - if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) { - /* srWindow identifies the visible area; dwSize identifies the buffer - */ - width = csbi.srWindow.Right - csbi.srWindow.Left + 1; - fprintf(stdout, "width = %d\n", width); - } - console.hConOut = GetStdHandle(STD_OUTPUT_HANDLE); nhassert(console.hConOut != NULL); // NOTE: this assert will not print GetConsoleScreenBufferInfo(console.hConOut, &console.orig_csbi); - //COORD screencheck = GetLargestConsoleWindowSize(console.hConOut); - - GetConsoleMode(console.hConOut, &console.orig_out_cmode); - console.out_cmode = console.orig_out_cmode; - console.out_cmode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; - SetConsoleMode(console.hConOut, console.out_cmode); -#if 0 - /* tests */ - WriteConsoleA(console.hConOut, "\033[8;;133t",9, &unused, NULL); - for (i = 0; i < 13; ++i) { - WriteConsoleA(console.hConOut, "0123456789", 10, &unused, NULL); - } - WriteConsoleA(console.hConOut, "\033[3;133ftest", 12, &unused, NULL); - GetConsoleScreenBufferInfo(console.hConOut, &console.orig_csbi); /* Testing of widths != COLNO has not turned up any problems. Need * to do a bit more testing and then we are likely to enable having * console width match window width. */ -#endif - #if 0 console.width = console.orig_csbi.srWindow.Right - console.orig_csbi.srWindow.Left + 1; diff --git a/sys/windows/vs/NetHack/NetHack.vcxproj b/sys/windows/vs/NetHack/NetHack.vcxproj index c6f2df87c..131a4cf05 100644 --- a/sys/windows/vs/NetHack/NetHack.vcxproj +++ b/sys/windows/vs/NetHack/NetHack.vcxproj @@ -74,7 +74,7 @@ Speed true $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);%(AdditionalIncludeDirectories) - WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;SND_SOUNDEFFECTS_AUTOMAP;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) + WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;SND_SOUNDEFFECTS_AUTOMAP;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) stdclatest @@ -218,6 +218,7 @@ + diff --git a/sys/windows/vs/NetHackW/NetHackW.vcxproj b/sys/windows/vs/NetHackW/NetHackW.vcxproj index cc3ec2563..fce2d2a1d 100644 --- a/sys/windows/vs/NetHackW/NetHackW.vcxproj +++ b/sys/windows/vs/NetHackW/NetHackW.vcxproj @@ -87,7 +87,7 @@ Disabled true $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);%(AdditionalIncludeDirectories) - TILES;_WINDOWS;DLB;MSWIN_GRAPHICS;NOTTYGRAPHICS;SND_LIB_WINDSOUND;USER_SOUNDS;SND_SOUNDEFFECTS_AUTOMAP;HAS_STDINT_H;HAS_INTTYPES_H;PDC_WIDE;%(PreprocessorDefinitions) + TILES;_WINDOWS;DLB;MSWIN_GRAPHICS;SAFEPROCS;NOTTYGRAPHICS;SND_LIB_WINDSOUND;USER_SOUNDS;SND_SOUNDEFFECTS_AUTOMAP;HAS_STDINT_H;HAS_INTTYPES_H;PDC_WIDE;%(PreprocessorDefinitions) stdclatest stdclatest stdclatest @@ -263,6 +263,7 @@ + diff --git a/sys/windows/vs/sfctool/sfctool.vcxproj b/sys/windows/vs/sfctool/sfctool.vcxproj index ad1971969..5dff90066 100644 --- a/sys/windows/vs/sfctool/sfctool.vcxproj +++ b/sys/windows/vs/sfctool/sfctool.vcxproj @@ -150,7 +150,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 stdclatest true @@ -166,7 +166,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_NDEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_NDEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 stdclatest true @@ -186,7 +186,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 stdclatest true @@ -202,7 +202,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 stdclatest true @@ -218,7 +218,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_NDEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_NDEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 stdclatest true @@ -238,7 +238,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_NDEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_NDEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 stdclatest true @@ -258,4 +258,4 @@ - + \ No newline at end of file diff --git a/sys/windows/vs/sftags/sftags.vcxproj b/sys/windows/vs/sftags/sftags.vcxproj index 52c63d47a..43f024c24 100644 --- a/sys/windows/vs/sftags/sftags.vcxproj +++ b/sys/windows/vs/sftags/sftags.vcxproj @@ -110,7 +110,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 true true @@ -125,7 +125,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 true true @@ -144,7 +144,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 true true @@ -159,7 +159,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 true true @@ -174,7 +174,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 true true @@ -194,7 +194,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 true true @@ -223,4 +223,4 @@ - + \ No newline at end of file diff --git a/sys/windows/windmain.c b/sys/windows/windmain.c index c50a6d292..9e34152ac 100644 --- a/sys/windows/windmain.c +++ b/sys/windows/windmain.c @@ -14,6 +14,10 @@ #include #include +#if !defined(SAFEPROCS) +#error You must #define SAFEPROCS to build windmain.c +#endif + static void nhusage(void); char *exename(void); boolean fakeconsole(void); @@ -63,6 +67,7 @@ char windows_yn_function(const char *, const char *, char); #ifdef WIN32CON extern int windows_console_custom_nhgetch(void); +void safe_routines(void); int tty_self_recover_prompt(void); #endif @@ -101,13 +106,6 @@ void update_file(const char *, const char *, const char *, const char *, BOOL); void windows_raw_print_bold(const char *); -staticfn void set_emergency_io(void); -staticfn void stdio_wait_synch(void); -staticfn void stdio_raw_print(const char *str); -staticfn void stdio_nonl_raw_print(const char *str); -staticfn void stdio_raw_print_bold(const char *str); -staticfn int stdio_nhgetch(void); - #ifdef PORT_HELP void port_help(void); #endif @@ -161,9 +159,18 @@ MAIN(int argc, char *argv[]) char *dir = NULL; #ifdef _MSC_VER - _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); + _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); #endif +#ifdef WIN32CON + /* + * Get a set of valid safe windowport function + * pointers during early startup initialization. + */ +// safe_routines(); +#endif /* WIN32CON */ + + /* setting iflags.colorcount has to be after early_init() * because it zeros out all of iflags */ hwnd = GetDesktopWindow(); @@ -201,9 +208,8 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ #ifndef MSWIN_GRAPHICS early_init(argc, argv); /* already in WinMain for MSWIN_GRAPHICS */ #endif - set_default_prefix_locations( - argv[0]); /* must be re-done after initoptions_init() - * which clears out gp.fqn_prefix[] */ + set_default_prefix_locations(argv[0]); /* must be re-done after initoptions_init() + * which clears out gp.fqn_prefix[] */ copy_sysconf_content(); copy_symbols_content(); /* Now that sysconf has had a chance to set the TROUBLEPREFIX, don't @@ -211,20 +217,13 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ fqn_prefix_locked[TROUBLEPREFIX] = TRUE; copy_config_content(); - // if (iflags.windowtype_deferred && gc.chosen_windowtype[0]) - // windowtype = gc.chosen_windowtype; - // windowtype = gc.chosen_windowtype; + // if (iflags.windowtype_deferred && gc.chosen_windowtype[0]) + // windowtype = gc.chosen_windowtype; + // windowtype = gc.chosen_windowtype; #if !defined(MSWIN_GRAPHICS) - nethack_enter_consoletty(); consoletty_open(1); -#endif - set_emergency_io(); - -#ifdef EARLY_CONFIGFILE_PASS - rcfile_interface_options(); - if (gc.chosen_windowtype && *gc.chosen_windowtype) - windowtype = gc.chosen_windowtype; + nethack_enter_consoletty(); #endif if (!windowtype) { @@ -234,10 +233,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ windowtype = "tty"; #endif } - choose_windows( - windowtype); /* sets all the window port function pointers */ - - init_nhwindows(&argc, argv); + choose_windows(windowtype); /* sets all the window port function pointers */ #if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) /* Save current directory and make sure it gets restored when @@ -247,9 +243,8 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ error("NetHack: current directory path too long"); #endif initoptions_init(); // This allows OPTIONS in syscf on Windows. - set_default_prefix_locations( - argv[0]); /* must be re-done after initoptions_init() - * which clears out gp.fqn_prefix[] */ + set_default_prefix_locations(argv[0]); /* must be re-done after initoptions_init() + * which clears out gp.fqn_prefix[] */ iflags.windowtype_deferred = TRUE; program_state.early_options = 1; @@ -291,6 +286,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ && (strstri(argv[0], "nethackw.exe") || GUILaunched)) iflags.windowtype_locked = TRUE; #endif + windowtype = default_window_sys; #ifdef DLB if (!dlb_init()) { @@ -305,6 +301,18 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ } #endif + if (!iflags.windowtype_locked) { +#if defined(TTY_GRAPHICS) + Strcpy(default_window_sys, "tty"); +#else +#if defined(CURSES_GRAPHICS) && !defined(MSWIN_GRAPHICS) + Strcpy(default_window_sys, "curses"); +#endif /* CURSES */ +#endif /* TTY */ + // if (iflags.windowtype_deferred && gc.chosen_windowtype[0]) + // windowtype = gc.chosen_windowtype; + } + // choose_windows(windowtype); #if defined(SND_LIB_FMOD) assign_soundlib(soundlib_fmod); #elif defined(SND_LIB_WINDSOUND) @@ -314,14 +322,19 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ u.uhp = 1; /* prevent RIP on early quits */ u.ux = 0; /* prevent flush_screen() */ + nethack_enter(argc, argv); iflags.use_background_glyph = FALSE; if (WINDOWPORT(mswin)) iflags.use_background_glyph = TRUE; +#ifdef WIN32CON + if (WINDOWPORT(tty)) + consoletty_open(1); +#endif #ifdef WINCHAIN commit_windowchain(); #endif -// init_nhwindows(&argc, argv); + init_nhwindows(&argc, argv); #ifdef WIN32CON if (WINDOWPORT(tty)) @@ -824,6 +837,20 @@ copy_hack_content(void) gf.fqn_prefix[DATAPREFIX], OPTIONFILE, FALSE); } +#ifdef WIN32CON +void +safe_routines(void) +{ + /* + * Get a set of valid safe windowport function + * pointers during early startup initialization. + */ + if (!WINDOWPORT(safestartup)) + windowprocs = *get_safe_procs(1); + if (!GUILaunched) + windowprocs.win_nhgetch = windows_console_custom_nhgetch; +} +#endif #ifdef PORT_HELP void @@ -1178,7 +1205,9 @@ tty_self_recover_prompt(void) c = 'n'; ct = 0; saved_procs = windowprocs; - + if (!WINDOWPORT(safestartup)) + windowprocs = *get_safe_procs(2); /* arg 2 uses no-newline variant */ + windowprocs.win_nhgetch = windows_console_custom_nhgetch; raw_print("\n"); raw_print("\n"); raw_print("\n"); @@ -1311,60 +1340,4 @@ chdirx(const char *dir, boolean wr) } #endif /* CHDIR */ -void -set_emergency_io(void) -{ - windowprocs.win_raw_print = stdio_raw_print; - windowprocs.win_raw_print_bold = stdio_raw_print_bold; - windowprocs.win_nhgetch = stdio_nhgetch; - windowprocs.win_wait_synch = stdio_wait_synch; -} - - -/* Add to your code: windowprocs.win_raw_print = stdio_wait_synch; */ -void -stdio_wait_synch(void) -{ - char valid[] = { ' ', '\n', '\r', '\033', '\0' }; - - fprintf(stdout, "--More--"); - (void) fflush(stdout); - while (!strchr(valid, nhgetch())) - ; -} - -/* Add to your code: windowprocs.win_raw_print = stdio_raw_print; */ -void -stdio_raw_print(const char *str) -{ - if (str) - fprintf(stdout, "%s\n", str); - return; -} - -/* no newline variation, add to your code: - windowprocs.win_raw_print = stdio_nonl_raw_print; */ -void -stdio_nonl_raw_print(const char *str) -{ - if (str) - fprintf(stdout, "%s", str); - return; -} - -/* Add to your code: windowprocs.win_raw_print_bold = stdio_raw_print_bold; */ -void -stdio_raw_print_bold(const char *str) -{ - stdio_raw_print(str); - return; -} - -/* Add to your code: windowprocs.win_nhgetch = stdio_nhgetch; */ -int -stdio_nhgetch(void) -{ - return getchar(); -} - - /*windmain.c*/ +/*windmain.c*/ diff --git a/sys/windows/windsys.c b/sys/windows/windsys.c index aca0cfe9e..88f1a69bf 100644 --- a/sys/windows/windsys.c +++ b/sys/windows/windsys.c @@ -73,6 +73,7 @@ static HWND GetConsoleHwnd(void); extern void backsp(void); #endif int windows_console_custom_nhgetch(void); +extern void safe_routines(void); int windows_early_options(const char *window_opt); unsigned long sys_random_seed(void); #if 0 @@ -292,6 +293,10 @@ win32_abort(void) exit_nhwindows((char *) 0); iflags.window_inited = FALSE; } +#ifdef WIN32CON + if (!WINDOWPORT(mswin) && !WINDOWPORT(safestartup)) + safe_routines(); +#endif if (wizard) { raw_print("Execute debug breakpoint wizard?"); if ((c = nhgetch()) == 'y' || c == 'Y') @@ -517,6 +522,15 @@ nethack_exit(int code) * GUILaunched is defined and set in consoletty.c. */ + +#ifdef WIN32CON + if (!GUILaunched) { + windowprocs = *get_safe_procs(1); + /* use our custom version which works + a little cleaner than the stdio one */ + windowprocs.win_nhgetch = windows_console_custom_nhgetch; + } else +#endif if (getreturn_enabled) { raw_print("\n"); if (iflags.window_inited) @@ -566,6 +580,10 @@ getreturn(const char *str) initializing the window port */ void nethack_enter_windows(void) { +#ifdef WIN32CON + if (WINDOWPORT(tty)) + nethack_enter_consoletty(); +#endif } /* CP437 to Unicode mapping according to the Unicode Consortium */ diff --git a/win/share/safeproc.c b/win/share/safeproc.c new file mode 100644 index 000000000..19e9aa9b6 --- /dev/null +++ b/win/share/safeproc.c @@ -0,0 +1,596 @@ +/* NetHack 3.7 safeproc.c */ +/* Copyright (c) Michael Allison, 2018 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* must #define SAFEPROCS in xxxconf.h or via CFLAGS or this won't compile */ +#include "hack.h" + +/* + * *********************************************************** + * This is a complete WindowPort implementation that can be + * assigned to the windowproc function pointers very early + * in the startup initialization, perhaps immediately even. + * It requires only the following call: + * windowprocs = *get_safe_procs(0); + * + * The game startup can trigger functions in other modules + * that make assumptions on a WindowPort being available + * and bad things can happen if any function pointers are + * null at that time. + * + * Some ports prior to 3.6.2 made attempts to early init + * various pieces of one of their WindowPorts, but that + * caused conflicts if that particular WindowPort wasn't + * the one that the user ended up selecting in their + * config file later. The WindowPort interfaced was designed + * to allow multiple WindowPorts to be linked into the same + * game binary. + * + * The base functions established by a call to get_safe_procs() + * accomplish the goal of preventing crashes, but not much + * else. + * + * There are also a few additional functions provided in here + * that can be selected optionally to provide some startup + * functionality for getting messages out to the user about + * issues that are being experienced during startup in + * general or during options parsing. The ones in here are + * deliberately free from any platforms or OS specific code. + * Please leave them using stdio C routines as much as + * possible. That isn't to say you can't do fancier functions + * prior to initialization of the primary WindowPort, but you + * can provide those platform-specific functions elsewhere, + * and assign them the same way that these more generic versions + * are assigned. + * + * The additional platform-independent, but more functional + * routines provided in here should be assigned after the + * windowprocs = *get_safe_procs(n) + * call. + * + * Usage: + * + * windowprocs = *get_safe_procs(0); + * initializes a set of winprocs function pointers that ensure + * none of the function pointers are left null, but that's all + * it does. + * + * windowprocs = *get_safe_procs(1); + * initializes a set of winprocs functions pointers that ensure + * none of the function pointers are left null, but also + * provides some basic output and input functionality using + * nothing other than C stdio routines (no platform-specific + * or OS-specific code). + * + * *********************************************************** + */ + +void safe_dismiss_nhwindow(winid); +void safe_putstr(winid, int, const char *); +void win_safe_init(int); +void safe_number_pad(int); + +struct window_procs safe_procs = { + WPID(safestartup), + (0 +#ifdef TTY_PERM_INVENT + | WC_PERM_INVENT +#endif +#ifdef MSDOS + | WC_TILED_MAP | WC_ASCII_MAP +#endif +#if defined(WIN32CON) + | WC_MOUSE_SUPPORT +#endif + | WC_COLOR | WC_HILITE_PET | WC_INVERSE | WC_EIGHT_BIT_IN), + (0 +#if defined(SELECTSAVED) + | WC2_SELECTSAVED +#endif +#if defined(STATUS_HILITES) + | WC2_HILITE_STATUS | WC2_HITPOINTBAR | WC2_FLUSH_STATUS + | WC2_RESET_STATUS +#endif + | WC2_DARKGRAY | WC2_SUPPRESS_HIST | WC2_URGENT_MESG | WC2_STATUSLINES + | WC2_U_UTF8STR | WC2_PETATTR +#if !defined(NO_TERMS) || defined(WIN32CON) + | WC2_EXTRACOLORS +#endif + ), + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* color availability */ + safe_init_nhwindows, safe_player_selection, safe_askname, + safe_get_nh_event, + safe_exit_nhwindows, safe_suspend_nhwindows, safe_resume_nhwindows, + safe_create_nhwindow, safe_clear_nhwindow, safe_display_nhwindow, + safe_destroy_nhwindow, safe_curs, safe_putstr, safe_putmixed, + safe_display_file, safe_start_menu, safe_add_menu, safe_end_menu, + safe_select_menu, safe_message_menu, + safe_mark_synch, + safe_wait_synch, +#ifdef CLIPPING + safe_cliparound, +#endif +#ifdef POSITIONBAR + safe_update_positionbar, +#endif + safe_print_glyph, safe_raw_print, safe_raw_print_bold, safe_nhgetch, + safe_nh_poskey, safe_nhbell, safe_doprev_message, safe_yn_function, + safe_getlin, safe_get_ext_cmd, safe_number_pad, safe_delay_output, +#ifdef CHANGE_COLOR /* the Mac uses a palette device */ + safe_change_color, +#ifdef MAC + safe_change_background, set_safe_font_name, +#endif + safe_get_color_string, +#endif + safe_outrip, + safe_preference_update, + safe_getmsghistory, safe_putmsghistory, + safe_status_init, + safe_status_finish, safe_status_enablefield, + safe_status_update, + safe_can_suspend, + safe_update_inventory, + safe_ctrl_nhwindow, +}; + +struct window_procs * +get_safe_procs(int optn) +{ + if (optn) { + /* include the slightly more functional stdc versions */ + safe_procs.win_raw_print = stdio_raw_print; + safe_procs.win_raw_print_bold = stdio_raw_print_bold; + safe_procs.win_nhgetch = stdio_nhgetch; + safe_procs.win_wait_synch = stdio_wait_synch; + if (optn == 2) + safe_procs.win_raw_print = stdio_nonl_raw_print; + } + return &safe_procs; +} + +/*ARGSUSED*/ +void +safe_init_nhwindows(int *argcp UNUSED, char **argv UNUSED) +{ + return; +} + +void +safe_player_selection(void) +{ + return; +} + +void +safe_askname(void) +{ + return; +} + +void +safe_get_nh_event(void) +{ + return; +} + +void +safe_suspend_nhwindows(const char *str UNUSED) +{ + return; +} + +void +safe_resume_nhwindows(void) +{ + return; +} + +void +safe_exit_nhwindows(const char *str UNUSED) +{ + return; +} + +winid +safe_create_nhwindow(int type UNUSED) +{ + return WIN_ERR; +} + +void +safe_clear_nhwindow(winid window UNUSED) +{ + return; +} + +/*ARGSUSED*/ +void +safe_display_nhwindow(winid window UNUSED, boolean blocking UNUSED) +{ + return; +} + +void +safe_dismiss_nhwindow(winid window UNUSED) +{ + return; +} + +void +safe_destroy_nhwindow(winid window UNUSED) +{ + return; +} + +void +safe_curs(winid window UNUSED, int x UNUSED, int y UNUSED) +{ + return; +} + +void +safe_putstr(winid window UNUSED, int attr UNUSED, const char *str UNUSED) +{ + return; +} + +void +safe_putmixed(winid window UNUSED, int attr UNUSED, const char *str UNUSED) +{ + return; +} + +void +safe_display_file(const char * fname UNUSED, boolean complain UNUSED) +{ + return; +} + +void +safe_start_menu(winid window UNUSED, unsigned long mbehavior UNUSED) +{ + return; +} + +/*ARGSUSED*/ +/* + * Add a menu item to the beginning of the menu list. This list is reversed + * later. + */ +void +safe_add_menu( + winid window UNUSED, /* window to use, must be of type NHW_MENU */ + const glyph_info *glyphinfo UNUSED, /* glyph plus glyph info */ + const anything *identifier UNUSED, /* what to return if selected */ + char ch UNUSED, /* keyboard accelerator (0 = pick our own) */ + char gch UNUSED, /* group accelerator (0 = no group) */ + int attr UNUSED, /* attribute for string (like safe_putstr()) */ + int clr UNUSED, /* colour for string */ + const char *str UNUSED, /* menu string */ + unsigned int itemflags UNUSED) /* itemflags such as marked as selected */ +{ + return; +} + +/* + * End a menu in this window, window must a type NHW_MENU. + */ +void +safe_end_menu( + winid window UNUSED, /* menu to use */ + const char *prompt UNUSED) /* prompt to for menu */ +{ + return; +} + +int +safe_select_menu( + winid window UNUSED, + int how UNUSED, + menu_item **menu_list UNUSED) +{ + return 0; +} + +/* special hack for treating top line --More-- as a one item menu */ +char +safe_message_menu( + char let UNUSED, + int how UNUSED, + const char *mesg UNUSED) +{ + return '\033'; +} + +void +safe_mark_synch(void) +{ +} + +void +safe_wait_synch(void) +{ +} + +#ifdef CLIPPING +void +safe_cliparound(int x UNUSED, int y UNUSED) +{ +} +#endif /* CLIPPING */ + +/* + * safe_print_glyph + * + * Print the glyph to the output device. Don't flush the output device. + */ +void +safe_print_glyph( + winid window UNUSED, + coordxy x UNUSED, + coordxy y UNUSED, + const glyph_info *glyphinfo UNUSED, + const glyph_info *bkglyphinfo UNUSED) +{ + return; +} + +void +safe_raw_print(const char *str UNUSED) +{ + return; +} + +void +safe_raw_print_bold(const char *str UNUSED) +{ + return; +} + +int +safe_nhgetch(void) +{ + return '\033'; +} + +/* + * return a key, or 0, in which case a mouse button was pressed + * mouse events should be returned as character positions in the map window. + * Since normal tty's don't have mice, just return a key. + */ +/*ARGSUSED*/ +int +safe_nh_poskey(coordxy *x UNUSED, coordxy *y UNUSED, int *mod UNUSED) +{ + return '\033'; +} + +void +win_safe_init(int dir UNUSED) +{ + return; +} + +#ifdef POSITIONBAR +void +safe_update_positionbar(char *posbar UNUSED) +{ + return; +} +#endif /* POSITIONBAR */ + +/* + * safe_status_init() + * -- initialize the port-specific data structures. + */ +void +safe_status_init(void) +{ + return; +} + +boolean +safe_can_suspend(void) +{ + return FALSE; +} + +void +safe_nhbell(void) +{ + return; +} + +int +safe_doprev_message(void) +{ + return 0; +} + +char +safe_yn_function(const char *query UNUSED, + const char *resp UNUSED, char def UNUSED) +{ + return '\033'; +} + +/*ARGSUSED*/ +void +safe_getlin(const char* prompt UNUSED, char *outbuf) +{ + Strcpy(outbuf, "\033"); +} + +int +safe_get_ext_cmd(void) +{ + return '\033'; +} + +void +safe_number_pad(int mode UNUSED) +{ + return; +} + +void +safe_delay_output(void) +{ + return; +} + +void +safe_outrip(winid tmpwin UNUSED, int how UNUSED, time_t when UNUSED) +{ + return; +} + +/*ARGSUSED*/ +void +safe_preference_update(const char *pref UNUSED) +{ + return; +} + +char * +safe_getmsghistory(boolean init UNUSED) +{ + return (char *) 0; +} + +void +safe_putmsghistory( + const char *msg UNUSED, + boolean is_restoring UNUSED) +{ +} + +void +safe_status_finish(void) +{ +} + +void +safe_status_enablefield( + int fieldidx UNUSED, + const char *nm UNUSED, + const char *fmt UNUSED, + boolean enable UNUSED) +{ +} + +/* call once for each field, then call with BL_FLUSH to output the result */ +void +safe_status_update( + int idx UNUSED, + genericptr_t ptr UNUSED, + int chg UNUSED, + int percent UNUSED, + int color UNUSED, + unsigned long *colormasks UNUSED) +{ +} + +void +safe_update_inventory(int arg UNUSED) +{ + return; +} + +#ifdef WIN32CON +extern win_request_info *tty_ctrl_nhwindow(winid window UNUSED, + int request UNUSED, + win_request_info *wri UNUSED); +#endif + +win_request_info * +safe_ctrl_nhwindow( + winid window UNUSED, + int request UNUSED, + win_request_info *wri UNUSED) +{ +#ifdef WIN32CON + return (*tty_ctrl_nhwindow)(window, request, wri); +#else + return (win_request_info *) 0; +#endif +} + +/************************************************************** + * These are some optionally selectable routines that add + * some base functionality over the safe_* versions above. + * The safe_* versions are primarily designed to ensure that + * there are no null function pointers remaining at early + * game startup/initialization time. + * + * The slightly more functional versions in here should be kept + * free of platform-specific code or OS-specific code. If you + * want to use versions that involve platform-specific or + * OS-specific code, go right ahead but use your own replacement + * version of the functions in a platform-specific or + * OS-specific source file, not in here. + ***************************************************************/ + +/* Add to your code: windowprocs.win_raw_print = stdio_wait_synch; */ +void +stdio_wait_synch(void) +{ + char valid[] = {' ', '\n', '\r', '\033', '\0'}; + + fprintf(stdout, "--More--"); + (void) fflush(stdout); + while (!strchr(valid, nhgetch())) + ; +} + +/* Add to your code: windowprocs.win_raw_print = stdio_raw_print; */ +void +stdio_raw_print(const char *str) +{ + if (str) + fprintf(stdout, "%s\n", str); + return; +} + +/* no newline variation, add to your code: + windowprocs.win_raw_print = stdio_nonl_raw_print; */ +void +stdio_nonl_raw_print(const char *str) +{ + if (str) + fprintf(stdout, "%s", str); + return; +} + +/* Add to your code: windowprocs.win_raw_print_bold = stdio_raw_print_bold; */ +void +stdio_raw_print_bold(const char *str) +{ + stdio_raw_print(str); + return; +} + +/* Add to your code: windowprocs.win_nhgetch = stdio_nhgetch; */ +int +stdio_nhgetch(void) +{ + return getchar(); +} + +#ifdef CHANGE_COLOR +void +safe_change_color(int color UNUSED, long rgb UNUSED, int reverse UNUSED) +{ +} + +char * +safe_get_color_string(void) +{ + return (""); +} + + +#endif + +/* safeprocs.c */ diff --git a/win/tty/wintty.c b/win/tty/wintty.c index 4cbe65a2a..61338542b 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -3559,9 +3559,8 @@ assesstty( short *offx, short *offy, long *rows, long *cols, long *maxcol, long *minrow, long *maxrow) { - boolean inuse_only = (invmode & (int) InvInUse) != 0, - show_gold = (invmode & (int) InvShowGold) != 0 - && !inuse_only; + boolean inuse_only = (invmode & InvInUse) != 0, + show_gold = (invmode & InvShowGold) != 0 && !inuse_only; int perminv_minrow = tty_perminv_minrow + (show_gold ? 1 : 0); if (!ttyDisplay) { diff --git a/win/win32/NetHackW.c b/win/win32/NetHackW.c index 56d8bb7c7..92b851c7a 100644 --- a/win/win32/NetHackW.c +++ b/win/win32/NetHackW.c @@ -15,6 +15,10 @@ #include "mhmain.h" #include "mhmap.h" +#if !defined(SAFEPROCS) +#error You must #define SAFEPROCS to build NetHackW.c +#endif + /* Borland and MinGW redefine "boolean" in shlwapi.h, so just use the little bit we need */ typedef struct _DLLVERSIONINFO { @@ -95,6 +99,32 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, UNREFERENCED_PARAMETER(lpCmdLine); UNREFERENCED_PARAMETER(nCmdShow); + /* + * Get a set of valid safe windowport function + * pointers during early startup initialization. + * + * When get_safe_procs is called with 0 as the param, + * non-functional, but safe function pointers are set + * for all windowport routines. + * + * When get_safe_procs is called with 1 as the param, + * raw_print, raw_print_bold, and wait_synch, and nhgetch + * are set to use C stdio routines via stdio_raw_print, + * stdio_raw_print_bold, stdio_wait_synch, and + * stdio_nhgetch. + */ + windowprocs = *get_safe_procs(0); + + /* + * Now we are going to override a couple + * of the windowprocs functions so that + * error messages are handled in a suitable + * way for the graphical version. + */ + windowprocs.win_raw_print = mswin_raw_print; + windowprocs.win_raw_print_bold = mswin_raw_print_bold; + windowprocs.win_wait_synch = mswin_wait_synch; + win10_init(); early_init(0, NULL); /* Change as needed to support CRASHREPORT */ diff --git a/win/win32/mswproc.c b/win/win32/mswproc.c index 55ba2f696..abef776da 100644 --- a/win/win32/mswproc.c +++ b/win/win32/mswproc.c @@ -734,6 +734,9 @@ mswin_exit_nhwindows(const char *str) /* Write Window settings to the registry */ mswin_write_reg(); + /* set things back to failsafes */ + windowprocs = *get_safe_procs(0); + /* and make sure there is still a way to communicate something */ windowprocs.win_raw_print = mswin_raw_print; windowprocs.win_raw_print_bold = mswin_raw_print_bold; From 6234e96b573544abb6ff9912e6c442395f61c4a8 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 5 Apr 2026 13:11:14 -0400 Subject: [PATCH 396/442] Change Windows startup - take 2 remove the safeproc pseudo-windowport routines from almost a decade ago. A very early pass is made through the config file, seeking out just the interface-related OPTIONS=windowport and OPTIONS=soundlib and ignoring all other options in the config file during that early pass, so the windowport can be activated without the NetHack core initialization in place that some of the other rcfile OPTIONS require. Bundles the existing rcfile processing code into rcfile(). New functions to control which rcfile options will be disregarded in the early config file pass, and which will be processed: set_all_options_disregarded(); set_all_options_heeded(); disregard_this_option(opt_xx); heed_this_option(opt_xx); Windows calls rcfile_interface_options(), which is a bundling of a series of function calls to achieve the desired result. void rcfile_interface_options(void) { allopt_array_init(); set_all_options_disregarded(); heed_this_option(opt_windowtype); heed_this_option(opt_soundlib); rcfile(); set_all_options_heeded(); disregard_this_option(opt_windowtype); disregard_this_option(opt_soundlib); } --- include/decl.h | 3 +- include/extern.h | 11 +- include/global.h | 41 +- include/hack.h | 16 - include/optlist.h | 18 +- include/windconf.h | 2 + include/winprocs.h | 75 --- src/cfgfiles.c | 79 +++ src/decl.c | 2 +- src/options.c | 280 ++++++----- sys/windows/GNUmakefile | 10 +- sys/windows/Makefile.nmake | 12 +- sys/windows/consoletty.c | 32 +- sys/windows/vs/NetHack/NetHack.vcxproj | 3 +- sys/windows/vs/NetHackW/NetHackW.vcxproj | 3 +- sys/windows/vs/sfctool/sfctool.vcxproj | 14 +- sys/windows/vs/sftags/sftags.vcxproj | 14 +- sys/windows/windmain.c | 149 +++--- sys/windows/windsys.c | 20 +- win/share/safeproc.c | 596 ----------------------- win/tty/wintty.c | 5 +- win/win32/NetHackW.c | 30 -- win/win32/mswproc.c | 3 - 23 files changed, 455 insertions(+), 963 deletions(-) delete mode 100644 win/share/safeproc.c diff --git a/include/decl.h b/include/decl.h index a34550ef9..f66c0f5a8 100644 --- a/include/decl.h +++ b/include/decl.h @@ -725,7 +725,8 @@ struct instance_globals_o { /* options.c */ - int opt_phase; /* builtin_opt, syscf_, rc_file_, environ_, play_opt */ + /* builtin_opt, syscf_, rc_file_, environ_, play_opt */ + enum option_phases opt_phase; boolean opt_initial; boolean opt_from_file; boolean opt_need_redraw; /* for doset() */ diff --git a/include/extern.h b/include/extern.h index 8ed1b0f0f..8226e9b91 100644 --- a/include/extern.h +++ b/include/extern.h @@ -335,6 +335,8 @@ extern boolean parse_conf_file(FILE *fp, boolean (*proc)(char *arg)); extern void set_configfile_name(const char *); extern char *get_configfile(void); extern const char *get_default_configfile(void); +extern void rcfile(void); +extern void rcfile_interface_options(void); /* ### coloratt.c ### */ @@ -1976,7 +1978,7 @@ extern long filesize(char *); #endif /* MSDOS */ extern char *foundfile_buffer(void); #endif /* __GO32__ */ -extern void chdrive(char *); +extern void chdrive(const char *); #ifndef TOS extern void disable_ctrlP(void); extern void enable_ctrlP(void); @@ -2320,6 +2322,10 @@ extern int msgtype_type(const char *, boolean) NONNULLARG1; extern void hide_unhide_msgtypes(boolean, int); extern void msgtype_free(void); extern void options_free_window_colors(void); +extern void set_all_options_heeded(void); +extern void set_all_options_disregarded(void); +extern void heed_this_option(enum opt); +extern void disregard_this_option(enum opt); #ifdef TTY_PERM_INVENT extern void check_perm_invent_again(void); #endif @@ -2345,6 +2351,7 @@ extern int dowhatdoes(void); extern char *dowhatdoes_core(char, char *) NONNULLARG2; /*might return NULL*/ extern int dohelp(void); extern int dohistory(void); +void allopt_array_init(void); /* ### xxmain.c ### */ @@ -3463,8 +3470,8 @@ extern void append_slash(char *) NONNULLARG1; extern boolean check_user_string(const char *) NONNULLARG1; extern char *get_login_name(void); extern unsigned long sys_random_seed(void); -ATTRNORETURN extern void after_opt_showpaths(const char *) NORETURN; #endif /* UNIX */ +ATTRNORETURN extern void after_opt_showpaths(const char *) NORETURN; /* ### unixtty.c ### */ diff --git a/include/global.h b/include/global.h index 21b00fe46..47e783742 100644 --- a/include/global.h +++ b/include/global.h @@ -102,8 +102,6 @@ typedef unsigned readLenType; #endif #define BOOL_RANDOM (-1) -enum optchoice { opt_in, opt_out}; - /* * type nhsym: loadable symbols go into this type */ @@ -575,6 +573,45 @@ typedef enum NHL_pcall_action { NHLpa_impossible } NHL_pcall_action; +enum optchoice { opt_in, opt_out}; +/* + * option setting restrictions + */ +enum optset_restrictions { + set_in_sysconf = 0, /* system config file option only */ + set_in_config = 1, /* config file option only */ + set_viaprog = 2, /* may be set via extern program, not seen in game */ + set_gameview = 3, /* may be set via extern program, displayed in game */ + set_in_game = 4, /* may be set via extern program or set in the game */ + set_wizonly = 5, /* may be set in the game if wizmode */ + set_wiznofuz = 6, /* wizard-mode only, but not by fuzzer */ + set_hidden = 7 /* placeholder for prefixed entries, never show it */ +}; + +/* these aren't the same as set_xxx */ +enum option_phases { + phase_not_set = 0, + builtin_opt = 1, /* compiled-in default value of an option */ + syscf_opt, /* sysconf setting of an option, overrides builtin */ + rc_file_opt, /* player's run-time config file setting, overrides syscf */ + environ_opt, /* player's environment NETHACKOPTIONS, overrides rc_file */ + cmdline_opt, /* program invocation command-line, overrides environ */ + play_opt, /* 'O' command, interactively set so overrides all */ + num_opt_phases +}; + +#define SET__IS_VALUE_VALID(s) ((s < set_in_sysconf) || (s > set_wiznofuz)) +#include "optlist.h" +enum opt { + opt_prefix_only = -1, +#define NHOPT_ENUM +#include "optlist.h" +#undef NHOPT_ENUM + OPTCOUNT +}; + + + #define SFCTOOL_BIT (1UL << 30) #endif /* GLOBAL_H */ diff --git a/include/hack.h b/include/hack.h index bb9f9d642..f655a8961 100644 --- a/include/hack.h +++ b/include/hack.h @@ -702,22 +702,6 @@ enum nhcb_calls { NUM_NHCB }; -/* - * option setting restrictions - */ - -enum optset_restrictions { - set_in_sysconf = 0, /* system config file option only */ - set_in_config = 1, /* config file option only */ - set_viaprog = 2, /* may be set via extern program, not seen in game */ - set_gameview = 3, /* may be set via extern program, displayed in game */ - set_in_game = 4, /* may be set via extern program or set in the game */ - set_wizonly = 5, /* may be set in the game if wizmode */ - set_wiznofuz = 6, /* wizard-mode only, but not by fuzzer */ - set_hidden = 7 /* placeholder for prefixed entries, never show it */ -}; -#define SET__IS_VALUE_VALID(s) ((s < set_in_sysconf) || (s > set_wiznofuz)) - struct plinemsg_type { xint16 msgtype; /* one of MSGTYP_foo */ struct nhregex *regex; diff --git a/include/optlist.h b/include/optlist.h index b920048ac..29f376dd8 100644 --- a/include/optlist.h +++ b/include/optlist.h @@ -4,10 +4,6 @@ #ifndef OPTLIST_H #define OPTLIST_H -#ifdef OPTIONS_C -static int optfn_boolean(int, int, boolean, char *, char *); -#endif - /* * NOTE: If you add (or delete) an option, please review: * doc/options.txt @@ -16,6 +12,10 @@ static int optfn_boolean(int, int, boolean, char *, char *); * updates that should accompany your change. */ +#define BACKWARD_COMPAT + +extern int optfn_boolean(int, int, boolean, char *, char *); + enum OptType { BoolOpt, CompOpt, OthrOpt }; enum Y_N { No, Yes }; enum Off_On { Off, On }; @@ -45,7 +45,7 @@ struct allopt_t { const char *alias; const char *descr; const char *prefixgw; - boolean initval, has_handler, dupdetected; + boolean initval, has_handler, dupdetected, disregarded; }; #endif /* OPTLIST_H */ @@ -74,16 +74,16 @@ static int optfn_##a(int, int, boolean, char *, char *); #elif defined(NHOPT_PARSE) #define NHOPTB(a, sec, b, c, s, i, n, v, d, al, bp, termp, desc) \ { #a, OptS_##sec, 0, b, opt_##a, s, BoolOpt, n, v, d, No, termp, c, \ - bp, &optfn_boolean, al, desc, (const char *) 0, i, 0, 0 }, + bp, &optfn_boolean, al, desc, (const char *) 0, i, 0, 0 , 0 }, #define NHOPTC(a, sec, b, c, s, n, v, d, h, al, z) \ { #a, OptS_##sec, 0, b, opt_##a, s, CompOpt, n, v, d, No, 0, c, \ - (boolean *) 0, &optfn_##a, al, z, (const char *) 0, Off, h, 0 }, + (boolean *) 0, &optfn_##a, al, z, (const char *) 0, Off, h, 0, 0 }, #define NHOPTP(a, sec, b, c, s, n, v, d, h, al, z) \ { #a, OptS_##sec, 0, b, pfx_##a, s, CompOpt, n, v, d, Yes, 0, c, \ - (boolean *) 0, &pfxfn_##a, al, z, #a, Off, h, 0 }, + (boolean *) 0, &pfxfn_##a, al, z, #a, Off, h, 0, 0 }, #define NHOPTO(m, sec, a, b, c, s, n, v, d, al, z) \ { m, OptS_##sec, 0, b, opt_##a, s, OthrOpt, n, v, d, No, 0, c, \ - (boolean *) 0, &optfn_##a, al, z, (const char *) 0, On, On, 0 }, + (boolean *) 0, &optfn_##a, al, z, (const char *) 0, On, On, 0, 0 }, /* this is not reliable because TILES_IN_GLYPHMAP might be defined * in a multi-interface binary but not apply to the current interface */ diff --git a/include/windconf.h b/include/windconf.h index bd30f8a6b..b7330668c 100644 --- a/include/windconf.h +++ b/include/windconf.h @@ -32,6 +32,8 @@ #define OPTIONS_AT_RUNTIME /* build info done at runtime not text file */ +#define EARLY_CONFIGFILE_PASS + /* * ----------------------------------------------------------------- * The remaining code shouldn't need modification. diff --git a/include/winprocs.h b/include/winprocs.h index 688d76138..014ee1e09 100644 --- a/include/winprocs.h +++ b/include/winprocs.h @@ -415,79 +415,4 @@ struct chain_procs { }; #endif /* WINCHAIN */ -#ifdef SAFEPROCS -/* - * window port routines available in sys/share/safeproc.c - */ -extern struct window_procs *get_safe_procs(int); -extern void safe_init_nhwindows(int *, char **); -extern void safe_player_selection(void); -extern void safe_askname(void); -extern void safe_get_nh_event(void); -extern void safe_exit_nhwindows(const char *); -extern void safe_suspend_nhwindows(const char *); -extern void safe_resume_nhwindows(void); -extern winid safe_create_nhwindow(int); -extern void safe_clear_nhwindow(winid); -extern void safe_display_nhwindow(winid, boolean); -extern void safe_destroy_nhwindow(winid); -extern void safe_curs(winid, int, int); -extern void safe_putstr(winid, int, const char *); -extern void safe_putmixed(winid, int, const char *); -extern void safe_display_file(const char *, boolean); -extern void safe_start_menu(winid, unsigned long); -extern void safe_add_menu(winid, const glyph_info *, const ANY_P *, - char, char, int, int, const char *, - unsigned int); -extern void safe_end_menu(winid, const char *); -extern int safe_select_menu(winid, int, MENU_ITEM_P **); -extern char safe_message_menu(char, int, const char *); -extern void safe_mark_synch(void); -extern void safe_wait_synch(void); -#ifdef CLIPPING -extern void safe_cliparound(int, int); -#endif -#ifdef POSITIONBAR -extern void safe_update_positionbar(char *); -#endif -extern void safe_print_glyph(winid, coordxy, coordxy, - const glyph_info *, const glyph_info *); -extern void safe_raw_print(const char *); -extern void safe_raw_print_bold(const char *); -extern int safe_nhgetch(void); -extern int safe_nh_poskey(coordxy *, coordxy *, int *); -extern void safe_nhbell(void); -extern int safe_doprev_message(void); -extern char safe_yn_function(const char *, const char *, char); -extern void safe_getlin(const char *, char *); -extern int safe_get_ext_cmd(void); -extern void safe_number_pad(int); -extern void safe_delay_output(void); -#ifdef CHANGE_COLOR -extern void safe_change_color(int, long, int); -#ifdef MAC -extern void safe_change_background(int); -extern short safe_set_font_name(winid, char *); -#endif -extern char *safe_get_color_string(void); -#endif -extern void safe_outrip(winid, int, time_t); -extern void safe_preference_update(const char *); -extern char *safe_getmsghistory(boolean); -extern void safe_putmsghistory(const char *, boolean); -extern void safe_status_init(void); -extern void safe_status_finish(void); -extern void safe_status_enablefield(int, const char *, const char *, - boolean); -extern void safe_status_update(int, genericptr_t, int, int, int, - unsigned long *); -extern boolean safe_can_suspend(void); -extern void stdio_raw_print(const char *); -extern void stdio_nonl_raw_print(const char *); -extern void stdio_raw_print_bold(const char *); -extern void stdio_wait_synch(void); -extern void safe_update_inventory(int); -extern win_request_info *safe_ctrl_nhwindow(winid, int, win_request_info *); -extern int stdio_nhgetch(void); -#endif /* SAFEPROCS */ #endif /* WINPROCS_H */ diff --git a/src/cfgfiles.c b/src/cfgfiles.c index 1b4715a4f..439747127 100644 --- a/src/cfgfiles.c +++ b/src/cfgfiles.c @@ -1881,6 +1881,85 @@ vconfig_error_add(const char *str, va_list the_args) config_erradd(buf); } +void +rcfile(void) +{ + char *opts = 0, *xtraopts = 0; + const char *envname, *namesrc, *nameval; + + go.opt_phase = environ_opt; + /* getenv() instead of nhgetenv(): let total length of options be long; + parseoptions() will check each individually */ + envname = "NETHACKOPTIONS"; + opts = getenv(envname); + if (!opts) { + /* fall back to original name; discouraged */ + envname = "HACKOPTIONS"; + opts = getenv(envname); + } + + if (gc.cmdline_rcfile) { + namesrc = "command line"; + nameval = gc.cmdline_rcfile; + xtraopts = opts; + if (opts && (*opts == '/' || *opts == '\\' || *opts == '@')) + xtraopts = 0; /* NETHACKOPTIONS is a file name; ignore it */ + } else if (opts && (*opts == '/' || *opts == '\\' || *opts == '@')) { + /* NETHACKOPTIONS is a file name; use that instead of the default */ + if (*opts == '@') + ++opts; /* @filename */ + namesrc = envname; + nameval = opts; + xtraopts = 0; + } else { + /* either no NETHACKOPTIONS or it wasn't a file name; + read the default configuration file */ + nameval = namesrc = 0; + xtraopts = opts; + } + + go.opt_phase = rc_file_opt; + /* seemingly arbitrary name length restriction is to prevent error + messages, if any were to be delivered while accessing the file, + from potentially overflowing buffers */ + if (nameval && (int) strlen(nameval) >= BUFSZ / 2) { + config_error_init(TRUE, namesrc, FALSE); + config_error_add( + "nethackrc file name \"%.40s\"... too long; using default", + nameval); + config_error_done(); + nameval = namesrc = 0; /* revert to default nethackrc */ + } + + config_error_init(TRUE, nameval, nameval ? CONFIG_ERROR_SECURE : FALSE); + (void) read_config_file(nameval, set_in_config); + config_error_done(); + if (xtraopts) { + /* NETHACKOPTIONS is present and not a file name */ + go.opt_phase = environ_opt; + config_error_init(FALSE, envname, FALSE); + (void) parseoptions(xtraopts, TRUE, FALSE); + config_error_done(); + } + + if (gc.cmdline_rcfile) + free((genericptr_t) gc.cmdline_rcfile), gc.cmdline_rcfile = 0; + /*[end of nethackrc handling]*/ +} + +void +rcfile_interface_options(void) +{ + allopt_array_init(); + set_all_options_disregarded(); + heed_this_option(opt_windowtype); + heed_this_option(opt_soundlib); + rcfile(); + set_all_options_heeded(); + disregard_this_option(opt_windowtype); + disregard_this_option(opt_soundlib); +} + #ifdef SYSCF #ifdef SYSCF_FILE void diff --git a/src/decl.c b/src/decl.c index cc1e8a3b6..f229de737 100644 --- a/src/decl.c +++ b/src/decl.c @@ -592,7 +592,7 @@ static const struct instance_globals_o g_init_o = { /* o_init.c */ DUMMY, /* oclass_prob_totals */ /* options.c */ - 0, /* opt_phase */ + phase_not_set, /* opt_phase */ FALSE, /* opt_initial */ FALSE, /* opt_from_file */ FALSE, /* opt_need_redraw */ diff --git a/src/options.c b/src/options.c index c22105c03..9bf14028f 100644 --- a/src/options.c +++ b/src/options.c @@ -56,21 +56,13 @@ NEARDATA struct accessibility_data a11y; #include "optlist.h" #undef NHOPT_PROTO -#define NHOPT_ENUM -enum opt { - opt_prefix_only = -1, -#include "optlist.h" - OPTCOUNT -}; -#undef NHOPT_ENUM - #define NHOPT_PARSE static struct allopt_t allopt_init[] = { #include "optlist.h" {(const char *) 0, OptS_Advanced, 0, 0, 0, set_in_sysconf, BoolOpt, No, No, No, No, Term_False, 0, (boolean *) 0, (int (*)(int, int, boolean, char *, char *)) 0, - (char *) 0, (const char *) 0, (const char *) 0, 0, 0, 0 } + (char *) 0, (const char *) 0, (const char *) 0, 0, 0, 0, TRUE } }; #undef NHOPT_PARSE @@ -94,16 +86,6 @@ enum optn_result { enum requests { do_nothing, do_init, do_set, do_handler, get_val, get_cnf_val }; -/* these aren't the same as set_xxx in optlist.h */ -enum option_phases { - builtin_opt=1,/* compiled-in default value of an option */ - syscf_opt, /* sysconf setting of an option, overrides builtin */ - rc_file_opt, /* player's run-time config file setting, overrides syscf */ - environ_opt, /* player's environment NETHACKOPTIONS, overrides rc_file */ - cmdline_opt, /* program invocation command-line, overrides environ */ - play_opt, /* 'O' command, interactively set so overrides all */ - num_opt_phases -}; static struct allopt_t allopt[SIZE(allopt_init)]; @@ -124,6 +106,7 @@ extern char ttycolors[CLR_MAX]; /* in sys/msdos/video.c */ static char empty_optstr[] = { '\0' }; static boolean duplicate, using_alias; static boolean give_opt_msg = TRUE; +static boolean restricted_options_mode = FALSE; enum { MAX_ROLEOPT = 4 }; /* 4: role,race,gend,algn */ static boolean opt_set_in_config[OPTCOUNT]; @@ -595,7 +578,7 @@ parseoptions( * placed that number into each option's allopt[n].minmatch. * */ - if (!got_match) + if (!got_match && allopt[i].name) got_match = match_optname(opts, allopt[i].name, allopt[i].minmatch, TRUE); if (got_match) { @@ -634,7 +617,8 @@ parseoptions( /* allow optfn's to test whether they were called from parseoptions() */ program_state.in_parseoptions++; - if (got_match && matchidx >= 0) { + if (got_match && (matchidx >= 0 && matchidx < OPTCOUNT) + && !allopt[matchidx].disregarded) { duplicate = duplicate_opt_detection(matchidx); if (duplicate && !allopt[matchidx].dupeok) complain_about_duplicate(matchidx); @@ -684,7 +668,7 @@ parseoptions( } } - if (optresult == optn_silenterr) + if (optresult == optn_silenterr || restricted_options_mode) return FALSE; if (pfx_match && optresult == optn_err) { char pfxbuf[BUFSZ], *pfxp; @@ -4971,17 +4955,20 @@ optfn_windowtype( * _end_ because comma-separated option strings are processed from * right to left. */ - if (iflags.windowtype_locked) - return optn_ok; + if (!iflags.window_inited) { + if (iflags.windowtype_locked) + return optn_ok; - if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE)) - != empty_optstr) { - nmcpy(gc.chosen_windowtype, op, WINTYPELEN); - if (!iflags.windowtype_deferred) { - choose_windows(gc.chosen_windowtype); + if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE)) + != empty_optstr) { + nmcpy(gc.chosen_windowtype, op, WINTYPELEN); + if (!iflags.windowtype_deferred) { + choose_windows(gc.chosen_windowtype); + } + } else { + return optn_err; } - } else - return optn_err; + } return optn_ok; } if (req == get_val || req == get_cnf_val) { @@ -5193,7 +5180,7 @@ pfxfn_IBM_(int optidx UNUSED, int req, boolean negated UNUSED, * (Use optidx to reference the specific option) */ -staticfn int +int optfn_boolean( int optidx, int req, boolean negated, char *opts, char *op) @@ -7066,29 +7053,14 @@ txt2key(char *txt) void initoptions(void) { - int i; - /* * Most places that call initoptions_init()/initoptions() would * have the calls next to each other, so instead of adding * initoptions_init() everywhere, just add it where it's needed in * a non-adjacent place and call it here for all the other cases. */ - if(go.opt_phase != builtin_opt) + if (go.opt_phase != builtin_opt) initoptions_init(); - - /* - * Call each option function with an init flag and give it a chance - * to make any preparations that it might require. We do this - * whether or not the option itself is ever specified; that's - * irrelevant for the init call. Doing this allows the prep code for - * option settings to remain adjacent to, and in the same function as, - * the code that processes those options. - */ - for (i = 0; i < OPTCOUNT; ++i) { - if (allopt[i].optfn) - (*allopt[i].optfn)(i, do_init, FALSE, empty_optstr, empty_optstr); - } #ifdef SYSCF /* someday there may be other SYSCF alternatives besides text file */ #ifdef SYSCF_FILE @@ -7130,9 +7102,7 @@ initoptions_init(void) go.opt_phase = builtin_opt; /* Did I need to move this here? */ /* initialize the function pointers for saving the game */ sf_init(); - memcpy(allopt, allopt_init, sizeof(allopt)); - determine_ambiguities(); - + allopt_array_init(); /* if windowtype has been specified on the command line, set it up early so windowtype-specific options use it as their base */ if (gc.cmdline_windowsys) { @@ -7286,6 +7256,28 @@ initoptions_init(void) /* since this is done before init_objects(), do partial init here */ objects[SLIME_MOLD].oc_name_idx = SLIME_MOLD; nmcpy(svp.pl_fruit, OBJ_NAME(objects[SLIME_MOLD]), PL_FSIZ); + +#ifdef SYSCF +/* someday there may be other SYSCF alternatives besides text file */ +#ifdef SYSCF_FILE + /* If SYSCF_FILE is specified, it _must_ exist... */ + assure_syscf_file(); + config_error_init(TRUE, SYSCF_FILE, FALSE); + + /* ... and _must_ parse correctly. */ + go.opt_phase = syscf_opt; + if (!read_config_file(SYSCF_FILE, set_in_sysconf)) { + if (config_error_done() && !iflags.initoptions_noterminate) + nh_terminate(EXIT_FAILURE); + } + config_error_done(); + /* + * TODO [maybe]: parse the sysopt entries which are space-separated + * lists of usernames into arrays with one name per element. + */ +#endif +#endif /* SYSCF */ + initoptions_finish(); } /* @@ -7306,72 +7298,69 @@ initoptions_init(void) */ void initoptions_finish(void) -{ - nhsym sym = 0; - char *opts = 0, *xtraopts = 0; -#ifndef MAC - const char *envname, *namesrc, *nameval; +{ nhsym sym = 0; - /* getenv() instead of nhgetenv(): let total length of options be long; - parseoptions() will check each individually */ - envname = "NETHACKOPTIONS"; - opts = getenv(envname); - if (!opts) { - /* fall back to original name; discouraged */ - envname = "HACKOPTIONS"; - opts = getenv(envname); + rcfile(); + + (void) fruitadd(svp.pl_fruit, (struct fruit *) 0); + /* + * Remove "slime mold" from list of object names. This will + * prevent it from being wished unless it's actually present + * as a named (or default) fruit. Wishing for "fruit" will + * result in the player's preferred fruit. [Once upon a time + * the override value used was "\033" which prevented wishing + * for the slime mold object at all except by asking for a + * specific named fruit.] Note that there are multiple fruit + * object types (apple, melon, &c) but the "fruit" object is + * slime mold or whatever custom name player assigns to that. + */ + obj_descr[SLIME_MOLD].oc_name = "fruit"; + + sym = get_othersym(SYM_BOULDER, + Is_rogue_level(&u.uz) ? ROGUESET : PRIMARYSET); + if (sym) + gs.showsyms[SYM_BOULDER + SYM_OFF_X] = sym; + reglyph_darkroom(); + reset_glyphmap(gm_optionchange); +#ifdef STATUS_HILITES + /* + * A multi-interface binary might only support status highlighting + * for some of the interfaces; check whether we asked for it but are + * using one which doesn't. + * + * Option processing can take place before a user-decided WindowPort + * is even initialized, so check for that too. + */ + if (!WINDOWPORT(safestartup)) { + if (iflags.hilite_delta && !wc2_supported("statushilites")) { + raw_printf("Status highlighting not supported for %s interface.", + windowprocs.name); + iflags.hilite_delta = 0; + } } +#endif + update_rest_on_space(); - if (gc.cmdline_rcfile) { - namesrc = "command line"; - nameval = gc.cmdline_rcfile; - xtraopts = opts; - if (opts && (*opts == '/' || *opts == '\\' || *opts == '@')) - xtraopts = 0; /* NETHACKOPTIONS is a file name; ignore it */ - } else if (opts && (*opts == '/' || *opts == '\\' || *opts == '@')) { - /* NETHACKOPTIONS is a file name; use that instead of the default */ - if (*opts == '@') - ++opts; /* @filename */ - namesrc = envname; - nameval = opts; - xtraopts = 0; - } else -#endif /* !MAC */ - /*else*/ { - /* either no NETHACKOPTIONS or it wasn't a file name; - read the default configuration file */ - nameval = namesrc = 0; - xtraopts = opts; - } + /* these can't rely on compile-time initialization for their defaults + because a multi-interface binary might need different values for + different interfaces; if neither tiled_map nor ascii_map pass the + wc_supported() test, assume ascii_map */ + if (iflags.wc_tiled_map && !wc_supported("tiled_map")) + iflags.wc_tiled_map = FALSE, iflags.wc_ascii_map = TRUE; + else if (iflags.wc_ascii_map && !wc_supported("ascii_map") + && wc_supported("tiled_map")) + iflags.wc_ascii_map = FALSE, iflags.wc_tiled_map = TRUE; - /* seemingly arbitrary name length restriction is to prevent error - messages, if any were to be delivered while accessing the file, - from potentially overflowing buffers */ - if (nameval && (int) strlen(nameval) >= BUFSZ / 2) { - go.opt_phase = rc_file_opt; - config_error_init(TRUE, namesrc, FALSE); - config_error_add( - "nethackrc file name \"%.40s\"... too long; using default", - nameval); - config_error_done(); - nameval = namesrc = 0; /* revert to default nethackrc */ - } - - config_error_init(TRUE, nameval, nameval ? CONFIG_ERROR_SECURE : FALSE); - (void) read_config_file(nameval, set_in_config); - config_error_done(); - if (xtraopts) { - /* NETHACKOPTIONS is present and not a file name */ - go.opt_phase = environ_opt; - config_error_init(FALSE, envname, FALSE); - (void) parseoptions(xtraopts, TRUE, FALSE); - config_error_done(); - } - - if (gc.cmdline_rcfile) - free((genericptr_t) gc.cmdline_rcfile), gc.cmdline_rcfile = 0; - /*[end of nethackrc handling]*/ +#ifdef ENHANCED_SYMBOLS + if (glyphid_cache_status()) + free_glyphid_cache(); + apply_customizations(gc.currentgraphics, do_custom_symbols); +#endif + go.opt_initial = FALSE; + return; +} +#if 0 (void) fruitadd(svp.pl_fruit, (struct fruit *) 0); /* * Remove "slime mold" from list of object names. This will @@ -7449,6 +7438,37 @@ initoptions_finish(void) } return; } +#endif +void +allopt_array_init(void) +{ + int i; + static boolean options_array_inited_already = FALSE; + + if (!options_array_inited_already) { + memcpy(allopt, allopt_init, sizeof(allopt)); + determine_ambiguities(); + for (i = 0; allopt[i].name; i++) { + if (allopt[i].addr) + *(allopt[i].addr) = allopt[i].initval; + } + set_all_options_heeded(); + /* + * Call each option function with an init flag and give it a chance + * to make any preparations that it might require. We do this + * whether or not the option itself is ever specified; that's + * irrelevant for the init call. Doing this allows the prep code for + * option settings to remain adjacent to, and in the same function as, + * the code that processes those options. + */ + for (i = 0; i < OPTCOUNT; ++i) { + if (allopt[i].optfn) + (*allopt[i].optfn)(i, do_init, FALSE, empty_optstr, + empty_optstr); + } + options_array_inited_already = TRUE; + } +} /* ******************************************* @@ -8966,6 +8986,7 @@ doset(void) /* changing options via menu by Per Liboriussen */ if (opt_indx < -1) opt_indx++; /* -1 offset for select_menu() */ opt_indx -= indexoffset; + assert(IndexOk(opt_indx, allopt)); if (allopt[opt_indx].opttyp == BoolOpt) { /* boolean option */ Sprintf(buf, "%s%s", *allopt[opt_indx].addr ? "!" : "", @@ -10220,6 +10241,41 @@ enhance_menu_text( return; } +void +set_all_options_heeded(void) +{ + int i; + + for (i = 0; i < OPTCOUNT; i++) + allopt[i].disregarded = FALSE; + restricted_options_mode = FALSE; +} + +void +set_all_options_disregarded(void) +{ + int i; + + for (i = 0; i < OPTCOUNT ; i++) + allopt[i].disregarded = TRUE; + restricted_options_mode = TRUE; +} + +void +heed_this_option(enum opt optidx) +{ + if (optidx >= 0 && optidx < (enum opt) OPTCOUNT) + allopt[optidx].disregarded = FALSE; +} +void +disregard_this_option(enum opt optidx) +{ + if (optidx >= 0 && optidx < (enum opt) OPTCOUNT) + allopt[optidx].disregarded = TRUE; + if (!restricted_options_mode) + restricted_options_mode = TRUE; +} + #undef OPTIONS_HEADING #undef CONFIG_SLOT diff --git a/sys/windows/GNUmakefile b/sys/windows/GNUmakefile index 3ea5c777b..4bc3df970 100644 --- a/sys/windows/GNUmakefile +++ b/sys/windows/GNUmakefile @@ -1239,14 +1239,14 @@ COREOBJS = $(addsuffix .o, allmain alloc apply artifact attrib ball bones botl \ nhlobj nhlsel nhlua windsound o_init objects objnam options \ pager pickup pline polyself potion pray priest quest questpgr \ random read rect region report restore rip rnd role rumors \ - safeproc save sfbase sfstruct shk shknam sit selvar sounds sp_lev \ + save sfbase sfstruct shk shknam sit selvar sounds sp_lev \ spell stairs steal steed strutil \ symbols sys teleport timeout topten track trap u_init uhitm utf8map \ vault version vision weapon were wield windmain windows windsys wizard \ wizcmds worm worn write zap $(SOUNDLIBOBJS)) -CFLAGSW = $(CFLAGS) $(CFLAGSXTRA) $(SOUNDLIBINCL) $(COMMONDEF) $(DLBFLG) -DTILES -D_WINDOWS -DMSWIN_GRAPHICS -DSAFEPROCS -DNOTTYGRAPHICS $(SOUNDLIBDEFS) -CPPFLAGSW = $(CFLAGS) $(CPPFLAGSXTRA) $(COMMONDEF) $(DLBFLG) -DTILES -D_WINDOWS -DMSWIN_GRAPHICS -DSAFEPROCS -DNOTTYGRAPHICS $(SOUNDLIBDEFS) +CFLAGSW = $(CFLAGS) $(CFLAGSXTRA) $(SOUNDLIBINCL) $(COMMONDEF) $(DLBFLG) -DTILES -D_WINDOWS -DMSWIN_GRAPHICS -DNOTTYGRAPHICS $(SOUNDLIBDEFS) +CPPFLAGSW = $(CFLAGS) $(CPPFLAGSXTRA) $(COMMONDEF) $(DLBFLG) -DTILES -D_WINDOWS -DMSWIN_GRAPHICS -DNOTTYGRAPHICS $(SOUNDLIBDEFS) ONHW = $(O)nethackw NHWONLY = $(addsuffix .o, mhaskyn mhdlg mhfont mhinput mhmain mhmap mhmenu \ @@ -1348,8 +1348,8 @@ CLEAN_FILE += $(NHWTARGETS) $(NHWOBJS) $(NHWRES) $(BMPS) $(WAV) #========================================== # nethack #========================================== -CFLAGSNH = $(CFLAGSU) $(CFLAGSXTRA) $(SOUNDLIBINCL) -DNO_TILE_C -DSAFEPROCS -D_LIB -DWIN32CON $(SOUNDLIBDEFS) -CPPFLAGSNH = $(CFLAGSU) $(CPPFLAGSXTRA) -DNO_TILE_C -DSAFEPROCS -D_LIB -DWIN32CON $(SOUNDLIBDEFS) +CFLAGSNH = $(CFLAGSU) $(CFLAGSXTRA) $(SOUNDLIBINCL) -DNO_TILE_C -D_LIB -DWIN32CON $(SOUNDLIBDEFS) +CPPFLAGSNH = $(CFLAGSU) $(CPPFLAGSXTRA) -DNO_TILE_C -D_LIB -DWIN32CON $(SOUNDLIBDEFS) ONH = $(O)nethack diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index 2097d7183..fe481ecc2 100644 --- a/sys/windows/Makefile.nmake +++ b/sys/windows/Makefile.nmake @@ -847,8 +847,7 @@ REGEXTTY = $(OTTY)cppregex.o LUAOBJTTY = $(OTTY)nhlua.o $(OTTY)nhlsel.o $(OTTY)nhlobj.o VVOBJTTY = $(OTTY)version.o -SOBJTTY = $(OTTY)windmain.o $(OTTY)windsys.o $(OTTY)win10.o \ - $(OTTY)safeproc.o +SOBJTTY = $(OTTY)windmain.o $(OTTY)windsys.o $(OTTY)win10.o TTYOBJTTY = $(OTTY)topl.o $(OTTY)getline.o $(OTTY)wintty.o @@ -908,8 +907,7 @@ REGEXGUI = $(OGUI)cppregex.o LUAOBJGUI = $(OGUI)nhlua.o $(OGUI)nhlsel.o $(OGUI)nhlobj.o VVOBJGUI = $(OGUI)version.o -SOBJGUI = $(OGUI)windmain.o $(OGUI)windsys.o $(OGUI)win10.o \ - $(OGUI)safeproc.o +SOBJGUI = $(OGUI)windmain.o $(OGUI)windsys.o $(OGUI)win10.o GUIOBJ = $(OGUI)mhaskyn.o $(OGUI)mhdlg.o \ $(OGUI)mhfont.o $(OGUI)mhinput.o $(OGUI)mhmain.o $(OGUI)mhmap.o \ @@ -1364,8 +1362,8 @@ INCLUSIONS= /I$(R_INCL) /I$(R_MSWSYS) $(R_LUAINCL) $(R_SOUNDLIBINCL) # Util and console builds #========================================== -CFLAGS = $(ctmpflags) $(INCLUSIONS) $(DLBDEF) -DSAFEPROCS $(SOUNDLIBDEFS) -CPPFLAGS = $(cpptmpflags) $(INCLUSIONS) $(DLBDEF) -DSAFEPROCS $(SOUNDLIBDEFS) +CFLAGS = $(ctmpflags) $(INCLUSIONS) $(DLBDEF) $(SOUNDLIBDEFS) +CPPFLAGS = $(cpptmpflags) $(INCLUSIONS) $(DLBDEF) $(SOUNDLIBDEFS) LFLAGS = $(lflags) $(conlibs) $(MACHINE) #========================================== @@ -2390,14 +2388,12 @@ $(OTTY)windsys.o: $(MSWSYS)windsys.c $(WINDHDR) $(HACK_H) $(OTTY)windsound.o: $(WINDSOUNDDIR)windsound.c $(HACK_H) #$(OTTY)sample.o: $(SOUNDDIR)sample.c $(HACK_H) $(OTTY)windmain.o: $(MSWSYS)windmain.c $(WINDHDR) $(HACK_H) -$(OTTY)safeproc.o: $(WSHR)safeproc.c $(WINDHDR) $(HACK_H) $(OGUI)consoletty.o: $(MSWSYS)consoletty.c $(WINDHDR) $(HACK_H) $(TILE_H) $(OGUI)win10.o: $(MSWSYS)win10.c $(WINDHDR) $(HACK_H) $(OGUI)windsys.o: $(MSWSYS)windsys.c $(WINDHDR) $(HACK_H) $(OGUI)windsound.o: $(WINDSOUNDDIR)windsound.c $(HACK_H) $(OGUI)windmain.o: $(MSWSYS)windmain.c $(WINDHDR) $(HACK_H) -$(OGUI)safeproc.o: $(WSHR)safeproc.c $(WINDHDR) $(HACK_H) #=================================================================== # win/win32 dependencies diff --git a/sys/windows/consoletty.c b/sys/windows/consoletty.c index 34489430f..bceb98132 100644 --- a/sys/windows/consoletty.c +++ b/sys/windows/consoletty.c @@ -156,7 +156,6 @@ static boolean check_font_widths(void); #endif static void set_known_good_console_font(void); static void restore_original_console_font(void); -extern void safe_routines(void); void tty_ibmgraphics_fixup(void); #ifdef VIRTUAL_TERMINAL_SEQUENCES extern void (*ibmgraphics_mode_callback)(void); /* symbols.c */ @@ -1189,8 +1188,6 @@ consoletty_open(int mode UNUSED) void consoletty_exit(void) { - /* go back to using the safe routines */ - safe_routines(); free_custom_colors(); free((genericptr_t) console.front_buffer); free((genericptr_t) console.back_buffer); @@ -2546,9 +2543,13 @@ void early_raw_print(const char *s) void nethack_enter_consoletty(void) { + int width; #ifdef VIRTUAL_TERMINAL_SEQUENCES char buf[BUFSZ], *bp, *localestr; BOOL apisuccess; +// DWORD unused; +// int i = 0; + #endif /* VIRTUAL_TERMINAL_SEQUENCES */ #if 0 /* set up state needed by early_raw_print() */ @@ -2562,13 +2563,38 @@ void nethack_enter_consoletty(void) GetWindowLong(console.hWnd, GWL_STYLE) & ~WS_MAXIMIZEBOX & ~WS_SIZEBOX); #endif + + CONSOLE_SCREEN_BUFFER_INFO csbi; + if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) { + /* srWindow identifies the visible area; dwSize identifies the buffer + */ + width = csbi.srWindow.Right - csbi.srWindow.Left + 1; + fprintf(stdout, "width = %d\n", width); + } + console.hConOut = GetStdHandle(STD_OUTPUT_HANDLE); nhassert(console.hConOut != NULL); // NOTE: this assert will not print GetConsoleScreenBufferInfo(console.hConOut, &console.orig_csbi); + //COORD screencheck = GetLargestConsoleWindowSize(console.hConOut); + + GetConsoleMode(console.hConOut, &console.orig_out_cmode); + console.out_cmode = console.orig_out_cmode; + console.out_cmode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + SetConsoleMode(console.hConOut, console.out_cmode); +#if 0 + /* tests */ + WriteConsoleA(console.hConOut, "\033[8;;133t",9, &unused, NULL); + for (i = 0; i < 13; ++i) { + WriteConsoleA(console.hConOut, "0123456789", 10, &unused, NULL); + } + WriteConsoleA(console.hConOut, "\033[3;133ftest", 12, &unused, NULL); + GetConsoleScreenBufferInfo(console.hConOut, &console.orig_csbi); /* Testing of widths != COLNO has not turned up any problems. Need * to do a bit more testing and then we are likely to enable having * console width match window width. */ +#endif + #if 0 console.width = console.orig_csbi.srWindow.Right - console.orig_csbi.srWindow.Left + 1; diff --git a/sys/windows/vs/NetHack/NetHack.vcxproj b/sys/windows/vs/NetHack/NetHack.vcxproj index 131a4cf05..c6f2df87c 100644 --- a/sys/windows/vs/NetHack/NetHack.vcxproj +++ b/sys/windows/vs/NetHack/NetHack.vcxproj @@ -74,7 +74,7 @@ Speed true $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);%(AdditionalIncludeDirectories) - WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;SND_SOUNDEFFECTS_AUTOMAP;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) + WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;SND_SOUNDEFFECTS_AUTOMAP;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) stdclatest @@ -218,7 +218,6 @@ - diff --git a/sys/windows/vs/NetHackW/NetHackW.vcxproj b/sys/windows/vs/NetHackW/NetHackW.vcxproj index fce2d2a1d..cc3ec2563 100644 --- a/sys/windows/vs/NetHackW/NetHackW.vcxproj +++ b/sys/windows/vs/NetHackW/NetHackW.vcxproj @@ -87,7 +87,7 @@ Disabled true $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);%(AdditionalIncludeDirectories) - TILES;_WINDOWS;DLB;MSWIN_GRAPHICS;SAFEPROCS;NOTTYGRAPHICS;SND_LIB_WINDSOUND;USER_SOUNDS;SND_SOUNDEFFECTS_AUTOMAP;HAS_STDINT_H;HAS_INTTYPES_H;PDC_WIDE;%(PreprocessorDefinitions) + TILES;_WINDOWS;DLB;MSWIN_GRAPHICS;NOTTYGRAPHICS;SND_LIB_WINDSOUND;USER_SOUNDS;SND_SOUNDEFFECTS_AUTOMAP;HAS_STDINT_H;HAS_INTTYPES_H;PDC_WIDE;%(PreprocessorDefinitions) stdclatest stdclatest stdclatest @@ -263,7 +263,6 @@ - diff --git a/sys/windows/vs/sfctool/sfctool.vcxproj b/sys/windows/vs/sfctool/sfctool.vcxproj index 5dff90066..ad1971969 100644 --- a/sys/windows/vs/sfctool/sfctool.vcxproj +++ b/sys/windows/vs/sfctool/sfctool.vcxproj @@ -150,7 +150,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 stdclatest true @@ -166,7 +166,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_NDEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_NDEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 stdclatest true @@ -186,7 +186,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 stdclatest true @@ -202,7 +202,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 stdclatest true @@ -218,7 +218,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_NDEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_NDEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 stdclatest true @@ -238,7 +238,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_NDEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_NDEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 stdclatest true @@ -258,4 +258,4 @@ - \ No newline at end of file + diff --git a/sys/windows/vs/sftags/sftags.vcxproj b/sys/windows/vs/sftags/sftags.vcxproj index 43f024c24..52c63d47a 100644 --- a/sys/windows/vs/sftags/sftags.vcxproj +++ b/sys/windows/vs/sftags/sftags.vcxproj @@ -110,7 +110,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 true true @@ -125,7 +125,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 true true @@ -144,7 +144,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 true true @@ -159,7 +159,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 true true @@ -174,7 +174,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 true true @@ -194,7 +194,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 true true @@ -223,4 +223,4 @@ - \ No newline at end of file + diff --git a/sys/windows/windmain.c b/sys/windows/windmain.c index 9e34152ac..c50a6d292 100644 --- a/sys/windows/windmain.c +++ b/sys/windows/windmain.c @@ -14,10 +14,6 @@ #include #include -#if !defined(SAFEPROCS) -#error You must #define SAFEPROCS to build windmain.c -#endif - static void nhusage(void); char *exename(void); boolean fakeconsole(void); @@ -67,7 +63,6 @@ char windows_yn_function(const char *, const char *, char); #ifdef WIN32CON extern int windows_console_custom_nhgetch(void); -void safe_routines(void); int tty_self_recover_prompt(void); #endif @@ -106,6 +101,13 @@ void update_file(const char *, const char *, const char *, const char *, BOOL); void windows_raw_print_bold(const char *); +staticfn void set_emergency_io(void); +staticfn void stdio_wait_synch(void); +staticfn void stdio_raw_print(const char *str); +staticfn void stdio_nonl_raw_print(const char *str); +staticfn void stdio_raw_print_bold(const char *str); +staticfn int stdio_nhgetch(void); + #ifdef PORT_HELP void port_help(void); #endif @@ -159,18 +161,9 @@ MAIN(int argc, char *argv[]) char *dir = NULL; #ifdef _MSC_VER - _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); + _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); #endif -#ifdef WIN32CON - /* - * Get a set of valid safe windowport function - * pointers during early startup initialization. - */ -// safe_routines(); -#endif /* WIN32CON */ - - /* setting iflags.colorcount has to be after early_init() * because it zeros out all of iflags */ hwnd = GetDesktopWindow(); @@ -208,8 +201,9 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ #ifndef MSWIN_GRAPHICS early_init(argc, argv); /* already in WinMain for MSWIN_GRAPHICS */ #endif - set_default_prefix_locations(argv[0]); /* must be re-done after initoptions_init() - * which clears out gp.fqn_prefix[] */ + set_default_prefix_locations( + argv[0]); /* must be re-done after initoptions_init() + * which clears out gp.fqn_prefix[] */ copy_sysconf_content(); copy_symbols_content(); /* Now that sysconf has had a chance to set the TROUBLEPREFIX, don't @@ -217,13 +211,20 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ fqn_prefix_locked[TROUBLEPREFIX] = TRUE; copy_config_content(); - // if (iflags.windowtype_deferred && gc.chosen_windowtype[0]) - // windowtype = gc.chosen_windowtype; - // windowtype = gc.chosen_windowtype; + // if (iflags.windowtype_deferred && gc.chosen_windowtype[0]) + // windowtype = gc.chosen_windowtype; + // windowtype = gc.chosen_windowtype; #if !defined(MSWIN_GRAPHICS) - consoletty_open(1); nethack_enter_consoletty(); + consoletty_open(1); +#endif + set_emergency_io(); + +#ifdef EARLY_CONFIGFILE_PASS + rcfile_interface_options(); + if (gc.chosen_windowtype && *gc.chosen_windowtype) + windowtype = gc.chosen_windowtype; #endif if (!windowtype) { @@ -233,7 +234,10 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ windowtype = "tty"; #endif } - choose_windows(windowtype); /* sets all the window port function pointers */ + choose_windows( + windowtype); /* sets all the window port function pointers */ + + init_nhwindows(&argc, argv); #if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) /* Save current directory and make sure it gets restored when @@ -243,8 +247,9 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ error("NetHack: current directory path too long"); #endif initoptions_init(); // This allows OPTIONS in syscf on Windows. - set_default_prefix_locations(argv[0]); /* must be re-done after initoptions_init() - * which clears out gp.fqn_prefix[] */ + set_default_prefix_locations( + argv[0]); /* must be re-done after initoptions_init() + * which clears out gp.fqn_prefix[] */ iflags.windowtype_deferred = TRUE; program_state.early_options = 1; @@ -286,7 +291,6 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ && (strstri(argv[0], "nethackw.exe") || GUILaunched)) iflags.windowtype_locked = TRUE; #endif - windowtype = default_window_sys; #ifdef DLB if (!dlb_init()) { @@ -301,18 +305,6 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ } #endif - if (!iflags.windowtype_locked) { -#if defined(TTY_GRAPHICS) - Strcpy(default_window_sys, "tty"); -#else -#if defined(CURSES_GRAPHICS) && !defined(MSWIN_GRAPHICS) - Strcpy(default_window_sys, "curses"); -#endif /* CURSES */ -#endif /* TTY */ - // if (iflags.windowtype_deferred && gc.chosen_windowtype[0]) - // windowtype = gc.chosen_windowtype; - } - // choose_windows(windowtype); #if defined(SND_LIB_FMOD) assign_soundlib(soundlib_fmod); #elif defined(SND_LIB_WINDSOUND) @@ -322,19 +314,14 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ u.uhp = 1; /* prevent RIP on early quits */ u.ux = 0; /* prevent flush_screen() */ - nethack_enter(argc, argv); iflags.use_background_glyph = FALSE; if (WINDOWPORT(mswin)) iflags.use_background_glyph = TRUE; -#ifdef WIN32CON - if (WINDOWPORT(tty)) - consoletty_open(1); -#endif #ifdef WINCHAIN commit_windowchain(); #endif - init_nhwindows(&argc, argv); +// init_nhwindows(&argc, argv); #ifdef WIN32CON if (WINDOWPORT(tty)) @@ -837,20 +824,6 @@ copy_hack_content(void) gf.fqn_prefix[DATAPREFIX], OPTIONFILE, FALSE); } -#ifdef WIN32CON -void -safe_routines(void) -{ - /* - * Get a set of valid safe windowport function - * pointers during early startup initialization. - */ - if (!WINDOWPORT(safestartup)) - windowprocs = *get_safe_procs(1); - if (!GUILaunched) - windowprocs.win_nhgetch = windows_console_custom_nhgetch; -} -#endif #ifdef PORT_HELP void @@ -1205,9 +1178,7 @@ tty_self_recover_prompt(void) c = 'n'; ct = 0; saved_procs = windowprocs; - if (!WINDOWPORT(safestartup)) - windowprocs = *get_safe_procs(2); /* arg 2 uses no-newline variant */ - windowprocs.win_nhgetch = windows_console_custom_nhgetch; + raw_print("\n"); raw_print("\n"); raw_print("\n"); @@ -1340,4 +1311,60 @@ chdirx(const char *dir, boolean wr) } #endif /* CHDIR */ -/*windmain.c*/ +void +set_emergency_io(void) +{ + windowprocs.win_raw_print = stdio_raw_print; + windowprocs.win_raw_print_bold = stdio_raw_print_bold; + windowprocs.win_nhgetch = stdio_nhgetch; + windowprocs.win_wait_synch = stdio_wait_synch; +} + + +/* Add to your code: windowprocs.win_raw_print = stdio_wait_synch; */ +void +stdio_wait_synch(void) +{ + char valid[] = { ' ', '\n', '\r', '\033', '\0' }; + + fprintf(stdout, "--More--"); + (void) fflush(stdout); + while (!strchr(valid, nhgetch())) + ; +} + +/* Add to your code: windowprocs.win_raw_print = stdio_raw_print; */ +void +stdio_raw_print(const char *str) +{ + if (str) + fprintf(stdout, "%s\n", str); + return; +} + +/* no newline variation, add to your code: + windowprocs.win_raw_print = stdio_nonl_raw_print; */ +void +stdio_nonl_raw_print(const char *str) +{ + if (str) + fprintf(stdout, "%s", str); + return; +} + +/* Add to your code: windowprocs.win_raw_print_bold = stdio_raw_print_bold; */ +void +stdio_raw_print_bold(const char *str) +{ + stdio_raw_print(str); + return; +} + +/* Add to your code: windowprocs.win_nhgetch = stdio_nhgetch; */ +int +stdio_nhgetch(void) +{ + return getchar(); +} + + /*windmain.c*/ diff --git a/sys/windows/windsys.c b/sys/windows/windsys.c index 88f1a69bf..83e63d608 100644 --- a/sys/windows/windsys.c +++ b/sys/windows/windsys.c @@ -73,7 +73,6 @@ static HWND GetConsoleHwnd(void); extern void backsp(void); #endif int windows_console_custom_nhgetch(void); -extern void safe_routines(void); int windows_early_options(const char *window_opt); unsigned long sys_random_seed(void); #if 0 @@ -179,7 +178,7 @@ filesize(char *file) * Chdrive() changes the default drive. */ void -chdrive(char *str) +chdrive(const char *str) { char *ptr; char drive; @@ -293,10 +292,6 @@ win32_abort(void) exit_nhwindows((char *) 0); iflags.window_inited = FALSE; } -#ifdef WIN32CON - if (!WINDOWPORT(mswin) && !WINDOWPORT(safestartup)) - safe_routines(); -#endif if (wizard) { raw_print("Execute debug breakpoint wizard?"); if ((c = nhgetch()) == 'y' || c == 'Y') @@ -522,15 +517,6 @@ nethack_exit(int code) * GUILaunched is defined and set in consoletty.c. */ - -#ifdef WIN32CON - if (!GUILaunched) { - windowprocs = *get_safe_procs(1); - /* use our custom version which works - a little cleaner than the stdio one */ - windowprocs.win_nhgetch = windows_console_custom_nhgetch; - } else -#endif if (getreturn_enabled) { raw_print("\n"); if (iflags.window_inited) @@ -580,10 +566,6 @@ getreturn(const char *str) initializing the window port */ void nethack_enter_windows(void) { -#ifdef WIN32CON - if (WINDOWPORT(tty)) - nethack_enter_consoletty(); -#endif } /* CP437 to Unicode mapping according to the Unicode Consortium */ diff --git a/win/share/safeproc.c b/win/share/safeproc.c deleted file mode 100644 index 19e9aa9b6..000000000 --- a/win/share/safeproc.c +++ /dev/null @@ -1,596 +0,0 @@ -/* NetHack 3.7 safeproc.c */ -/* Copyright (c) Michael Allison, 2018 */ -/* NetHack may be freely redistributed. See license for details. */ - -/* must #define SAFEPROCS in xxxconf.h or via CFLAGS or this won't compile */ -#include "hack.h" - -/* - * *********************************************************** - * This is a complete WindowPort implementation that can be - * assigned to the windowproc function pointers very early - * in the startup initialization, perhaps immediately even. - * It requires only the following call: - * windowprocs = *get_safe_procs(0); - * - * The game startup can trigger functions in other modules - * that make assumptions on a WindowPort being available - * and bad things can happen if any function pointers are - * null at that time. - * - * Some ports prior to 3.6.2 made attempts to early init - * various pieces of one of their WindowPorts, but that - * caused conflicts if that particular WindowPort wasn't - * the one that the user ended up selecting in their - * config file later. The WindowPort interfaced was designed - * to allow multiple WindowPorts to be linked into the same - * game binary. - * - * The base functions established by a call to get_safe_procs() - * accomplish the goal of preventing crashes, but not much - * else. - * - * There are also a few additional functions provided in here - * that can be selected optionally to provide some startup - * functionality for getting messages out to the user about - * issues that are being experienced during startup in - * general or during options parsing. The ones in here are - * deliberately free from any platforms or OS specific code. - * Please leave them using stdio C routines as much as - * possible. That isn't to say you can't do fancier functions - * prior to initialization of the primary WindowPort, but you - * can provide those platform-specific functions elsewhere, - * and assign them the same way that these more generic versions - * are assigned. - * - * The additional platform-independent, but more functional - * routines provided in here should be assigned after the - * windowprocs = *get_safe_procs(n) - * call. - * - * Usage: - * - * windowprocs = *get_safe_procs(0); - * initializes a set of winprocs function pointers that ensure - * none of the function pointers are left null, but that's all - * it does. - * - * windowprocs = *get_safe_procs(1); - * initializes a set of winprocs functions pointers that ensure - * none of the function pointers are left null, but also - * provides some basic output and input functionality using - * nothing other than C stdio routines (no platform-specific - * or OS-specific code). - * - * *********************************************************** - */ - -void safe_dismiss_nhwindow(winid); -void safe_putstr(winid, int, const char *); -void win_safe_init(int); -void safe_number_pad(int); - -struct window_procs safe_procs = { - WPID(safestartup), - (0 -#ifdef TTY_PERM_INVENT - | WC_PERM_INVENT -#endif -#ifdef MSDOS - | WC_TILED_MAP | WC_ASCII_MAP -#endif -#if defined(WIN32CON) - | WC_MOUSE_SUPPORT -#endif - | WC_COLOR | WC_HILITE_PET | WC_INVERSE | WC_EIGHT_BIT_IN), - (0 -#if defined(SELECTSAVED) - | WC2_SELECTSAVED -#endif -#if defined(STATUS_HILITES) - | WC2_HILITE_STATUS | WC2_HITPOINTBAR | WC2_FLUSH_STATUS - | WC2_RESET_STATUS -#endif - | WC2_DARKGRAY | WC2_SUPPRESS_HIST | WC2_URGENT_MESG | WC2_STATUSLINES - | WC2_U_UTF8STR | WC2_PETATTR -#if !defined(NO_TERMS) || defined(WIN32CON) - | WC2_EXTRACOLORS -#endif - ), - {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* color availability */ - safe_init_nhwindows, safe_player_selection, safe_askname, - safe_get_nh_event, - safe_exit_nhwindows, safe_suspend_nhwindows, safe_resume_nhwindows, - safe_create_nhwindow, safe_clear_nhwindow, safe_display_nhwindow, - safe_destroy_nhwindow, safe_curs, safe_putstr, safe_putmixed, - safe_display_file, safe_start_menu, safe_add_menu, safe_end_menu, - safe_select_menu, safe_message_menu, - safe_mark_synch, - safe_wait_synch, -#ifdef CLIPPING - safe_cliparound, -#endif -#ifdef POSITIONBAR - safe_update_positionbar, -#endif - safe_print_glyph, safe_raw_print, safe_raw_print_bold, safe_nhgetch, - safe_nh_poskey, safe_nhbell, safe_doprev_message, safe_yn_function, - safe_getlin, safe_get_ext_cmd, safe_number_pad, safe_delay_output, -#ifdef CHANGE_COLOR /* the Mac uses a palette device */ - safe_change_color, -#ifdef MAC - safe_change_background, set_safe_font_name, -#endif - safe_get_color_string, -#endif - safe_outrip, - safe_preference_update, - safe_getmsghistory, safe_putmsghistory, - safe_status_init, - safe_status_finish, safe_status_enablefield, - safe_status_update, - safe_can_suspend, - safe_update_inventory, - safe_ctrl_nhwindow, -}; - -struct window_procs * -get_safe_procs(int optn) -{ - if (optn) { - /* include the slightly more functional stdc versions */ - safe_procs.win_raw_print = stdio_raw_print; - safe_procs.win_raw_print_bold = stdio_raw_print_bold; - safe_procs.win_nhgetch = stdio_nhgetch; - safe_procs.win_wait_synch = stdio_wait_synch; - if (optn == 2) - safe_procs.win_raw_print = stdio_nonl_raw_print; - } - return &safe_procs; -} - -/*ARGSUSED*/ -void -safe_init_nhwindows(int *argcp UNUSED, char **argv UNUSED) -{ - return; -} - -void -safe_player_selection(void) -{ - return; -} - -void -safe_askname(void) -{ - return; -} - -void -safe_get_nh_event(void) -{ - return; -} - -void -safe_suspend_nhwindows(const char *str UNUSED) -{ - return; -} - -void -safe_resume_nhwindows(void) -{ - return; -} - -void -safe_exit_nhwindows(const char *str UNUSED) -{ - return; -} - -winid -safe_create_nhwindow(int type UNUSED) -{ - return WIN_ERR; -} - -void -safe_clear_nhwindow(winid window UNUSED) -{ - return; -} - -/*ARGSUSED*/ -void -safe_display_nhwindow(winid window UNUSED, boolean blocking UNUSED) -{ - return; -} - -void -safe_dismiss_nhwindow(winid window UNUSED) -{ - return; -} - -void -safe_destroy_nhwindow(winid window UNUSED) -{ - return; -} - -void -safe_curs(winid window UNUSED, int x UNUSED, int y UNUSED) -{ - return; -} - -void -safe_putstr(winid window UNUSED, int attr UNUSED, const char *str UNUSED) -{ - return; -} - -void -safe_putmixed(winid window UNUSED, int attr UNUSED, const char *str UNUSED) -{ - return; -} - -void -safe_display_file(const char * fname UNUSED, boolean complain UNUSED) -{ - return; -} - -void -safe_start_menu(winid window UNUSED, unsigned long mbehavior UNUSED) -{ - return; -} - -/*ARGSUSED*/ -/* - * Add a menu item to the beginning of the menu list. This list is reversed - * later. - */ -void -safe_add_menu( - winid window UNUSED, /* window to use, must be of type NHW_MENU */ - const glyph_info *glyphinfo UNUSED, /* glyph plus glyph info */ - const anything *identifier UNUSED, /* what to return if selected */ - char ch UNUSED, /* keyboard accelerator (0 = pick our own) */ - char gch UNUSED, /* group accelerator (0 = no group) */ - int attr UNUSED, /* attribute for string (like safe_putstr()) */ - int clr UNUSED, /* colour for string */ - const char *str UNUSED, /* menu string */ - unsigned int itemflags UNUSED) /* itemflags such as marked as selected */ -{ - return; -} - -/* - * End a menu in this window, window must a type NHW_MENU. - */ -void -safe_end_menu( - winid window UNUSED, /* menu to use */ - const char *prompt UNUSED) /* prompt to for menu */ -{ - return; -} - -int -safe_select_menu( - winid window UNUSED, - int how UNUSED, - menu_item **menu_list UNUSED) -{ - return 0; -} - -/* special hack for treating top line --More-- as a one item menu */ -char -safe_message_menu( - char let UNUSED, - int how UNUSED, - const char *mesg UNUSED) -{ - return '\033'; -} - -void -safe_mark_synch(void) -{ -} - -void -safe_wait_synch(void) -{ -} - -#ifdef CLIPPING -void -safe_cliparound(int x UNUSED, int y UNUSED) -{ -} -#endif /* CLIPPING */ - -/* - * safe_print_glyph - * - * Print the glyph to the output device. Don't flush the output device. - */ -void -safe_print_glyph( - winid window UNUSED, - coordxy x UNUSED, - coordxy y UNUSED, - const glyph_info *glyphinfo UNUSED, - const glyph_info *bkglyphinfo UNUSED) -{ - return; -} - -void -safe_raw_print(const char *str UNUSED) -{ - return; -} - -void -safe_raw_print_bold(const char *str UNUSED) -{ - return; -} - -int -safe_nhgetch(void) -{ - return '\033'; -} - -/* - * return a key, or 0, in which case a mouse button was pressed - * mouse events should be returned as character positions in the map window. - * Since normal tty's don't have mice, just return a key. - */ -/*ARGSUSED*/ -int -safe_nh_poskey(coordxy *x UNUSED, coordxy *y UNUSED, int *mod UNUSED) -{ - return '\033'; -} - -void -win_safe_init(int dir UNUSED) -{ - return; -} - -#ifdef POSITIONBAR -void -safe_update_positionbar(char *posbar UNUSED) -{ - return; -} -#endif /* POSITIONBAR */ - -/* - * safe_status_init() - * -- initialize the port-specific data structures. - */ -void -safe_status_init(void) -{ - return; -} - -boolean -safe_can_suspend(void) -{ - return FALSE; -} - -void -safe_nhbell(void) -{ - return; -} - -int -safe_doprev_message(void) -{ - return 0; -} - -char -safe_yn_function(const char *query UNUSED, - const char *resp UNUSED, char def UNUSED) -{ - return '\033'; -} - -/*ARGSUSED*/ -void -safe_getlin(const char* prompt UNUSED, char *outbuf) -{ - Strcpy(outbuf, "\033"); -} - -int -safe_get_ext_cmd(void) -{ - return '\033'; -} - -void -safe_number_pad(int mode UNUSED) -{ - return; -} - -void -safe_delay_output(void) -{ - return; -} - -void -safe_outrip(winid tmpwin UNUSED, int how UNUSED, time_t when UNUSED) -{ - return; -} - -/*ARGSUSED*/ -void -safe_preference_update(const char *pref UNUSED) -{ - return; -} - -char * -safe_getmsghistory(boolean init UNUSED) -{ - return (char *) 0; -} - -void -safe_putmsghistory( - const char *msg UNUSED, - boolean is_restoring UNUSED) -{ -} - -void -safe_status_finish(void) -{ -} - -void -safe_status_enablefield( - int fieldidx UNUSED, - const char *nm UNUSED, - const char *fmt UNUSED, - boolean enable UNUSED) -{ -} - -/* call once for each field, then call with BL_FLUSH to output the result */ -void -safe_status_update( - int idx UNUSED, - genericptr_t ptr UNUSED, - int chg UNUSED, - int percent UNUSED, - int color UNUSED, - unsigned long *colormasks UNUSED) -{ -} - -void -safe_update_inventory(int arg UNUSED) -{ - return; -} - -#ifdef WIN32CON -extern win_request_info *tty_ctrl_nhwindow(winid window UNUSED, - int request UNUSED, - win_request_info *wri UNUSED); -#endif - -win_request_info * -safe_ctrl_nhwindow( - winid window UNUSED, - int request UNUSED, - win_request_info *wri UNUSED) -{ -#ifdef WIN32CON - return (*tty_ctrl_nhwindow)(window, request, wri); -#else - return (win_request_info *) 0; -#endif -} - -/************************************************************** - * These are some optionally selectable routines that add - * some base functionality over the safe_* versions above. - * The safe_* versions are primarily designed to ensure that - * there are no null function pointers remaining at early - * game startup/initialization time. - * - * The slightly more functional versions in here should be kept - * free of platform-specific code or OS-specific code. If you - * want to use versions that involve platform-specific or - * OS-specific code, go right ahead but use your own replacement - * version of the functions in a platform-specific or - * OS-specific source file, not in here. - ***************************************************************/ - -/* Add to your code: windowprocs.win_raw_print = stdio_wait_synch; */ -void -stdio_wait_synch(void) -{ - char valid[] = {' ', '\n', '\r', '\033', '\0'}; - - fprintf(stdout, "--More--"); - (void) fflush(stdout); - while (!strchr(valid, nhgetch())) - ; -} - -/* Add to your code: windowprocs.win_raw_print = stdio_raw_print; */ -void -stdio_raw_print(const char *str) -{ - if (str) - fprintf(stdout, "%s\n", str); - return; -} - -/* no newline variation, add to your code: - windowprocs.win_raw_print = stdio_nonl_raw_print; */ -void -stdio_nonl_raw_print(const char *str) -{ - if (str) - fprintf(stdout, "%s", str); - return; -} - -/* Add to your code: windowprocs.win_raw_print_bold = stdio_raw_print_bold; */ -void -stdio_raw_print_bold(const char *str) -{ - stdio_raw_print(str); - return; -} - -/* Add to your code: windowprocs.win_nhgetch = stdio_nhgetch; */ -int -stdio_nhgetch(void) -{ - return getchar(); -} - -#ifdef CHANGE_COLOR -void -safe_change_color(int color UNUSED, long rgb UNUSED, int reverse UNUSED) -{ -} - -char * -safe_get_color_string(void) -{ - return (""); -} - - -#endif - -/* safeprocs.c */ diff --git a/win/tty/wintty.c b/win/tty/wintty.c index 61338542b..4cbe65a2a 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -3559,8 +3559,9 @@ assesstty( short *offx, short *offy, long *rows, long *cols, long *maxcol, long *minrow, long *maxrow) { - boolean inuse_only = (invmode & InvInUse) != 0, - show_gold = (invmode & InvShowGold) != 0 && !inuse_only; + boolean inuse_only = (invmode & (int) InvInUse) != 0, + show_gold = (invmode & (int) InvShowGold) != 0 + && !inuse_only; int perminv_minrow = tty_perminv_minrow + (show_gold ? 1 : 0); if (!ttyDisplay) { diff --git a/win/win32/NetHackW.c b/win/win32/NetHackW.c index 92b851c7a..56d8bb7c7 100644 --- a/win/win32/NetHackW.c +++ b/win/win32/NetHackW.c @@ -15,10 +15,6 @@ #include "mhmain.h" #include "mhmap.h" -#if !defined(SAFEPROCS) -#error You must #define SAFEPROCS to build NetHackW.c -#endif - /* Borland and MinGW redefine "boolean" in shlwapi.h, so just use the little bit we need */ typedef struct _DLLVERSIONINFO { @@ -99,32 +95,6 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, UNREFERENCED_PARAMETER(lpCmdLine); UNREFERENCED_PARAMETER(nCmdShow); - /* - * Get a set of valid safe windowport function - * pointers during early startup initialization. - * - * When get_safe_procs is called with 0 as the param, - * non-functional, but safe function pointers are set - * for all windowport routines. - * - * When get_safe_procs is called with 1 as the param, - * raw_print, raw_print_bold, and wait_synch, and nhgetch - * are set to use C stdio routines via stdio_raw_print, - * stdio_raw_print_bold, stdio_wait_synch, and - * stdio_nhgetch. - */ - windowprocs = *get_safe_procs(0); - - /* - * Now we are going to override a couple - * of the windowprocs functions so that - * error messages are handled in a suitable - * way for the graphical version. - */ - windowprocs.win_raw_print = mswin_raw_print; - windowprocs.win_raw_print_bold = mswin_raw_print_bold; - windowprocs.win_wait_synch = mswin_wait_synch; - win10_init(); early_init(0, NULL); /* Change as needed to support CRASHREPORT */ diff --git a/win/win32/mswproc.c b/win/win32/mswproc.c index abef776da..55ba2f696 100644 --- a/win/win32/mswproc.c +++ b/win/win32/mswproc.c @@ -734,9 +734,6 @@ mswin_exit_nhwindows(const char *str) /* Write Window settings to the registry */ mswin_write_reg(); - /* set things back to failsafes */ - windowprocs = *get_safe_procs(0); - /* and make sure there is still a way to communicate something */ windowprocs.win_raw_print = mswin_raw_print; windowprocs.win_raw_print_bold = mswin_raw_print_bold; From 1745d0bccedafd033406017c4c47c634915a704a Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 5 Apr 2026 13:36:06 -0400 Subject: [PATCH 397/442] follow-up: cross compile for msdos --- sys/msdos/msdos.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/msdos/msdos.c b/sys/msdos/msdos.c index 8a059008d..86adbd4bb 100644 --- a/sys/msdos/msdos.c +++ b/sys/msdos/msdos.c @@ -449,7 +449,7 @@ filesize_nh(char *file) * Chdrive() changes the default drive. */ void -chdrive(char *str) +chdrive(const char *str) { #define SELECTDISK 0x0E char *ptr; From 2790fa22b2b0560d6e79a66184afd92ef504b413 Mon Sep 17 00:00:00 2001 From: nhw_cron Date: Sun, 5 Apr 2026 13:16:07 -0400 Subject: [PATCH 398/442] This is cron-daily v1-Jan-12-2026. 000files updated: Files --- Files | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Files b/Files index abdb08205..a91ef523d 100644 --- a/Files +++ b/Files @@ -642,8 +642,8 @@ win/share: (files for versions using optional tiles) bmptiles.c gifread.c giftiles.c monsters.txt nhicns.uu nhsplash.xpm objects.txt other.txt ppmwrite.c renumtiles.pl -safeproc.c thintile.c tile.doc tile.h tile2bmp.c -tilemap.c tileset.c tiletext.c tiletxt.c +thintile.c tile.doc tile.h tile2bmp.c tilemap.c +tileset.c tiletext.c tiletxt.c win/shim: (file in top directory) From 0a3b8a7b720f0dfae628e3ec3d9ead27b257c048 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 5 Apr 2026 20:28:43 -0400 Subject: [PATCH 399/442] Makefile.nmake tweaking --- sys/windows/Makefile.nmake | 229 +++++++++++++++++++------------------ 1 file changed, 116 insertions(+), 113 deletions(-) diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index fe481ecc2..d450ecbe8 100644 --- a/sys/windows/Makefile.nmake +++ b/sys/windows/Makefile.nmake @@ -243,7 +243,7 @@ GAMEDIR = $(R_GAMEDIR)$(BACKSLASH) #U_BinDir = $(BinDir:\=/) #U_PkgDir = $(PkgDir:\=/) #U_SOUNDDIR = $(SOUNDDIR:\=/) -U_GAMEDIR = $(GAMEDIR:\=/) +U_GAMEDIR = $(R_GAMEDIR:\=/) # #!MESSAGE $(U_SRC) #!MESSAGE $(U_GAMEDIR) @@ -466,37 +466,37 @@ DLBDEF = # Object file directories. # -R_OBJTTY_B = objtty -R_OBJGUI_B = objgui -R_OBJUTIL_B = objutil -R_OBJLUA_B = objlua -R_OBJPDC_B = objpdc -R_OBJPDCC_B = objpdcc -R_OBJPDCG_B = objpdcg +R_OBJTTY_B = objtty +R_OBJGUI_B = objgui +R_OBJUTIL_B = objutil +R_OBJLUA_B = objlua +R_OBJPDC_B = objpdc +R_OBJPDCTXT_B = objpdctxt +R_OBJPDCGUI_B = objpdcgui OBJTTY_B = $(R_OBJTTY_B)$(BACKSLASH) OBJGUI_B = $(R_OBJGUI_B)$(BACKSLASH) OBJUTIL_B = $(R_OBJUTIL_B)$(BACKSLASH) OBJLUA_B = $(R_OBJLUA_B)$(BACKSLASH) OBJPDC_B = $(R_OBJPDC_B)$(BACKSLASH) -OBJPDCC_B = $(R_OBJPDCC_B)$(BACKSLASH) -OBJPDCG_B = $(R_OBJPDCG_B)$(BACKSLASH) +OBJPDCTXT_B = $(R_OBJPDCTXT_B)$(BACKSLASH) +OBJPDCGUI_B = $(R_OBJPDCGUI_B)$(BACKSLASH) R_OBJTTY = $(OBJTTY_B)$(TARGET_CPU) R_OBJGUI = $(OBJGUI_B)$(TARGET_CPU) R_OBJUTIL = $(OBJUTIL_B)$(TARGET_CPU) R_OBJLUA = $(OBJLUA_B)$(TARGET_CPU) R_OBJPDC = $(OBJPDC_B)$(TARGET_CPU) -R_OBJPDCC = $(OBJPDCC_B)$(TARGET_CPU) -R_OBJPDCG = $(OBJPDCG_B)$(TARGET_CPU) +R_OBJPDCTXT = $(OBJPDCTXT_B)$(TARGET_CPU) +R_OBJPDCGUI = $(OBJPDCGUI_B)$(TARGET_CPU) OBJTTY = $(R_OBJTTY)$(BACKSLASH) OBJGUI = $(R_OBJGUI)$(BACKSLASH) OBJUTIL = $(R_OBJUTIL)$(BACKSLASH) OBJLUA = $(R_OBJLUA)$(BACKSLASH) OBJPDC = $(R_OBJPDC)$(BACKSLASH) -OBJPDCC = $(R_OBJPDCC)$(BACKSLASH) -OBJPDCG = $(R_OBJPDCG)$(BACKSLASH) +OBJPDCTXT = $(R_OBJPDCTXT)$(BACKSLASH) +OBJPDCGUI = $(R_OBJPDCGUI)$(BACKSLASH) # # Shorten up the location for some files @@ -507,8 +507,8 @@ OGUI = $(OBJGUI) OUTL = $(OBJUTIL) OLUA = $(OBJLUA) OPDC = $(OBJPDC) -OPDCC = $(OBJPDCC) -OPDCG = $(OBJPDCG) +OPDCTXT = $(OBJPDCTXT) +OPDCGUI = $(OBJPDCGUI) U = $(UTIL) @@ -598,20 +598,23 @@ LUAOBJFILES = $(LUAOBJFILES) $(OLUA)lbitlib.o #================================================================= !IF "$(ADD_CURSES)" == "Y" +PDCTXTFOLDER = wincon +#PDCTXTFOLDER = vt PDCURSES_CURSES_H = $(PDCURSES_TOP)curses.h PDCURSES_CURSPRIV_H = $(PDCURSES_TOP)curspriv.h PDCURSES_HEADERS = $(PDCURSES_CURSES_H) $(PDCURSES_CURSPRIV_H) R_PDCSRC = $(PDCURSES_TOP)pdcurses PDCSRC = $(R_PDCSRC)$(BACKSLASH) !IF "$(CURSES_CONSOLE)" == "Y" -R_PDCWINCON = $(PDCURSES_TOP)wincon -PDCWINCON = $(PDCURSES_TOP)wincon$(BACKSLASH) +R_PDCTXT = $(PDCURSES_TOP)$(PDCTXTFOLDER) +PDCTXT = $(PDCURSES_TOP)$(PDCTXTFOLDER)$(BACKSLASH) +#!MESSAGE PDCTXT = $(PDCTXT) !ENDIF !IF "$(CURSES_GRAPHICAL)" == "Y" -R_PDCWINGUI = $(PDCURSES_TOP)wingui -PDCWINGUI = $(PDCURSES_TOP)wingui$(BACKSLASH) +R_PDCGUI = $(PDCURSES_TOP)wingui +PDCGUI = $(PDCURSES_TOP)wingui$(BACKSLASH) !ENDIF -PDCDEP = $(PDCURSES_TOP)curses.h +PDCDEP = $(PDCURSES_TOP)curses.h PDCCOMMONOBJS = $(OPDC)addch.o $(OPDC)addchstr.o $(OPDC)addstr.o $(OPDC)attr.o $(OPDC)beep.o \ $(OPDC)bkgd.o $(OPDC)border.o $(OPDC)clear.o $(OPDC)color.o $(OPDC)debug.o \ @@ -625,23 +628,23 @@ PDCCOMMONOBJS = $(OPDC)addch.o $(OPDC)addchstr.o $(OPDC)addstr.o $(OPDC)attr.o $ $(OPDC)util.o $(OPDC)window.o PDCINCL = /I$(R_PDCURSES_TOP) /I$(R_PDCSRC) !IF "$(CURSES_CONSOLE)" == "Y" -PDCINCLCON = /I$(PDCWINCON) -PDCWINCONOBJS = $(OPDCC)pdcclip.o $(OPDCC)pdcdisp.o $(OPDCC)pdcgetsc.o \ - $(OPDCC)pdckbd.o $(OPDCC)pdcscrn.o $(OPDCC)pdcsetsc.o $(OPDCC)pdcutil.o +PDCINCLTXT = /I$(R_PDCTXT) +PDCTXTOBJS = $(OPDCTXT)pdcclip.o $(OPDCTXT)pdcdisp.o $(OPDCTXT)pdcgetsc.o \ + $(OPDCTXT)pdckbd.o $(OPDCTXT)pdcscrn.o $(OPDCTXT)pdcsetsc.o $(OPDCTXT)pdcutil.o !ENDIF !IF "$(CURSES_GRAPHICAL)" == "Y" -PDCWINGUIOBJS = $(OPDCG)pdcclip.o $(OPDCG)pdcdisp.o $(OPDCG)pdcgetsc.o \ - $(OPDCG)pdckbd.o $(OPDCG)pdcscrn.o $(OPDCG)pdcsetsc.o $(OPDCG)pdcutil.o -PDCINCLGUI = /I$(PDCWINCON) +PDCGUIOBJS = $(OPDCGUI)pdcclip.o $(OPDCGUI)pdcdisp.o $(OPDCGUI)pdcgetsc.o \ + $(OPDCGUI)pdckbd.o $(OPDCGUI)pdcscrn.o $(OPDCGUI)pdcsetsc.o $(OPDCGUI)pdcutil.o +PDCINCLGUI = /I$(R_PDCGUI) !ENDIF !ELSE PDCDEP= PDCCOMMONOBJS= -PDCWINCONOBJS= -PDCWINGUIOBJS= +PDCTXTOBJS= +PDCGUIOBJS= PDCINCL= PDINCLGUI= -PDCINCLCON= +PDCINCLTXT= !ENDIF @@ -804,14 +807,14 @@ CURSESOBJ= $(OTTY)cursdial.o $(OTTY)cursinit.o $(OTTY)cursinvt.o \ $(OTTY)cursstat.o $(OTTY)curswins.o !IF "$(CURSES_CONSOLE)" == "Y" CURSESWINCONOBJS = $(CURSESOBJ) -PDCWINCONLIB = $(LIBDIR)$(PDCDIST)-wincon-$(TARGET_CPU)-static.lib +PDCTXTLIB = $(LIBDIR)$(R_PDCDIST)-$(PDCTXTFOLDER)-$(TARGET_CPU)-static.lib CURSESCONDEF1=-D"CURSES_GRAPHICS" -DPDC_NCMOUSE #CURSESDEF2=-D"CURSES_BRIEF_INCLUDE" -DCHTYPE_32 CURSESDEF2=-DCURSES_UNICODE $(PDCURSESFLAGS) !ENDIF !IF "$(CURSES_GRAPHICAL)" == "Y" CURSESWINGUIOBJS = $(CURSESOBJ) $(OGUI)guitty.o -PDCWINGUILIB = $(LIBDIR)$(PDCDIST)-wingui-$(TARGET_CPU)-static.lib +PDCGUILIB = $(LIBDIR)$(R_PDCDIST)-wingui-$(TARGET_CPU)-static.lib CURSESGUIDEF1=-D"CURSES_GRAPHICS" -DPDC_NCMOUSE #CURSESDEF2=-D"CURSES_BRIEF_INCLUDE" -DCHTYPE_32 CURSESDEF2=-DCURSES_UNICODE $(PDCURSESFLAGS) @@ -823,8 +826,8 @@ CURSESDEF2=-DCURSES_UNICODE $(PDCURSESFLAGS) !UNDEF CURSESOBJ !UNDEF CURSESWINCONOBJS !UNDEF CURSESWINGUIOBJS -!UNDEF PDCWINCONLIB -!UNDEF PDCWINGUILIB +!UNDEF PDCTXTLIB +!UNDEF PDCGUILIB !ENDIF OUTLHACKLIBOBJS = $(OUTL)hacklib.o @@ -1517,7 +1520,7 @@ DLB = #!MESSAGE {$(R_PDCSRC)}.c{$(R_OBJPDC)}.o: #!MESSAGE PDCINCL = $(PDCINCL) #!MESSAGE PDCINCLGUI = $(PDCINCLGUI) -#!MESSAGE PDCINCLCON = $(PDCINCLCON) +#!MESSAGE PDCINCLTXT = $(PDCINCLTXT) #!MESSAGE $(Q)$(CC) /wd4244 /wd4267 /wd4774 $(R_PDCINCL) $(CFLAGS:-w44774= ) $(CURSESDEF2) /wd5262 $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -FoX2 X1 # @@ -1529,15 +1532,15 @@ DLB = $(Q)$(CC) /wd4244 /wd4267 /wd4774 $(PDCINCL) $(CFLAGS:-w44774= ) $(CURSESDEF2) /wd5262 $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< !IF "$(CURSES_CONSOLE)" == "Y" -{$(R_PDCWINCON)}.c{$(R_OBJPDCC)}.o: - $(Q)$(CC) /wd4244 /wd4267 /wd4774 $(PDCINCL) $(PDCINCLCON) $(CFLAGS) $(CURSESDEF2) /wd5262 $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< -#!MESSAGE {$(R_PDCWINCON)}.c{$(R_OBJPDCC)}.o: +{$(R_PDCTXT)}.c{$(R_OBJPDCTXT)}.o: + $(Q)$(CC) /wd4244 /wd4267 /wd4774 $(PDCINCL) $(PDCINCLTXT) $(CFLAGS) $(CURSESDEF2) /wd5262 $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< +#!MESSAGE {$(R_PDCTXT)}.c{$(R_OBJPDCTXT)}.o: !ENDIF !IF "$(CURSES_GRAPHICAL)" == "Y" -{$(R_PDCWINGUI)}.c{$(R_OBJPDCG)}.o: +{$(R_PDCGUI)}.c{$(R_OBJPDCGUI)}.o: $(Q)$(CC) /wd4244 /wd4267 /wd4774 $(PDCINCL) $(PDCINCLGUI) $(CFLAGS) $(CURSESDEF2) /wd5262 $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< -#!MESSAGE {$(R_PDCWINGUI)}.c{$(R_OBJPDCG)}.o: +#!MESSAGE {$(R_PDCGUI)}.c{$(R_OBJPDCGUI)}.o: !ENDIF !ENDIF @@ -1678,16 +1681,16 @@ GAMEOBJ=$(GAMEOBJ:^ =^ $(GAMEDIR)NetHack.exe : gamedir.tag $(OTTY)consoletty.o \ $(ALLOBJTTY) $(CURSESWINCONOBJS) \ $(TTYSOUNDOBJS) $(OTTY)date.o $(OTTY)console.res \ - $(LUALIB) $(TTYOBJTTY) $(PDCWINCONLIB) $(PDCWINCONOBJS) $(OTTYHACKLIB) + $(LUALIB) $(TTYOBJTTY) $(PDCTXTLIB) $(PDCTXTOBJS) $(OTTYHACKLIB) @if not exist $(GAMEDIR)*.* mkdir $(R_GAMEDIR) @echo Linking $(@:\=/) $(link) $(LFLAGS) $(conlflags) /STACK:2048 /PDB:$(GAMEDIR)$(@B).PDB /MAP:$(OTTY)$(@B).MAP \ - $(LIBS) $(PDCWINCONLIB) $(LUALIB) $(TTYSOUNDLIBS) $(OTTYHACKLIB) \ + $(LIBS) $(PDCTXTLIB) $(LUALIB) $(TTYSOUNDLIBS) $(OTTYHACKLIB) \ $(conlibs) $(BCRYPT) -out:$@ @<<$(@B).lnk $(GAMEOBJTTY) $(ALLOBJTTY) $(TTYOBJTTY) - $(TTYSOUNDOBJS) $(CURSESWINCONOBJS) $(PDCWINCONOBJS) + $(TTYSOUNDOBJS) $(CURSESWINCONOBJS) $(PDCTXTOBJS) $(OTTY)consoletty.o $(OTTY)date.o $(OTTY)console.res @@ -1702,18 +1705,18 @@ $(GAMEDIR)NetHackW.exe : gamedir.tag $(OGUI)tile.o \ $(ALLOBJGUI) $(GAMEOBJGUI) $(GUIOBJ) $(GUISOUNDOBJS) \ $(CURSESWINGUIOBJS) $(OGUI)date.o \ $(OGUI)NetHackW.res \ - $(LUALIB) $(PDCWINGUILIB) $(PDCWINGUIOBJS) $(OGUIHACKLIB) + $(LUALIB) $(PDCGUILIB) $(PDCGUIOBJS) $(OGUIHACKLIB) @if not exist $(GAMEDIR)*.* mkdir $(R_GAMEDIR) @echo Linking $(@:\=/) $(link) $(LFLAGS) $(guilflags) /STACK:2048 /PDB:$(GAMEDIR)$(@B).PDB \ /MAP:$(OGUI)$(@B).MAP \ - $(LIBS) $(PDCWINGUILIB) $(LUALIB) \ + $(LIBS) $(PDCGUILIB) $(LUALIB) \ $(GUISOUNDLIBS) $(OGUIHACKLIB) \ $(guilibs) $(COMCTRL) $(BCRYPT) -out:$@ @<<$(@B).lnk $(GAMEOBJGUI) $(ALLOBJGUI) $(GUIOBJ) - $(GUISOUNDOBJS) $(CURSESWINGUIOBJS) $(PDCWINGUIOBJS) + $(GUISOUNDOBJS) $(CURSESWINGUIOBJS) $(PDCGUIOBJS) $(OGUI)tile.o $(OGUI)date.o $(OGUI)NetHackW.res @@ -1978,14 +1981,14 @@ opdcdir$(TARGET_CPU).tag: @if not exist $(OBJPDC)*.* mkdir $(R_OBJPDC) @echo directory created >$@ -opdccdir$(TARGET_CPU).tag: - @if not exist $(OBJPDCC)*.* echo creating directory $(OBJPDCC:\=/) - @if not exist $(OBJPDCC)*.* mkdir $(R_OBJPDCC) +opdctxtdir$(TARGET_CPU).tag: + @if not exist $(OBJPDCTXT)*.* echo creating directory $(OBJPDCTXT:\=/) + @if not exist $(OBJPDCTXT)*.* mkdir $(R_OBJPDCTXT) @echo directory created >$@ -opdcgdir$(TARGET_CPU).tag: - @if not exist $(OBJPDCG)*.* echo creating directory $(OBJPDCG:\=/) - @if not exist $(OBJPDCG)*.* mkdir $(R_OBJPDCG) +opdcguidir$(TARGET_CPU).tag: + @if not exist $(OBJPDCGUI)*.* echo creating directory $(OBJPDCGUI:\=/) + @if not exist $(OBJPDCGUI)*.* mkdir $(R_OBJPDCGUI) @echo directory created >$@ libdir.tag: @@ -2323,14 +2326,14 @@ $(SRC)x11tiles: $(U)tile2x11.exe $(WSHR)monsters.txt $(WSHR)objects.txt \ #=============================================================================== !IF "$(ADD_CURSES)" == "Y" !IF "$(CURSES_CONSOLE)" == "Y" -$(PDCWINCONLIB) : $(PDCCOMMONOBJS) $(PDCWINCONOBJS) +$(PDCTXTLIB) : $(PDCCOMMONOBJS) $(PDCTXTOBJS) @echo Building library $@ from $** - @$(librarian) -nologo /out:$@ $(PDCCOMMONOBJS) $(PDCWINCONOBJS) + @$(librarian) -nologo /out:$@ $(PDCCOMMONOBJS) $(PDCTXTOBJS) !ENDIF !IF "$(CURSES_GRAPHICAL)" == "Y" -$(PDCWINGUILIB) : $(PDCCOMMONOBJS) $(PDCWINGUIOBJS) +$(PDCGUILIB) : $(PDCCOMMONOBJS) $(PDCGUIOBJS) @echo Building library $@ from $** - @$(librarian) -nologo /out:$@ $(PDCCOMMONOBJS) $(PDCWINGUIOBJS) + @$(librarian) -nologo /out:$@ $(PDCCOMMONOBJS) $(PDCGUIOBJS) !ENDIF !ENDIF $(OGUI)guitty.o: $(MSWSYS)guitty.c $(WINDHDR) $(HACK_H) $(TILE_H) @@ -2808,7 +2811,7 @@ $(DBGSYMZIP): $(PDBTOZIP) binary: envchk.tag libdir.tag ottydir$(TARGET_CPU).tag \ outldir$(TARGET_CPU).tag oguidir$(TARGET_CPU).tag \ oluadir$(TARGET_CPU).tag opdcdir$(TARGET_CPU).tag \ - opdccdir$(TARGET_CPU).tag opdcgdir$(TARGET_CPU).tag \ + opdctxtdir$(TARGET_CPU).tag opdcguidir$(TARGET_CPU).tag \ $(LUASRC)lua.h $(PDCDEP) \ $(INCL)nhlua.h $(OUTL)utility.tag \ $(DAT)data $(DAT)rumors $(DAT)oracles $(DAT)engrave \ @@ -2821,61 +2824,61 @@ binary: envchk.tag libdir.tag ottydir$(TARGET_CPU).tag \ #=============================================================================== spotless: clean - if exist $(GAMEDIR)NetHack.exe del $(GAMEDIR)NetHack.exe + if exist $(GAMEDIR)NetHack.exe del $(GAMEDIR)NetHack.exe if exist $(GAMEDIR)NetHackW.exe del $(GAMEDIR)NetHackW.exe - if exist $(GAMEDIR)NetHack.pdb del $(GAMEDIR)NetHack.pdb - if exist $(GAMEDIR)nhdat$(NHV) del $(GAMEDIR)nhdat$(NHV) - if exist $(INCL)date.h del $(INCL)date.h - if exist $(INCL)onames.h del $(INCL)onames.h - if exist $(INCL)pm.h del $(INCL)pm.h + if exist $(GAMEDIR)NetHack.pdb del $(GAMEDIR)NetHack.pdb + if exist $(GAMEDIR)nhdat$(NHV) del $(GAMEDIR)nhdat$(NHV) + if exist $(INCL)date.h del $(INCL)date.h + if exist $(INCL)onames.h del $(INCL)onames.h + if exist $(INCL)pm.h del $(INCL)pm.h if exist $(U)*.lnk del $(U)*.lnk if exist $(U)*.map del $(U)*.map - if exist $(DAT)data del $(DAT)data - if exist $(DAT)rumors del $(DAT)rumors - if exist $(DAT)engrave del $(DAT)engrave - if exist $(DAT)epitaph del $(DAT)epitaph - if exist $(DAT)bogusmon del $(DAT)bogusmon - if exist $(DAT)porthelp del $(DAT)porthelp + if exist $(DAT)data del $(DAT)data + if exist $(DAT)rumors del $(DAT)rumors + if exist $(DAT)engrave del $(DAT)engrave + if exist $(DAT)epitaph del $(DAT)epitaph + if exist $(DAT)bogusmon del $(DAT)bogusmon + if exist $(DAT)porthelp del $(DAT)porthelp if exist nhdat$(NHV). del nhdat$(NHV). if exist outldirx86.tag del outldirx86.tag if exist ottydirx86.tag del ottydirx86.tag if exist oguidirx86.tag del oguidirx86.tag if exist oluadirx86.tag del oluadirx86.tag if exist opdcdirx86.tag del opdcdirx86.tag - if exist opdccdirx86.tag del opdccdirx86.tag - if exist opdcgdirx86.tag del opdcgdirx86.tag + if exist opdctxtdirx86.tag del opdctxtdirx86.tag + if exist opdcguidirx86.tag del opdcguidirx86.tag if exist outldirx64.tag del outldirx64.tag if exist ottydirx64.tag del ottydirx64.tag if exist oguidirx64.tag del oguidirx64.tag if exist oluadirx64.tag del oluadirx64.tag if exist opdcdirx64.tag del opdcdirx64.tag - if exist opdccdirx64.tag del opdccdirx64.tag - if exist opdcgdirx64.tag del opdcgdirx64.tag + if exist opdctxtdirx64.tag del opdctxtdirx64.tag + if exist opdcguidirx64.tag del opdcguidirx64.tag if exist outldirarm64.tag del outldirarm64.tag if exist ottydirarm64.tag del ottydirarm64.tag if exist oguidirarm64.tag del oguidirarm64.tag if exist oluadirarm64.tag del oluadirarm64.tag if exist opdcdirarm64.tag del opdcdirarm64.tag - if exist opdccdirarm64.tag del opdccdirarm64.tag - if exist opdcgdirarm64.tag del opdcgdirarm64.tag + if exist opdctxtdirarm64.tag del opdctxtdirarm64.tag + if exist opdcguidirarm64.tag del opdcguidirarm64.tag if exist libdir.tag del libdir.tag if exist gamedir.tag del gamedir.tag - if exist $(MSWIN)mnsel.bmp del $(MSWIN)mnsel.bmp - if exist $(MSWIN)mnselcnt.bmp del $(MSWIN)mnselcnt.bmp - if exist $(MSWIN)mnunsel.bmp del $(MSWIN)mnunsel.bmp - if exist $(MSWIN)petmark.bmp del $(MSWIN)petmark.bmp - if exist $(MSWIN)pilemark.bmp del $(MSWIN)pilemark.bmp - if exist $(MSWIN)rip.bmp del $(MSWIN)rip.bmp - if exist $(MSWIN)splash.bmp del $(MSWIN)splash.bmp - if exist $(MSWIN)nethack.ico del $(MSWIN)nethack.ico - if exist $(MSWSYS)nethack.ico del $(MSWSYS)nethack.ico + if exist $(MSWIN)mnsel.bmp del $(MSWIN)mnsel.bmp + if exist $(MSWIN)mnselcnt.bmp del $(MSWIN)mnselcnt.bmp + if exist $(MSWIN)mnunsel.bmp del $(MSWIN)mnunsel.bmp + if exist $(MSWIN)petmark.bmp del $(MSWIN)petmark.bmp + if exist $(MSWIN)pilemark.bmp del $(MSWIN)pilemark.bmp + if exist $(MSWIN)rip.bmp del $(MSWIN)rip.bmp + if exist $(MSWIN)splash.bmp del $(MSWIN)splash.bmp + if exist $(MSWIN)nethack.ico del $(MSWIN)nethack.ico + if exist $(MSWSYS)nethack.ico del $(MSWSYS)nethack.ico if exist $(U)recover.exe del $(U)recover.exe if exist $(U)tile2bmp.exe del $(U)tile2bmp.exe if exist $(U)tilemap.exe del $(U)tilemap.exe if exist $(U)uudecode.exe del $(U)uudecode.exe if exist $(U)dlb.exe del $(U)dlb.exe !IF "$(ADD_CURSES)" == "Y" - if exist $(LIBDIR)$(PDCDIST)-wincon-$(TARGET_CPU)-static.lib del $(LIBDIR)$(PDCDIST)-wincon-$(TARGET_CPU)-static.lib + if exist $(LIBDIR)$(PDCDIST)-$(PDCTXTFOLDER)-$(TARGET_CPU)-static.lib del $(LIBDIR)$(PDCDIST)-$(PDCTXTFOLDER)-$(TARGET_CPU)-static.lib if exist $(LIBDIR)$(PDCDIST)-wingui-$(TARGET_CPU)-static.lib del $(LIBDIR)$(PDCDIST)-wingui-$(TARGET_CPU)-static.lib !ENDIF if exist $(LUALIB) del $(LUALIB) @@ -2886,23 +2889,23 @@ spotless: clean if exist $(DAT)guioptions del $(DAT)guioptions if exist $(DAT)data del $(DAT)data if exist tilemappings.lst del tilemappings.lst - if exist $(SndWavDir)*.wav del $(SndWavDir)*.wav + if exist $(SndWavDir)*.wav del $(SndWavDir)*.wav if exist $(MAINZIP) del $(MAINZIP) if exist $(DBGSYMZIP) del $(DBGSYMZIP) - if exist $(OBJTTY)* rmdir $(OBJTTY) /s /Q - if exist $(OBJGUI)* rmdir $(OBJGUI) /s /Q - if exist $(OBJUTIL)* rmdir $(OBJUTIL) /s /Q - if exist $(OBJLUA)* rmdir $(OBJLUA) /s /Q - if exist $(OBJPDC)* rmdir $(OBJPDC) /s /Q - if exist $(OBJPDCC)* rmdir $(OBJPDCC) /s /Q - if exist $(OBJPDCG)* rmdir $(OBJPDCG) /s /Q - if exist $(OBJTTY_B)* rmdir $(OBJTTY_B) /s /Q - if exist $(OBJGUI_B)* rmdir $(OBJGUI_B) /s /Q - if exist $(OBJUTIL_B)* rmdir $(OBJUTIL_B) /s /Q - if exist $(OBJLUA_B)* rmdir $(OBJLUA_B) /s /Q - if exist $(OBJPDC_B)* rmdir $(OBJPDC_B) /s /Q - if exist $(OBJPDCC_B)* rmdir $(OBJPDCC_B) /s /Q - if exist $(OBJPDCG_B)* rmdir $(OBJPDCG_B) /s /Q + if exist $(OBJTTY)* rmdir $(OBJTTY) /s /Q + if exist $(OBJGUI)* rmdir $(OBJGUI) /s /Q + if exist $(OBJUTIL)* rmdir $(OBJUTIL) /s /Q + if exist $(OBJLUA)* rmdir $(OBJLUA) /s /Q + if exist $(OBJPDC)* rmdir $(OBJPDC) /s /Q + if exist $(OBJPDCTXT)* rmdir $(OBJPDCTXT) /s /Q + if exist $(OBJPDCGUI)* rmdir $(OBJPDCGUI) /s /Q + if exist $(OBJTTY_B)* rmdir $(OBJTTY_B) /s /Q + if exist $(OBJGUI_B)* rmdir $(OBJGUI_B) /s /Q + if exist $(OBJUTIL_B)* rmdir $(OBJUTIL_B) /s /Q + if exist $(OBJLUA_B)* rmdir $(OBJLUA_B) /s /Q + if exist $(OBJPDC_B)* rmdir $(OBJPDC_B) /s /Q + if exist $(OBJPDCTXT_B)* rmdir $(OBJPDCTXT_B) /s /Q + if exist $(OBJPDCGUI_B)* rmdir $(OBJPDCGUI_B) /s /Q clean: if exist binary.tag del binary.tag @@ -2914,28 +2917,28 @@ clean: if exist $(OUTL)utility.tag del $(OUTL)utility.tag if exist $(OTTY)sp_lev.tag del $(OTTY)sp_lev.tag if exist $(OGUI)sp_lev.tag del $(OGUI)sp_lev.tag - if exist $(SRC)tile.c del $(SRC)tile.c - if exist $(INCL)nhlua.h del $(INCL)nhlua.h + if exist $(SRC)tile.c del $(SRC)tile.c + if exist $(INCL)nhlua.h del $(INCL)nhlua.h if exist $(U)makedefs.exe del $(U)makedefs.exe if exist $(U)dlb_main.exe del $(U)dlb_main.exe if exist $(U)tile2bmp.exe del $(U)tile2bmp.exe if exist $(U)tilemap.exe del $(U)tilemap.exe - if exist $(SRC)*.lnk del $(SRC)*.lnk - if exist $(DAT)dlb.lst del $(DAT)dlb.lst + if exist $(SRC)*.lnk del $(SRC)*.lnk + if exist $(DAT)dlb.lst del $(DAT)dlb.lst if exist $(OUTL)*.o del $(OUTL)*.o if exist $(OTTY)*.o del $(OTTY)*.o if exist $(OGUI)*.o del $(OGUI)*.o if exist $(OLUA)*.o del $(OLUA)*.o if exist $(OPDC)*.o del $(OPDC)*.o - if exist $(OPDCC)*.o del $(OPDCC)*.o - if exist $(OPDCG)*.o del $(OPDCG)*.o + if exist $(OPDCTXT)*.o del $(OPDCTXT)*.o + if exist $(OPDCGUI)*.o del $(OPDCGUI)*.o if exist $(OUTL)*.PDB del $(OUTL)*.PDB if exist $(OTTY)*.PDB del $(OTTY)*.PDB if exist $(OGUI)*.PDB del $(OGUI)*.PDB if exist $(OLUA)*.PDB del $(OLUA)*.PDB if exist $(OPDC)*.PDB del $(OPDC)*.PDB - if exist $(OPDCC)*.PDB del $(OPDCC)*.PDB - if exist $(OPDCG)*.PDB del $(OPDCG)*.PDB + if exist $(OPDCTXT)*.PDB del $(OPDCTXT)*.PDB + if exist $(OPDCGUI)*.PDB del $(OPDCGUI)*.PDB if exist $(OUTL)*.MAP del $(OUTL)*.MAP if exist $(OTTY)*.MAP del $(OTTY)*.MAP if exist $(OGUI)*.MAP del $(OGUI)*.MAP @@ -2946,16 +2949,16 @@ clean: if exist $(OGUI)*.LIB del $(OGUI)*.LIB if exist $(OLUA)*.LIB del $(OLUA)*.LIB if exist $(OPDC)*.LIB del $(OPDC)*.LIB - if exist $(OPDCC)*.LIB del $(OPDCC)*.LIB - if exist $(OPDCG)*.LIB del $(OPDCG)*.LIB + if exist $(OPDCTXT)*.LIB del $(OPDCTXT)*.LIB + if exist $(OPDCGUI)*.LIB del $(OPDCGUI)*.LIB if exist $(OUTL)*.EXP del $(OUTL)*.EXP if exist $(OTTY)*.EXP del $(OTTY)*.EXP if exist $(OGUI)*.EXP del $(OGUI)*.EXP if exist $(OLUA)*.EXP del $(OLUA)*.EXP if exist $(OPDC)*.EXP del $(OPDC)*.EXP - if exist $(OPDCC)*.EXP del $(OPDCC)*.EXP - if exist $(OPDCG)*.EXP del $(OPDCG)*.EXP - if exist $(SRC)tiles.bmp del $(SRC)tiles.bmp + if exist $(OPDCTXT)*.EXP del $(OPDCTXT)*.EXP + if exist $(OPDCGUI)*.EXP del $(OPDCGUI)*.EXP + if exist $(SRC)tiles.bmp del $(SRC)tiles.bmp if exist $(OTTY)console.res del $(OTTY)console.res if exist $(OTTY)NetHack.res del $(OTTY)NetHack.res if exist $(OGUI)NetHackW.res del $(OGUI)NetHackW.res From 190cbfa2b7315cb182013c22ae899c07548f2bd1 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Mon, 6 Apr 2026 11:50:18 +0300 Subject: [PATCH 400/442] Fix segfault when binding keys in-game --- src/cmd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cmd.c b/src/cmd.c index 8b34060b6..8457f8b14 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -2297,7 +2297,6 @@ handler_rebind_keys_add(boolean keyfirst) char buf2[QBUFSZ]; uchar key = '\0'; int clr = NO_COLOR; - struct Cmd_bind *bind; if (keyfirst) { pline("Bind which key? "); @@ -2312,8 +2311,9 @@ handler_rebind_keys_add(boolean keyfirst) any = cg.zeroany; if (key) { - bind = cmdbind_get(key); - if (bind) { + struct Cmd_bind *bind = cmdbind_get(key); + + if (bind && bind->cmd) { Sprintf(buf, "Key '%s' is currently bound to \"%s\".", key2txt(key, buf2), bind->cmd->ef_txt); } else { From b2b2c3f6f2886460e2a0db2799c9b7b66d328187 Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 6 Apr 2026 11:00:12 -0400 Subject: [PATCH 401/442] remove extraneous line --- src/options.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/options.c b/src/options.c index 9bf14028f..5f21a86c3 100644 --- a/src/options.c +++ b/src/options.c @@ -7277,7 +7277,6 @@ initoptions_init(void) */ #endif #endif /* SYSCF */ - initoptions_finish(); } /* From 8716d92280feaf020b96b63e7f05fb47809a6b6a Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 6 Apr 2026 12:38:25 -0400 Subject: [PATCH 402/442] objects.txt tile numbering Closes #1496 reported by @horlogeislux --- win/share/objects.txt | 660 +++++++++++++++++++++--------------------- 1 file changed, 330 insertions(+), 330 deletions(-) diff --git a/win/share/objects.txt b/win/share/objects.txt index 986d88e6f..53ebdcf13 100644 --- a/win/share/objects.txt +++ b/win/share/objects.txt @@ -2957,7 +2957,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 152 (wooden shield / shield of drain resistance) +# tile 153 (wooden shield / shield of drain resistance) { ................ ................ @@ -2976,7 +2976,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 152 (wooden shield / shield of shock resistance) +# tile 154 (wooden shield / shield of shock resistance) { ................ ................ @@ -2995,7 +2995,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 153 (blue and green shield / elven shield) +# tile 155 (blue and green shield / elven shield) { ................ ................ @@ -3014,7 +3014,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 154 (white-handed shield / Uruk-hai shield) +# tile 156 (white-handed shield / Uruk-hai shield) { ................ ...K.KKKJJJ.J... @@ -3033,7 +3033,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 155 (red-eyed shield / orcish shield) +# tile 157 (red-eyed shield / orcish shield) { ................ ................ @@ -3052,7 +3052,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 156 (large shield) +# tile 158 (large shield) { ................ ...N.NNNOOO.O... @@ -3071,7 +3071,7 @@ Z = (195, 195, 195) ........AA...... ................ } -# tile 157 (large round shield / dwarvish roundshield) +# tile 159 (large round shield / dwarvish roundshield) { ................ ................ @@ -3090,7 +3090,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 158 (polished silver shield / shield of reflection) +# tile 160 (polished silver shield / shield of reflection) { ................ ................ @@ -3109,7 +3109,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 159 (old gloves / leather gloves) +# tile 161 (old gloves / leather gloves) { ................ ................ @@ -3128,7 +3128,7 @@ Z = (195, 195, 195) .........AAA.... ................ } -# tile 160 (padded gloves / gauntlets of fumbling) +# tile 162 (padded gloves / gauntlets of fumbling) { ................ ................ @@ -3147,7 +3147,7 @@ Z = (195, 195, 195) .........AAA.... ................ } -# tile 161 (riding gloves / gauntlets of power) +# tile 163 (riding gloves / gauntlets of power) { ................ ................ @@ -3166,7 +3166,7 @@ Z = (195, 195, 195) .........AAA.... ................ } -# tile 162 (fencing gloves / gauntlets of dexterity) +# tile 164 (fencing gloves / gauntlets of dexterity) { ................ ................ @@ -3185,7 +3185,7 @@ Z = (195, 195, 195) .........AAA.... ................ } -# tile 163 (walking shoes / low boots) +# tile 165 (walking shoes / low boots) { ................ ................ @@ -3204,7 +3204,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 164 (hard shoes / iron shoes) +# tile 166 (hard shoes / iron shoes) { ................ ................ @@ -3223,7 +3223,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 165 (jackboots / high boots) +# tile 167 (jackboots / high boots) { .......CCKKKK... ......CKAAAAJJ.. @@ -3242,7 +3242,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 166 (combat boots / speed boots) +# tile 168 (combat boots / speed boots) { ................ ................ @@ -3261,7 +3261,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 167 (jungle boots / water walking boots) +# tile 169 (jungle boots / water walking boots) { ................ ................ @@ -3280,7 +3280,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 168 (hiking boots / jumping boots) +# tile 170 (hiking boots / jumping boots) { ................ ................ @@ -3299,7 +3299,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 169 (mud boots / elven boots) +# tile 171 (mud boots / elven boots) { ................ ................ @@ -3318,7 +3318,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 170 (buckled boots / kicking boots) +# tile 172 (buckled boots / kicking boots) { ................ ................ @@ -3337,7 +3337,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 171 (riding boots / fumble boots) +# tile 173 (riding boots / fumble boots) { ................ ................ @@ -3356,7 +3356,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 172 (snow boots / levitation boots) +# tile 174 (snow boots / levitation boots) { ................ ................ @@ -3375,7 +3375,7 @@ Z = (195, 195, 195) ...A.A.A.A.A.A.. ................ } -# tile 173 (wooden / adornment) +# tile 175 (wooden / adornment) { ................ ................ @@ -3394,7 +3394,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 174 (granite / gain strength) +# tile 176 (granite / gain strength) { ................ ................ @@ -3413,7 +3413,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 175 (opal / gain constitution) +# tile 177 (opal / gain constitution) { ................ ................ @@ -3432,7 +3432,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 176 (clay / increase accuracy) +# tile 178 (clay / increase accuracy) { ................ ................ @@ -3451,7 +3451,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 177 (coral / increase damage) +# tile 179 (coral / increase damage) { ................ ................ @@ -3470,7 +3470,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 178 (black onyx / protection) +# tile 180 (black onyx / protection) { ................ ................ @@ -3489,7 +3489,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 179 (moonstone / regeneration) +# tile 181 (moonstone / regeneration) { ................ ................ @@ -3508,7 +3508,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 180 (tiger eye / searching) +# tile 182 (tiger eye / searching) { ................ ................ @@ -3527,7 +3527,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 181 (jade / stealth) +# tile 183 (jade / stealth) { ................ ................ @@ -3546,7 +3546,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 182 (bronze / sustain ability) +# tile 184 (bronze / sustain ability) { ................ ................ @@ -3565,7 +3565,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 183 (agate / levitation) +# tile 185 (agate / levitation) { ................ ................ @@ -3584,7 +3584,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 184 (topaz / hunger) +# tile 186 (topaz / hunger) { ................ ................ @@ -3603,7 +3603,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 185 (sapphire / aggravate monster) +# tile 187 (sapphire / aggravate monster) { ................ ................ @@ -3622,7 +3622,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 186 (ruby / conflict) +# tile 188 (ruby / conflict) { ................ ................ @@ -3641,7 +3641,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 187 (diamond / warning) +# tile 189 (diamond / warning) { ................ ................ @@ -3660,7 +3660,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 188 (pearl / poison resistance) +# tile 190 (pearl / poison resistance) { ................ ................ @@ -3679,7 +3679,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 189 (iron / fire resistance) +# tile 191 (iron / fire resistance) { ................ ................ @@ -3698,7 +3698,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 190 (brass / cold resistance) +# tile 192 (brass / cold resistance) { ................ ................ @@ -3717,7 +3717,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 191 (copper / shock resistance) +# tile 193 (copper / shock resistance) { ................ ................ @@ -3736,7 +3736,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 192 (twisted / free action) +# tile 194 (twisted / free action) { ................ ................ @@ -3755,7 +3755,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 193 (steel / slow digestion) +# tile 195 (steel / slow digestion) { ................ ................ @@ -3774,7 +3774,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 194 (silver / teleportation) +# tile 196 (silver / teleportation) { ................ ................ @@ -3793,7 +3793,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 195 (gold / teleport control) +# tile 197 (gold / teleport control) { ................ ................ @@ -3812,7 +3812,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 196 (ivory / polymorph) +# tile 198 (ivory / polymorph) { ................ ................ @@ -3831,7 +3831,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 197 (emerald / polymorph control) +# tile 199 (emerald / polymorph control) { ................ ................ @@ -3850,7 +3850,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 198 (wire / invisibility) +# tile 200 (wire / invisibility) { ................ ................ @@ -3869,7 +3869,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 199 (engagement / see invisible) +# tile 201 (engagement / see invisible) { ................ ................ @@ -3888,7 +3888,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 200 (shiny / protection from shape changers) +# tile 202 (shiny / protection from shape changers) { ................ ................ @@ -3907,7 +3907,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 201 (circular / amulet of ESP) +# tile 203 (circular / amulet of ESP) { ................ ......LLLLLAA... @@ -3926,7 +3926,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 202 (spherical / amulet of life saving) +# tile 204 (spherical / amulet of life saving) { ................ ......LLLLLAA... @@ -3945,7 +3945,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 203 (oval / amulet of strangulation) +# tile 205 (oval / amulet of strangulation) { ................ ......LLLLLLAA.. @@ -3964,7 +3964,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 204 (triangular / amulet of restful sleep) +# tile 206 (triangular / amulet of restful sleep) { ................ ......LLLLLAA... @@ -3983,7 +3983,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 205 (pyramidal / amulet versus poison) +# tile 207 (pyramidal / amulet versus poison) { ................ ......LLLLLAA... @@ -4002,7 +4002,7 @@ Z = (195, 195, 195) ....KJJJJJJJJA.. .......AAAAAAA.. } -# tile 206 (square / amulet of change) +# tile 208 (square / amulet of change) { ................ ......LLLLLAA... @@ -4021,7 +4021,7 @@ Z = (195, 195, 195) .......AAAAA.... ................ } -# tile 207 (concave / amulet of unchanging) +# tile 209 (concave / amulet of unchanging) { ................ ......LLLLLAA... @@ -4040,7 +4040,7 @@ Z = (195, 195, 195) .......KCCAA.... ........AAA..... } -# tile 208 (hexagonal / amulet of reflection) +# tile 210 (hexagonal / amulet of reflection) { ................ ......LLLLLAA... @@ -4059,7 +4059,7 @@ Z = (195, 195, 195) ........AAA..... ................ } -# tile 209 (octagonal / amulet of magical breathing) +# tile 211 (octagonal / amulet of magical breathing) { ................ ......LLLLLAA... @@ -4078,7 +4078,7 @@ Z = (195, 195, 195) .......KKKAA.... ........AAA..... } -# tile 210 (perforated / amulet of guarding) +# tile 212 (perforated / amulet of guarding) { ................ ......LLLLLAA... @@ -4097,7 +4097,7 @@ Z = (195, 195, 195) .......KKKAA.... ........AAA..... } -# tile 211 (cubical / amulet of flying) +# tile 213 (cubical / amulet of flying) { ................ ......LLLLLAA... @@ -4116,7 +4116,7 @@ Z = (195, 195, 195) ......CKKKKA.... .......AAAAA.... } -# tile 212 (Amulet of Yendor / cheap plastic imitation of the Amulet of Yendor) +# tile 214 (Amulet of Yendor / cheap plastic imitation of the Amulet of Yendor) { ................ ......HHHHHAA... @@ -4135,7 +4135,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 213 (Amulet of Yendor / Amulet of Yendor) +# tile 215 (Amulet of Yendor / Amulet of Yendor) { ................ ......HHHHHAA... @@ -4154,7 +4154,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 214 (large box) +# tile 216 (large box) { ................ ................ @@ -4173,7 +4173,7 @@ Z = (195, 195, 195) CKKKKKKKKKKJAA.. .AAAAAAAAAAAA... } -# tile 215 (chest) +# tile 217 (chest) { ................ ................ @@ -4192,7 +4192,7 @@ Z = (195, 195, 195) CKKKKKKKKKKJAA.. .AAAAAAAAAAAA... } -# tile 216 (ice box) +# tile 218 (ice box) { ................ ................ @@ -4211,7 +4211,7 @@ Z = (195, 195, 195) NBBBBBBBBBBPAA.. .AAAAAAAAAAAA... } -# tile 217 (bag / sack) +# tile 219 (bag / sack) { ................ ................ @@ -4230,7 +4230,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 218 (bag / oilskin sack) +# tile 220 (bag / oilskin sack) { ................ ................ @@ -4249,7 +4249,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 219 (bag / bag of holding) +# tile 221 (bag / bag of holding) { ................ ................ @@ -4268,7 +4268,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 220 (bag / bag of tricks) +# tile 222 (bag / bag of tricks) { ................ ................ @@ -4287,7 +4287,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 221 (key / skeleton key) +# tile 223 (key / skeleton key) { ................ ................ @@ -4306,7 +4306,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 222 (lock pick) +# tile 224 (lock pick) { ................ ................ @@ -4325,7 +4325,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 223 (credit card) +# tile 225 (credit card) { ................ ................ @@ -4344,7 +4344,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 224 (candle / tallow candle) +# tile 226 (candle / tallow candle) { ................ ................ @@ -4363,7 +4363,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 225 (candle / wax candle) +# tile 227 (candle / wax candle) { ................ ................ @@ -4382,7 +4382,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 226 (brass lantern) +# tile 228 (brass lantern) { ................ ................ @@ -4401,7 +4401,7 @@ Z = (195, 195, 195) .....AAAAAAA.... ................ } -# tile 227 (lamp / oil lamp) +# tile 229 (lamp / oil lamp) { ................ ................ @@ -4420,7 +4420,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 228 (lamp / magic lamp) +# tile 230 (lamp / magic lamp) { ................ ................ @@ -4439,7 +4439,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 229 (expensive camera) +# tile 231 (expensive camera) { ................ ................ @@ -4458,7 +4458,7 @@ Z = (195, 195, 195) ...PPPPPPPPPPPP. ................ } -# tile 230 (looking glass / mirror) +# tile 232 (looking glass / mirror) { ................ ................ @@ -4477,7 +4477,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 231 (glass orb / crystal ball) +# tile 233 (glass orb / crystal ball) { ................ ................ @@ -4496,7 +4496,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 232 (lenses) +# tile 234 (lenses) { ................ ................ @@ -4515,7 +4515,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 233 (blindfold) +# tile 235 (blindfold) { ................ ................ @@ -4534,7 +4534,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 234 (towel) +# tile 236 (towel) { ................ ................ @@ -4553,7 +4553,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 235 (saddle) +# tile 237 (saddle) { ................ ................ @@ -4572,7 +4572,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 236 (leash) +# tile 238 (leash) { ................ ................ @@ -4591,7 +4591,7 @@ Z = (195, 195, 195) .....AAAA....... ................ } -# tile 237 (stethoscope) +# tile 239 (stethoscope) { ................ ................ @@ -4610,7 +4610,7 @@ Z = (195, 195, 195) ........AAA..... ................ } -# tile 238 (tinning kit) +# tile 240 (tinning kit) { ................ ................ @@ -4629,7 +4629,7 @@ Z = (195, 195, 195) ......AAA....... ................ } -# tile 239 (tin opener) +# tile 241 (tin opener) { ................ ................ @@ -4648,7 +4648,7 @@ Z = (195, 195, 195) ........AA...... ................ } -# tile 240 (can of grease) +# tile 242 (can of grease) { ................ ................ @@ -4667,7 +4667,7 @@ Z = (195, 195, 195) .......AAAA..... ................ } -# tile 241 (figurine) +# tile 243 (figurine) { ................ ................ @@ -4686,7 +4686,7 @@ Z = (195, 195, 195) .....JJJJJAA.... ................ } -# tile 242 (magic marker) +# tile 244 (magic marker) { ................ ................ @@ -4705,7 +4705,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 243 (land mine) +# tile 245 (land mine) { ................ ................ @@ -4724,7 +4724,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 244 (beartrap) +# tile 246 (beartrap) { ................ ................ @@ -4743,7 +4743,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 245 (whistle / tin whistle) +# tile 247 (whistle / tin whistle) { ................ ................ @@ -4762,7 +4762,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 246 (whistle / magic whistle) +# tile 248 (whistle / magic whistle) { ................ ................ @@ -4781,7 +4781,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 247 (flute / wooden flute) +# tile 249 (flute / wooden flute) { ................ ................ @@ -4800,7 +4800,7 @@ Z = (195, 195, 195) ..A............. ................ } -# tile 248 (flute / magic flute) +# tile 250 (flute / magic flute) { ................ ................ @@ -4819,7 +4819,7 @@ Z = (195, 195, 195) ..A............. ................ } -# tile 249 (horn / tooled horn) +# tile 251 (horn / tooled horn) { ................ ................ @@ -4838,7 +4838,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 250 (horn / frost horn) +# tile 252 (horn / frost horn) { ................ ................ @@ -4857,7 +4857,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 251 (horn / fire horn) +# tile 253 (horn / fire horn) { ................ ................ @@ -4876,7 +4876,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 252 (horn / horn of plenty) +# tile 254 (horn / horn of plenty) { ................ ................ @@ -4895,7 +4895,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 253 (harp / wooden harp) +# tile 255 (harp / wooden harp) { ................ ................ @@ -4914,7 +4914,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 254 (harp / magic harp) +# tile 256 (harp / magic harp) { ................ ................ @@ -4933,7 +4933,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 255 (bell) +# tile 257 (bell) { ................ .......KA....... @@ -4952,7 +4952,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 256 (bugle) +# tile 258 (bugle) { ................ ................ @@ -4971,7 +4971,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 257 (drum / leather drum) +# tile 259 (drum / leather drum) { ................ ................ @@ -4990,7 +4990,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 258 (drum / drum of earthquake) +# tile 260 (drum / drum of earthquake) { ................ ................ @@ -5009,7 +5009,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 259 (pick-axe) +# tile 261 (pick-axe) { ................ ................ @@ -5028,7 +5028,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 260 (grappling hook) +# tile 262 (grappling hook) { .............N.. ..............P. @@ -5047,7 +5047,7 @@ Z = (195, 195, 195) ..OOA..OOOA..... ....OOOAA....... } -# tile 261 (unicorn horn) +# tile 263 (unicorn horn) { ................ ................ @@ -5066,7 +5066,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 262 (candelabrum / Candelabrum of Invocation) +# tile 264 (candelabrum / Candelabrum of Invocation) { .......N........ .......D........ @@ -5085,7 +5085,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 263 (silver bell / Bell of Opening) +# tile 265 (silver bell / Bell of Opening) { ................ .......OA....... @@ -5104,7 +5104,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 264 (tripe ration) +# tile 266 (tripe ration) { ................ ................ @@ -5123,7 +5123,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 265 (corpse) +# tile 267 (corpse) { ................ .....D.DPLN..... @@ -5142,7 +5142,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 266 (egg) +# tile 268 (egg) { ................ ................ @@ -5161,7 +5161,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 267 (meatball) +# tile 269 (meatball) { ................ ................ @@ -5180,7 +5180,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 268 (meat stick) +# tile 270 (meat stick) { ................ ................ @@ -5199,7 +5199,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 269 (enormous meatball) +# tile 271 (enormous meatball) { ................ ................ @@ -5218,7 +5218,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 270 (meat ring) +# tile 272 (meat ring) { ................ ................ @@ -5237,7 +5237,7 @@ Z = (195, 195, 195) ......AAAAA..... ................ } -# tile 271 (glob of gray ooze) +# tile 273 (glob of gray ooze) { ................ ................ @@ -5256,7 +5256,7 @@ Z = (195, 195, 195) ...AAA.AAAAA.... ................ } -# tile 272 (glob of brown pudding) +# tile 274 (glob of brown pudding) { ................ ................ @@ -5275,7 +5275,7 @@ Z = (195, 195, 195) ...AAA.AAAAA.... ................ } -# tile 273 (glob of green slime) +# tile 275 (glob of green slime) { ................ ................ @@ -5294,7 +5294,7 @@ Z = (195, 195, 195) ...AAA.AAAAA.... ................ } -# tile 274 (glob of black pudding) +# tile 276 (glob of black pudding) { ................ ................ @@ -5313,7 +5313,7 @@ Z = (195, 195, 195) ...AAA.AAAAA.... ................ } -# tile 275 (kelp frond) +# tile 277 (kelp frond) { ....FA.......... ....FFA......... @@ -5332,7 +5332,7 @@ Z = (195, 195, 195) .....FFFFA...... ......FFFFA..... } -# tile 276 (eucalyptus leaf) +# tile 278 (eucalyptus leaf) { ................ ................ @@ -5351,7 +5351,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 277 (apple) +# tile 279 (apple) { ................ ................ @@ -5370,7 +5370,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 278 (orange) +# tile 280 (orange) { ................ ................ @@ -5389,7 +5389,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 279 (pear) +# tile 281 (pear) { ................ ................ @@ -5408,7 +5408,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 280 (melon) +# tile 282 (melon) { ................ ................ @@ -5427,7 +5427,7 @@ Z = (195, 195, 195) ......AAA....... ................ } -# tile 281 (banana) +# tile 283 (banana) { ................ ................ @@ -5446,7 +5446,7 @@ Z = (195, 195, 195) .....AAAAA...... ................ } -# tile 282 (carrot) +# tile 284 (carrot) { ................ ..........F..F.. @@ -5465,7 +5465,7 @@ Z = (195, 195, 195) ...A............ ................ } -# tile 283 (sprig of wolfsbane) +# tile 285 (sprig of wolfsbane) { ................ ................ @@ -5484,7 +5484,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 284 (clove of garlic) +# tile 286 (clove of garlic) { ................ ................ @@ -5503,7 +5503,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 285 (slime mold) +# tile 287 (slime mold) { ................ ................ @@ -5522,7 +5522,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 286 (lump of royal jelly) +# tile 288 (lump of royal jelly) { ................ ................ @@ -5541,7 +5541,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 287 (cream pie) +# tile 289 (cream pie) { ................ ................ @@ -5560,7 +5560,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 288 (candy bar) +# tile 290 (candy bar) { ................ ................ @@ -5579,7 +5579,7 @@ Z = (195, 195, 195) ....A........... ................ } -# tile 289 (fortune cookie) +# tile 291 (fortune cookie) { ................ ................ @@ -5598,7 +5598,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 290 (pancake) +# tile 292 (pancake) { ................ ................ @@ -5617,7 +5617,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 291 (lembas wafer) +# tile 293 (lembas wafer) { ................ ................ @@ -5636,7 +5636,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 292 (cram ration) +# tile 294 (cram ration) { ................ ...JKA.......... @@ -5655,7 +5655,7 @@ Z = (195, 195, 195) .....AAAAAA..... ................ } -# tile 293 (food ration) +# tile 295 (food ration) { ...JJA.......... ...BPA.......... @@ -5674,7 +5674,7 @@ Z = (195, 195, 195) ....KKKKKKKKKA.. .....AAAAAAAA... } -# tile 294 (K-ration) +# tile 296 (K-ration) { ................ ................ @@ -5693,7 +5693,7 @@ Z = (195, 195, 195) ....KKKKKKKKKA.. .....AAAAAAAA... } -# tile 295 (C-ration) +# tile 297 (C-ration) { ................ ................ @@ -5712,7 +5712,7 @@ Z = (195, 195, 195) ....KKKKKKKKKA.. .....AAAAAAAA... } -# tile 296 (tin) +# tile 298 (tin) { ................ ................ @@ -5731,7 +5731,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 297 (ruby / gain ability) +# tile 299 (ruby / gain ability) { ................ ................ @@ -5750,7 +5750,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 298 (pink / restore ability) +# tile 300 (pink / restore ability) { ................ ................ @@ -5769,7 +5769,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 299 (orange / confusion) +# tile 301 (orange / confusion) { ................ ................ @@ -5788,7 +5788,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 300 (yellow / blindness) +# tile 302 (yellow / blindness) { ................ ................ @@ -5807,7 +5807,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 301 (emerald / paralysis) +# tile 303 (emerald / paralysis) { ................ ................ @@ -5826,7 +5826,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 302 (dark green / speed) +# tile 304 (dark green / speed) { ................ ................ @@ -5845,7 +5845,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 303 (cyan / levitation) +# tile 305 (cyan / levitation) { ................ ................ @@ -5864,7 +5864,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 304 (sky blue / hallucination) +# tile 306 (sky blue / hallucination) { ................ ................ @@ -5883,7 +5883,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 305 (brilliant blue / invisibility) +# tile 307 (brilliant blue / invisibility) { ................ ................ @@ -5902,7 +5902,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 306 (magenta / see invisible) +# tile 308 (magenta / see invisible) { ................ ................ @@ -5921,7 +5921,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 307 (purple-red / healing) +# tile 309 (purple-red / healing) { ................ ................ @@ -5940,7 +5940,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 308 (puce / extra healing) +# tile 310 (puce / extra healing) { ................ ................ @@ -5959,7 +5959,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 309 (milky / gain level) +# tile 311 (milky / gain level) { ................ ................ @@ -5978,7 +5978,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 310 (swirly / enlightenment) +# tile 312 (swirly / enlightenment) { ................ ................ @@ -5997,7 +5997,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 311 (bubbly / monster detection) +# tile 313 (bubbly / monster detection) { ................ ................ @@ -6016,7 +6016,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 312 (smoky / object detection) +# tile 314 (smoky / object detection) { ................ ................ @@ -6035,7 +6035,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 313 (cloudy / gain energy) +# tile 315 (cloudy / gain energy) { ................ ................ @@ -6054,7 +6054,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 314 (effervescent / sleeping) +# tile 316 (effervescent / sleeping) { ................ ................ @@ -6073,7 +6073,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 315 (black / full healing) +# tile 317 (black / full healing) { ................ ................ @@ -6092,7 +6092,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 316 (golden / polymorph) +# tile 318 (golden / polymorph) { ................ ................ @@ -6111,7 +6111,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 317 (brown / booze) +# tile 319 (brown / booze) { ................ ................ @@ -6130,7 +6130,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 318 (fizzy / sickness) +# tile 320 (fizzy / sickness) { ................ ................ @@ -6149,7 +6149,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 319 (dark / fruit juice) +# tile 321 (dark / fruit juice) { ................ ................ @@ -6168,7 +6168,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 320 (white / acid) +# tile 322 (white / acid) { ................ ................ @@ -6187,7 +6187,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 321 (murky / oil) +# tile 323 (murky / oil) { ................ ................ @@ -6206,7 +6206,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 322 (clear / water) +# tile 324 (clear / water) { ................ ................ @@ -6225,7 +6225,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 323 (ZELGO MER / enchant armor) +# tile 325 (ZELGO MER / enchant armor) { ................ ................ @@ -6244,7 +6244,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 324 (JUYED AWK YACC / destroy armor) +# tile 326 (JUYED AWK YACC / destroy armor) { ................ ................ @@ -6263,7 +6263,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 325 (NR 9 / confuse monster) +# tile 327 (NR 9 / confuse monster) { ................ ................ @@ -6282,7 +6282,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 326 (XIXAXA XOXAXA XUXAXA / scare monster) +# tile 328 (XIXAXA XOXAXA XUXAXA / scare monster) { ................ ................ @@ -6301,7 +6301,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 327 (PRATYAVAYAH / remove curse) +# tile 329 (PRATYAVAYAH / remove curse) { ................ ................ @@ -6320,7 +6320,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 328 (DAIYEN FOOELS / enchant weapon) +# tile 330 (DAIYEN FOOELS / enchant weapon) { ................ ................ @@ -6339,7 +6339,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 329 (LEP GEX VEN ZEA / create monster) +# tile 331 (LEP GEX VEN ZEA / create monster) { ................ ................ @@ -6358,7 +6358,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 330 (PRIRUTSENIE / taming) +# tile 332 (PRIRUTSENIE / taming) { ................ ................ @@ -6377,7 +6377,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 331 (ELBIB YLOH / genocide) +# tile 333 (ELBIB YLOH / genocide) { ................ ................ @@ -6396,7 +6396,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 332 (VERR YED HORRE / light) +# tile 334 (VERR YED HORRE / light) { ................ ................ @@ -6415,7 +6415,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 333 (VENZAR BORGAVVE / teleportation) +# tile 335 (VENZAR BORGAVVE / teleportation) { ................ ................ @@ -6434,7 +6434,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 334 (THARR / gold detection) +# tile 336 (THARR / gold detection) { ................ ................ @@ -6453,7 +6453,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 335 (YUM YUM / food detection) +# tile 337 (YUM YUM / food detection) { ................ ................ @@ -6472,7 +6472,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 336 (KERNOD WEL / identify) +# tile 338 (KERNOD WEL / identify) { ................ ................ @@ -6491,7 +6491,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 337 (ELAM EBOW / magic mapping) +# tile 339 (ELAM EBOW / magic mapping) { ................ ................ @@ -6510,7 +6510,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 338 (DUAM XNAHT / amnesia) +# tile 340 (DUAM XNAHT / amnesia) { ................ ................ @@ -6529,7 +6529,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 339 (ANDOVA BEGARIN / fire) +# tile 341 (ANDOVA BEGARIN / fire) { ................ ................ @@ -6548,7 +6548,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 340 (KIRJE / earth) +# tile 342 (KIRJE / earth) { ................ ................ @@ -6567,7 +6567,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 341 (VE FORBRYDERNE / punishment) +# tile 343 (VE FORBRYDERNE / punishment) { ................ ................ @@ -6586,7 +6586,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 342 (HACKEM MUCHE / charging) +# tile 344 (HACKEM MUCHE / charging) { ................ ................ @@ -6605,7 +6605,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 343 (VELOX NEB / stinking cloud) +# tile 345 (VELOX NEB / stinking cloud) { ................ ................ @@ -6624,7 +6624,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 344 (FOOBIE BLETCH) +# tile 346 (FOOBIE BLETCH) { ................ ................ @@ -6643,7 +6643,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 345 (TEMOV) +# tile 347 (TEMOV) { ................ ................ @@ -6662,7 +6662,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 346 (GARVEN DEH) +# tile 348 (GARVEN DEH) { ................ ................ @@ -6681,7 +6681,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 347 (READ ME) +# tile 349 (READ ME) { ................ ................ @@ -6700,7 +6700,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 348 (ETAOIN SHRDLU) +# tile 350 (ETAOIN SHRDLU) { ................ ................ @@ -6719,7 +6719,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 349 (LOREM IPSUM) +# tile 351 (LOREM IPSUM) { ................ ................ @@ -6738,7 +6738,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 350 (FNORD) +# tile 352 (FNORD) { ................ ................ @@ -6757,7 +6757,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 351 (KO BATE) +# tile 353 (KO BATE) { ................ ................ @@ -6776,7 +6776,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 352 (ABRA KA DABRA) +# tile 354 (ABRA KA DABRA) { ................ ................ @@ -6795,7 +6795,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 353 (ASHPD SODALG) +# tile 355 (ASHPD SODALG) { ................ ................ @@ -6814,7 +6814,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 354 (ZLORFIK) +# tile 356 (ZLORFIK) { ................ ................ @@ -6833,7 +6833,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 355 (GNIK SISI VLE) +# tile 357 (GNIK SISI VLE) { ................ ................ @@ -6852,7 +6852,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 356 (HAPAX LEGOMENON) +# tile 358 (HAPAX LEGOMENON) { ................ ................ @@ -6871,7 +6871,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 357 (EIRIS SAZUN IDISI) +# tile 359 (EIRIS SAZUN IDISI) { ................ ................ @@ -6890,7 +6890,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 358 (PHOL ENDE WODAN) +# tile 360 (PHOL ENDE WODAN) { ................ ................ @@ -6909,7 +6909,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 359 (GHOTI) +# tile 361 (GHOTI) { ................ ................ @@ -6928,7 +6928,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 360 (MAPIRO MAHAMA DIROMAT) +# tile 362 (MAPIRO MAHAMA DIROMAT) { ................ ................ @@ -6947,7 +6947,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 361 (VAS CORP BET MANI) +# tile 363 (VAS CORP BET MANI) { ................ ................ @@ -6966,7 +6966,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 362 (XOR OTA) +# tile 364 (XOR OTA) { ................ ................ @@ -6985,7 +6985,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 363 (STRC PRST SKRZ KRK) +# tile 365 (STRC PRST SKRZ KRK) { ................ ................ @@ -7004,7 +7004,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 364 (stamped / mail) +# tile 366 (stamped / mail) { ................ ................ @@ -7023,7 +7023,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 365 (unlabeled / blank paper) +# tile 367 (unlabeled / blank paper) { ................ ................ @@ -7042,7 +7042,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 366 (parchment / dig) +# tile 368 (parchment / dig) { ................ ................ @@ -7061,7 +7061,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 367 (vellum / magic missile) +# tile 369 (vellum / magic missile) { ................ ................ @@ -7080,7 +7080,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 368 (ragged / fireball) +# tile 370 (ragged / fireball) { ................ ................ @@ -7099,7 +7099,7 @@ Z = (195, 195, 195) ......OOJJAA.... ................ } -# tile 369 (dog eared / cone of cold) +# tile 371 (dog eared / cone of cold) { ................ ................ @@ -7118,7 +7118,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 370 (mottled / sleep) +# tile 372 (mottled / sleep) { ................ ................ @@ -7137,7 +7137,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 371 (stained / finger of death) +# tile 373 (stained / finger of death) { ................ ................ @@ -7156,7 +7156,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 372 (cloth / light) +# tile 374 (cloth / light) { ................ ................ @@ -7175,7 +7175,7 @@ Z = (195, 195, 195) .......PPPAA.... ................ } -# tile 373 (leathery / detect monsters) +# tile 375 (leathery / detect monsters) { ................ ................ @@ -7194,7 +7194,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 374 (white / healing) +# tile 376 (white / healing) { ................ ................ @@ -7213,7 +7213,7 @@ Z = (195, 195, 195) .......PNNAA.... ................ } -# tile 375 (pink / knock) +# tile 377 (pink / knock) { ................ ................ @@ -7232,7 +7232,7 @@ Z = (195, 195, 195) .......IIIAA.... ................ } -# tile 376 (red / force bolt) +# tile 378 (red / force bolt) { ................ ................ @@ -7251,7 +7251,7 @@ Z = (195, 195, 195) .......DDDAA.... ................ } -# tile 377 (orange / confuse monster) +# tile 379 (orange / confuse monster) { ................ ................ @@ -7270,7 +7270,7 @@ Z = (195, 195, 195) .......CCCAA.... ................ } -# tile 378 (yellow / cure blindness) +# tile 380 (yellow / cure blindness) { ................ ................ @@ -7289,7 +7289,7 @@ Z = (195, 195, 195) .......HHHAA.... ................ } -# tile 379 (velvet / drain life) +# tile 381 (velvet / drain life) { ................ ................ @@ -7308,7 +7308,7 @@ Z = (195, 195, 195) .......EEEAA.... ................ } -# tile 380 (light green / slow monster) +# tile 382 (light green / slow monster) { ................ ................ @@ -7327,7 +7327,7 @@ Z = (195, 195, 195) .......GGGAA.... ................ } -# tile 381 (dark green / wizard lock) +# tile 383 (dark green / wizard lock) { ................ ................ @@ -7346,7 +7346,7 @@ Z = (195, 195, 195) .......FFFAA.... ................ } -# tile 382 (turquoise / create monster) +# tile 384 (turquoise / create monster) { ................ ................ @@ -7365,7 +7365,7 @@ Z = (195, 195, 195) .......FBBAA.... ................ } -# tile 383 (cyan / detect food) +# tile 385 (cyan / detect food) { ................ ................ @@ -7384,7 +7384,7 @@ Z = (195, 195, 195) .......BBBAA.... ................ } -# tile 384 (light blue / cause fear) +# tile 386 (light blue / cause fear) { ................ ................ @@ -7403,7 +7403,7 @@ Z = (195, 195, 195) .......BBBAA.... ................ } -# tile 385 (dark blue / clairvoyance) +# tile 387 (dark blue / clairvoyance) { ................ ................ @@ -7422,7 +7422,7 @@ Z = (195, 195, 195) .......EEEAA.... ................ } -# tile 386 (indigo / cure sickness) +# tile 388 (indigo / cure sickness) { ................ ................ @@ -7441,7 +7441,7 @@ Z = (195, 195, 195) .......EEEAA.... ................ } -# tile 387 (magenta / charm monster) +# tile 389 (magenta / charm monster) { ................ ................ @@ -7460,7 +7460,7 @@ Z = (195, 195, 195) .......IIIAA.... ................ } -# tile 388 (purple / haste self) +# tile 390 (purple / haste self) { ................ ................ @@ -7479,7 +7479,7 @@ Z = (195, 195, 195) .......IIIAA.... ................ } -# tile 389 (violet / detect unseen) +# tile 391 (violet / detect unseen) { ................ ................ @@ -7498,7 +7498,7 @@ Z = (195, 195, 195) .......IIIAA.... ................ } -# tile 390 (tan / levitation) +# tile 392 (tan / levitation) { ................ ................ @@ -7517,7 +7517,7 @@ Z = (195, 195, 195) .......KKKAA.... ................ } -# tile 391 (plaid / extra healing) +# tile 393 (plaid / extra healing) { ................ ................ @@ -7536,7 +7536,7 @@ Z = (195, 195, 195) .......EFDAA.... ................ } -# tile 392 (light brown / restore ability) +# tile 394 (light brown / restore ability) { ................ ................ @@ -7555,7 +7555,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 393 (dark brown / invisibility) +# tile 395 (dark brown / invisibility) { ................ ................ @@ -7574,7 +7574,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 394 (gray / detect treasure) +# tile 396 (gray / detect treasure) { ................ ................ @@ -7593,7 +7593,7 @@ Z = (195, 195, 195) .......PPPAA.... ................ } -# tile 395 (wrinkled / remove curse) +# tile 397 (wrinkled / remove curse) { ................ ................ @@ -7612,7 +7612,7 @@ Z = (195, 195, 195) ......JJKKAA.... ................ } -# tile 396 (dusty / magic mapping) +# tile 398 (dusty / magic mapping) { ................ ................ @@ -7631,7 +7631,7 @@ Z = (195, 195, 195) .KAKA..JJJAA.... ................ } -# tile 397 (bronze / identify) +# tile 399 (bronze / identify) { ................ ................ @@ -7650,7 +7650,7 @@ Z = (195, 195, 195) .......CCCAA.... ................ } -# tile 398 (copper / turn undead) +# tile 400 (copper / turn undead) { ................ ................ @@ -7669,7 +7669,7 @@ Z = (195, 195, 195) .......JCJAA.... ................ } -# tile 399 (silver / polymorph) +# tile 401 (silver / polymorph) { ................ ................ @@ -7688,7 +7688,7 @@ Z = (195, 195, 195) .......PPPAA.... ................ } -# tile 400 (gold / teleport away) +# tile 402 (gold / teleport away) { ................ ................ @@ -7707,7 +7707,7 @@ Z = (195, 195, 195) .......HHHAA.... ................ } -# tile 401 (glittering / create familiar) +# tile 403 (glittering / create familiar) { ................ ................ @@ -7726,7 +7726,7 @@ Z = (195, 195, 195) .......PPPAN.... .......N........ } -# tile 402 (shining / cancellation) +# tile 404 (shining / cancellation) { ....N........... .......N........ @@ -7745,7 +7745,7 @@ Z = (195, 195, 195) .......PPPAA.... ................ } -# tile 403 (dull / protection) +# tile 405 (dull / protection) { ................ ................ @@ -7764,7 +7764,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 404 (thin / jumping) +# tile 406 (thin / jumping) { ................ ................ @@ -7783,7 +7783,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 405 (thick / stone to flesh) +# tile 407 (thick / stone to flesh) { ................ ................ @@ -7802,7 +7802,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 406 (checkered / chain lightning) +# tile 408 (checkered / chain lightning) { ................ ................ @@ -7821,7 +7821,7 @@ Z = (195, 195, 195) .......AAA...... ................ } -# tile 406 (plain / blank paper) +# tile 409 (plain / blank paper) { ................ ................ @@ -7840,7 +7840,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 407 (paperback / novel) +# tile 410 (paperback / novel) { ................ ................ @@ -7859,7 +7859,7 @@ Z = (195, 195, 195) .......EEEAA.... ................ } -# tile 408 (papyrus / Book of the Dead) +# tile 411 (papyrus / Book of the Dead) { ................ ................ @@ -7878,7 +7878,7 @@ Z = (195, 195, 195) .......AAA...... ................ } -# tile 409 (glass / light) +# tile 412 (glass / light) { ................ ................ @@ -7897,7 +7897,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 410 (balsa / secret door detection) +# tile 413 (balsa / secret door detection) { ................ ................ @@ -7916,7 +7916,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 411 (crystal / enlightenment) +# tile 414 (crystal / enlightenment) { ................ ................ @@ -7935,7 +7935,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 412 (maple / create monster) +# tile 415 (maple / create monster) { ................ ................ @@ -7954,7 +7954,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 413 (pine / wishing) +# tile 416 (pine / wishing) { ................ ................ @@ -7973,7 +7973,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 413 (redwood / stasis) +# tile 417 (redwood / stasis) { ................ ................ @@ -7992,7 +7992,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 414 (oak / nothing) +# tile 418 (oak / nothing) { ................ ................ @@ -8011,7 +8011,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 415 (ebony / striking) +# tile 419 (ebony / striking) { ................ ................ @@ -8030,7 +8030,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 416 (marble / make invisible) +# tile 420 (marble / make invisible) { ................ ................ @@ -8049,7 +8049,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 417 (tin / slow monster) +# tile 421 (tin / slow monster) { ................ ................ @@ -8068,7 +8068,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 418 (brass / speed monster) +# tile 422 (brass / speed monster) { ................ ................ @@ -8087,7 +8087,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 419 (copper / undead turning) +# tile 423 (copper / undead turning) { ................ ................ @@ -8106,7 +8106,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 420 (silver / polymorph) +# tile 424 (silver / polymorph) { ................ ................ @@ -8125,7 +8125,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 421 (platinum / cancellation) +# tile 425 (platinum / cancellation) { ................ ................ @@ -8144,7 +8144,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 422 (iridium / teleportation) +# tile 426 (iridium / teleportation) { ................ ................ @@ -8163,7 +8163,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 423 (zinc / opening) +# tile 427 (zinc / opening) { ................ ................ @@ -8182,7 +8182,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 424 (aluminum / locking) +# tile 428 (aluminum / locking) { ................ ................ @@ -8201,7 +8201,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 425 (uranium / probing) +# tile 429 (uranium / probing) { ................ ................ @@ -8220,7 +8220,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 426 (iron / digging) +# tile 430 (iron / digging) { ................ ................ @@ -8239,7 +8239,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 427 (steel / magic missile) +# tile 431 (steel / magic missile) { ................ ................ @@ -8258,7 +8258,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 428 (hexagonal / fire) +# tile 432 (hexagonal / fire) { ................ ................ @@ -8277,7 +8277,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 429 (short / cold) +# tile 433 (short / cold) { ................ ................ @@ -8296,7 +8296,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 430 (runed / sleep) +# tile 434 (runed / sleep) { ................ ................ @@ -8315,7 +8315,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 431 (long / death) +# tile 435 (long / death) { ................ .............NO. @@ -8334,7 +8334,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 432 (curved / lightning) +# tile 436 (curved / lightning) { ................ .......NO....... @@ -8353,7 +8353,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 433 (forked) +# tile 437 (forked) { ................ ................ @@ -8372,7 +8372,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 434 (spiked) +# tile 438 (spiked) { ................ ................ @@ -8391,7 +8391,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 435 (jeweled) +# tile 439 (jeweled) { ................ ................ @@ -8410,7 +8410,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 436 (gold piece) +# tile 440 (gold piece) { ................ ................ @@ -8429,7 +8429,7 @@ Z = (195, 195, 195) .........HA..... ...........HA... } -# tile 437 (white / dilithium crystal) +# tile 441 (white / dilithium crystal) { ................ ................ @@ -8448,7 +8448,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 438 (white / diamond) +# tile 442 (white / diamond) { ................ ................ @@ -8467,7 +8467,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 439 (red / ruby) +# tile 443 (red / ruby) { ................ ................ @@ -8486,7 +8486,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 440 (orange / jacinth) +# tile 444 (orange / jacinth) { ................ ................ @@ -8505,7 +8505,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 441 (blue / sapphire) +# tile 445 (blue / sapphire) { ................ ................ @@ -8524,7 +8524,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 442 (black / black opal) +# tile 446 (black / black opal) { ................ ................ @@ -8543,7 +8543,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 443 (green / emerald) +# tile 447 (green / emerald) { ................ ................ @@ -8562,7 +8562,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 444 (green / turquoise) +# tile 448 (green / turquoise) { ................ ................ @@ -8581,7 +8581,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 445 (yellow / citrine) +# tile 449 (yellow / citrine) { ................ ................ @@ -8600,7 +8600,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 446 (green / aquamarine) +# tile 450 (green / aquamarine) { ................ ................ @@ -8619,7 +8619,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 447 (yellowish brown / amber) +# tile 451 (yellowish brown / amber) { ................ ................ @@ -8638,7 +8638,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 448 (yellowish brown / topaz) +# tile 452 (yellowish brown / topaz) { ................ ................ @@ -8657,7 +8657,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 449 (black / jet) +# tile 453 (black / jet) { ................ ................ @@ -8676,7 +8676,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 450 (white / opal) +# tile 454 (white / opal) { ................ ................ @@ -8695,7 +8695,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 451 (yellow / chrysoberyl) +# tile 455 (yellow / chrysoberyl) { ................ ................ @@ -8714,7 +8714,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 452 (red / garnet) +# tile 456 (red / garnet) { ................ ................ @@ -8733,7 +8733,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 453 (violet / amethyst) +# tile 457 (violet / amethyst) { ................ ................ @@ -8752,7 +8752,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 454 (red / jasper) +# tile 458 (red / jasper) { ................ ................ @@ -8771,7 +8771,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 455 (violet / fluorite) +# tile 459 (violet / fluorite) { ................ ................ @@ -8790,7 +8790,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 456 (black / obsidian) +# tile 460 (black / obsidian) { ................ ................ @@ -8809,7 +8809,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 457 (orange / agate) +# tile 461 (orange / agate) { ................ ................ @@ -8828,7 +8828,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 458 (green / jade) +# tile 462 (green / jade) { ................ ................ @@ -8847,7 +8847,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 459 (white / worthless piece of white glass) +# tile 463 (white / worthless piece of white glass) { ................ ................ @@ -8866,7 +8866,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 460 (blue / worthless piece of blue glass) +# tile 464 (blue / worthless piece of blue glass) { ................ ................ @@ -8885,7 +8885,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 461 (red / worthless piece of red glass) +# tile 465 (red / worthless piece of red glass) { ................ ................ @@ -8904,7 +8904,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 462 (yellowish brown / worthless piece of yellowish brown glass) +# tile 466 (yellowish brown / worthless piece of yellowish brown glass) { ................ ................ @@ -8923,7 +8923,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 463 (orange / worthless piece of orange glass) +# tile 467 (orange / worthless piece of orange glass) { ................ ................ @@ -8942,7 +8942,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 464 (yellow / worthless piece of yellow glass) +# tile 468 (yellow / worthless piece of yellow glass) { ................ ................ @@ -8961,7 +8961,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 465 (black / worthless piece of black glass) +# tile 469 (black / worthless piece of black glass) { ................ ................ @@ -8980,7 +8980,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 466 (green / worthless piece of green glass) +# tile 470 (green / worthless piece of green glass) { ................ ................ @@ -8999,7 +8999,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 467 (violet / worthless piece of violet glass) +# tile 471 (violet / worthless piece of violet glass) { ................ ................ @@ -9018,7 +9018,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 468 (gray / luckstone) +# tile 472 (gray / luckstone) { ................ ................ @@ -9037,7 +9037,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 469 (gray / loadstone) +# tile 473 (gray / loadstone) { ................ ................ @@ -9056,7 +9056,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 470 (gray / touchstone) +# tile 474 (gray / touchstone) { ................ ................ @@ -9075,7 +9075,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 471 (gray / flint) +# tile 475 (gray / flint) { ................ ................ @@ -9094,7 +9094,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 472 (rock) +# tile 476 (rock) { ................ ................ @@ -9113,7 +9113,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 473 (boulder) +# tile 477 (boulder) { ................ ................ @@ -9132,7 +9132,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 474 (statue) +# tile 478 (statue) { ................ ........JJ...... @@ -9151,7 +9151,7 @@ Z = (195, 195, 195) .....JJJJJJAA... ................ } -# tile 475 (heavy iron ball) +# tile 479 (heavy iron ball) { ................ ................ @@ -9170,7 +9170,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 476 (iron chain) +# tile 480 (iron chain) { ................ ................ @@ -9189,7 +9189,7 @@ Z = (195, 195, 195) ...........PP.PA ............AA.. } -# tile 477 (splash of venom / splash of blinding venom) +# tile 481 (splash of venom / splash of blinding venom) { ................ ................ @@ -9208,7 +9208,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 478 (splash of venom / splash of acid venom) +# tile 482 (splash of venom / splash of acid venom) { ................ ................ From fdbf84b9b743d92ede13458875bb631571f95d61 Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 6 Apr 2026 13:17:32 -0400 Subject: [PATCH 403/442] remove some dead code --- src/options.c | 63 +-------------------------------------------------- 1 file changed, 1 insertion(+), 62 deletions(-) diff --git a/src/options.c b/src/options.c index 5f21a86c3..7bd3e155d 100644 --- a/src/options.c +++ b/src/options.c @@ -7360,67 +7360,6 @@ initoptions_finish(void) } #if 0 - (void) fruitadd(svp.pl_fruit, (struct fruit *) 0); - /* - * Remove "slime mold" from list of object names. This will - * prevent it from being wished unless it's actually present - * as a named (or default) fruit. Wishing for "fruit" will - * result in the player's preferred fruit. [Once upon a time - * the override value used was "\033" which prevented wishing - * for the slime mold object at all except by asking for a - * specific named fruit.] Note that there are multiple fruit - * object types (apple, melon, &c) but the "fruit" object is - * slime mold or whatever custom name player assigns to that. - */ - obj_descr[SLIME_MOLD].oc_name = "fruit"; - - sym = get_othersym(SYM_BOULDER, - Is_rogue_level(&u.uz) ? ROGUESET : PRIMARYSET); - if (sym) - gs.showsyms[SYM_BOULDER + SYM_OFF_X] = sym; - reglyph_darkroom(); - reset_glyphmap(gm_optionchange); -#ifdef STATUS_HILITES - /* - * A multi-interface binary might only support status highlighting - * for some of the interfaces; check whether we asked for it but are - * using one which doesn't. - * - * Option processing can take place before a user-decided WindowPort - * is even initialized, so check for that too. - */ - if (!WINDOWPORT(safestartup)) { - if (iflags.hilite_delta && !wc2_supported("statushilites")) { - raw_printf("Status highlighting not supported for %s interface.", - windowprocs.name); - iflags.hilite_delta = 0; - } - } -#endif - update_rest_on_space(); - - /* these can't rely on compile-time initialization for their defaults - because a multi-interface binary might need different values for - different interfaces; if neither tiled_map nor ascii_map pass the - wc_supported() test, assume ascii_map */ - if (iflags.wc_tiled_map && !wc_supported("tiled_map")) - iflags.wc_tiled_map = FALSE, iflags.wc_ascii_map = TRUE; - else if (iflags.wc_ascii_map && !wc_supported("ascii_map") - && wc_supported("tiled_map")) - iflags.wc_ascii_map = FALSE, iflags.wc_tiled_map = TRUE; - - if (iflags.wc_tiled_map && !opt_set_in_config[opt_color]) - iflags.wc_color = TRUE; - if (iflags.wc_ascii_map && !iflags.wc_color - && !opt_set_in_config[opt_bgcolors]) - iflags.bgcolors = FALSE; - - if (glyphid_cache_status()) - free_glyphid_cache(); - apply_customizations(gc.currentgraphics, - (do_custom_colors | do_custom_symbols)); - go.opt_initial = FALSE; - /* * Do these after clearing the 'opt_initial' flag. */ @@ -7435,9 +7374,9 @@ initoptions_finish(void) if (can_set_perm_invent()) iflags.perm_invent = TRUE; } - return; } #endif + void allopt_array_init(void) { From cf7a51459692f7f65f7d290cd1f7cc2a1112cb42 Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 7 Apr 2026 13:59:43 -0400 Subject: [PATCH 404/442] consoletty.c bit restrict a debugging line --- sys/windows/consoletty.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sys/windows/consoletty.c b/sys/windows/consoletty.c index bceb98132..7831d89e0 100644 --- a/sys/windows/consoletty.c +++ b/sys/windows/consoletty.c @@ -2569,7 +2569,10 @@ void nethack_enter_consoletty(void) /* srWindow identifies the visible area; dwSize identifies the buffer */ width = csbi.srWindow.Right - csbi.srWindow.Left + 1; - fprintf(stdout, "width = %d\n", width); +#ifdef DEBUG + if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) + fprintf(stdout, "width = %d\n", width); +#endif } console.hConOut = GetStdHandle(STD_OUTPUT_HANDLE); From 8cbbb74f3b7bc9e8ff651c846cf15d7889d85234 Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 7 Apr 2026 14:22:07 -0400 Subject: [PATCH 405/442] more early config file pass Because some optlist entries were ignored in the early pass, some non OPTIONS= lines in the config file were having issues due to those options not being set. Extend what is being disregarded in the early config file pass to include other config file statements. --- include/extern.h | 12 ++++++-- src/cfgfiles.c | 74 +++++++++++++++++++++++++++++++++++++++++++----- src/options.c | 17 ++++++----- 3 files changed, 85 insertions(+), 18 deletions(-) diff --git a/include/extern.h b/include/extern.h index 8226e9b91..67a938731 100644 --- a/include/extern.h +++ b/include/extern.h @@ -337,6 +337,13 @@ extern char *get_configfile(void); extern const char *get_default_configfile(void); extern void rcfile(void); extern void rcfile_interface_options(void); +extern void heed_all_config_statements(void); +extern void disregard_all_config_statements(void); +extern void heed_this_config_statement(int); +extern void disregard_this_config_statement(int); +extern boolean config_unmatched_ignored(void); +extern void clear_ignore_errors_on_unmatched(void); +extern void set_ignore_errors_on_unmatched(void); /* ### coloratt.c ### */ @@ -2322,10 +2329,11 @@ extern int msgtype_type(const char *, boolean) NONNULLARG1; extern void hide_unhide_msgtypes(boolean, int); extern void msgtype_free(void); extern void options_free_window_colors(void); -extern void set_all_options_heeded(void); -extern void set_all_options_disregarded(void); +extern void heed_all_options(void); +extern void disregard_all_options(void); extern void heed_this_option(enum opt); extern void disregard_this_option(enum opt); +extern void clear_ignore_errors_on_unmatched(void); #ifdef TTY_PERM_INVENT extern void check_perm_invent_again(void); #endif diff --git a/src/cfgfiles.c b/src/cfgfiles.c index 439747127..28ae34f54 100644 --- a/src/cfgfiles.c +++ b/src/cfgfiles.c @@ -110,6 +110,7 @@ staticfn void parse_conf_buf(struct _cnf_parser_state *parser, boolean (*proc)(char *arg)); /* next one is in extern.h; why here too? */ boolean parse_conf_str(const char *str, boolean (*proc)(char *arg)); +static boolean ignore_errors_on_unmatched = FALSE; #ifdef SFCTOOL #ifdef wait_synch @@ -1203,7 +1204,8 @@ cnf_line_SYMBOLS(char *bufp) switch_symbols(TRUE); return TRUE; } - config_error_add("Error in SYMBOLS definition '%s'", bufp); + if (!config_unmatched_ignored()) + config_error_add("Error in SYMBOLS definition '%s'", bufp); return FALSE; } @@ -1295,7 +1297,7 @@ typedef boolean (*config_line_stmt_func)(char *); /* normal, alias */ #define CNFL_NA(n, l, f) { #n, l, FALSE, FALSE, cnf_line_##f } /* sysconf only */ -#define CNFL_S(n, l) { #n, l, TRUE, FALSE, cnf_line_##n } +#define CNFL_S(n, l) { #n, l, TRUE, FALSE, cnf_line_##n } static const struct match_config_line_stmt { const char *name; @@ -1380,6 +1382,8 @@ static const struct match_config_line_stmt { #undef CNFL_NA #undef CNFL_S +static boolean disregarded_config_lines[SIZE(config_line_stmt)]; + boolean parse_config_line(char *origbuf) { @@ -1422,11 +1426,13 @@ parse_config_line(char *origbuf) config_line_stmt[i].len)) { char *parm = config_line_stmt[i].origbuf ? origbuf : bufp; - return config_line_stmt[i].fn(parm); + if (!disregarded_config_lines[i]) + return config_line_stmt[i].fn(parm); } } - config_error_add("Unknown config statement"); + if (!ignore_errors_on_unmatched) + config_error_add("Unknown config statement"); return FALSE; } @@ -1859,7 +1865,8 @@ config_error_add(const char *str, ...) va_list the_args; va_start(the_args, str); - vconfig_error_add(str, the_args); + if (!config_unmatched_ignored()) + vconfig_error_add(str, the_args); va_end(the_args); } @@ -1951,13 +1958,66 @@ void rcfile_interface_options(void) { allopt_array_init(); - set_all_options_disregarded(); + disregard_all_options(); + disregard_all_config_statements(); heed_this_option(opt_windowtype); heed_this_option(opt_soundlib); + set_ignore_errors_on_unmatched(); rcfile(); - set_all_options_heeded(); + heed_all_config_statements(); + heed_all_options(); disregard_this_option(opt_windowtype); disregard_this_option(opt_soundlib); + clear_ignore_errors_on_unmatched(); +} + +void +heed_all_config_statements(void) +{ + int i; + + for (i = 0; i < SIZE(disregarded_config_lines); i++) { + disregarded_config_lines[i] = FALSE; + } +} +void +disregard_all_config_statements(void) +{ + int i; + + for (i = 0; i < SIZE(disregarded_config_lines); i++) { + disregarded_config_lines[i] = TRUE; + } +} +void +heed_this_config_statement(int statement_idx) +{ + if (statement_idx >= 0 && statement_idx < SIZE(disregarded_config_lines)) + disregarded_config_lines[statement_idx] = FALSE; +} +void +disregard_this_config_statement(int statement_idx) +{ + if (statement_idx >= 0 && statement_idx < SIZE(disregarded_config_lines)) + disregarded_config_lines[statement_idx] = TRUE; +} + +void +clear_ignore_errors_on_unmatched(void) +{ + ignore_errors_on_unmatched = FALSE; +} +void +set_ignore_errors_on_unmatched(void) +{ + ignore_errors_on_unmatched = TRUE; +} +boolean +config_unmatched_ignored(void) +{ + if (ignore_errors_on_unmatched) + return TRUE; + return FALSE; } #ifdef SYSCF diff --git a/src/options.c b/src/options.c index 7bd3e155d..bada22768 100644 --- a/src/options.c +++ b/src/options.c @@ -106,7 +106,6 @@ extern char ttycolors[CLR_MAX]; /* in sys/msdos/video.c */ static char empty_optstr[] = { '\0' }; static boolean duplicate, using_alias; static boolean give_opt_msg = TRUE; -static boolean restricted_options_mode = FALSE; enum { MAX_ROLEOPT = 4 }; /* 4: role,race,gend,algn */ static boolean opt_set_in_config[OPTCOUNT]; @@ -668,7 +667,9 @@ parseoptions( } } - if (optresult == optn_silenterr || restricted_options_mode) + if (optresult == optn_silenterr + || (got_match && allopt[matchidx].disregarded) + || (!got_match && config_unmatched_ignored())) return FALSE; if (pfx_match && optresult == optn_err) { char pfxbuf[BUFSZ], *pfxp; @@ -7390,7 +7391,7 @@ allopt_array_init(void) if (allopt[i].addr) *(allopt[i].addr) = allopt[i].initval; } - set_all_options_heeded(); + heed_all_options(); /* * Call each option function with an init flag and give it a chance * to make any preparations that it might require. We do this @@ -10180,23 +10181,21 @@ enhance_menu_text( } void -set_all_options_heeded(void) +heed_all_options(void) { int i; for (i = 0; i < OPTCOUNT; i++) allopt[i].disregarded = FALSE; - restricted_options_mode = FALSE; } void -set_all_options_disregarded(void) +disregard_all_options(void) { int i; for (i = 0; i < OPTCOUNT ; i++) allopt[i].disregarded = TRUE; - restricted_options_mode = TRUE; } void @@ -10210,10 +10209,10 @@ disregard_this_option(enum opt optidx) { if (optidx >= 0 && optidx < (enum opt) OPTCOUNT) allopt[optidx].disregarded = TRUE; - if (!restricted_options_mode) - restricted_options_mode = TRUE; } + + #undef OPTIONS_HEADING #undef CONFIG_SLOT From 0085feb56ac7a3831a31495f68ff45053df6fd88 Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 7 Apr 2026 14:29:58 -0400 Subject: [PATCH 406/442] follow-up: remove one unnecessary change --- src/cfgfiles.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cfgfiles.c b/src/cfgfiles.c index 28ae34f54..331f90134 100644 --- a/src/cfgfiles.c +++ b/src/cfgfiles.c @@ -1865,8 +1865,7 @@ config_error_add(const char *str, ...) va_list the_args; va_start(the_args, str); - if (!config_unmatched_ignored()) - vconfig_error_add(str, the_args); + vconfig_error_add(str, the_args); va_end(the_args); } From f8fbd9848eff1fef75e7cb30b70553296e052c3b Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 8 Apr 2026 10:32:29 -0400 Subject: [PATCH 407/442] a couple more minor startup tweaks --- src/cfgfiles.c | 8 ++++++-- sys/windows/windmain.c | 5 +++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/cfgfiles.c b/src/cfgfiles.c index 331f90134..89ffbd4ab 100644 --- a/src/cfgfiles.c +++ b/src/cfgfiles.c @@ -110,7 +110,8 @@ staticfn void parse_conf_buf(struct _cnf_parser_state *parser, boolean (*proc)(char *arg)); /* next one is in extern.h; why here too? */ boolean parse_conf_str(const char *str, boolean (*proc)(char *arg)); -static boolean ignore_errors_on_unmatched = FALSE; +static boolean ignore_errors_on_unmatched = FALSE, + ignore_statement_errors = FALSE; #ifdef SFCTOOL #ifdef wait_synch @@ -1409,7 +1410,8 @@ parse_config_line(char *origbuf) /* find the '=' or ':' */ bufp = find_optparam(buf); if (!bufp) { - config_error_add("Not a config statement, missing '='"); + if (!ignore_statement_errors) + config_error_add("Not a config statement, missing '='"); return FALSE; } /* skip past '=', then space between it and value, if any */ @@ -1962,12 +1964,14 @@ rcfile_interface_options(void) heed_this_option(opt_windowtype); heed_this_option(opt_soundlib); set_ignore_errors_on_unmatched(); + ignore_statement_errors = TRUE; rcfile(); heed_all_config_statements(); heed_all_options(); disregard_this_option(opt_windowtype); disregard_this_option(opt_soundlib); clear_ignore_errors_on_unmatched(); + ignore_statement_errors = FALSE; } void diff --git a/sys/windows/windmain.c b/sys/windows/windmain.c index c50a6d292..37a202378 100644 --- a/sys/windows/windmain.c +++ b/sys/windows/windmain.c @@ -238,6 +238,8 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ windowtype); /* sets all the window port function pointers */ init_nhwindows(&argc, argv); + /* if (GUILaunched || IsDebuggerPresent()) */ + getreturn_enabled = TRUE; #if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) /* Save current directory and make sure it gets restored when @@ -261,8 +263,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ chdir(gf.fqn_prefix[HACKPREFIX]); #endif - /* if (GUILaunched || IsDebuggerPresent()) */ - getreturn_enabled = TRUE; + check_recordfile((char *) 0); /* did something earlier flag a need to exit without starting a game? */ From 49cccb5a8d129f578783d0925ac3fef7c489c97b Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 9 Apr 2026 10:06:24 -0400 Subject: [PATCH 408/442] quiet a warning compiling sys/share/uudecode in recent compiler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ../sys/share/uudecode.c: In function ‘main’: ../sys/share/uudecode.c:131:32: warning: format ‘%o’ expects argument of type ‘unsigned int *’, but argument 3 has type ‘int *’ [-Wformat=] 131 | (void) sscanf(buf, "begin %o %s", &mode, dest); | ~^ ~~~~~ | | | | | int * | unsigned int * | %o --- sys/share/uudecode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/share/uudecode.c b/sys/share/uudecode.c index 223b94031..801aba412 100644 --- a/sys/share/uudecode.c +++ b/sys/share/uudecode.c @@ -99,7 +99,7 @@ int main(int argc, char **argv) { FILE *in, *out; - int mode; + unsigned int mode; char dest[128]; char buf[80]; From 213400529a946b1bc3237d2f63f0aa7b6ed1dfbc Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 9 Apr 2026 10:18:00 -0400 Subject: [PATCH 409/442] follow-up comment bit --- sys/share/uudecode.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sys/share/uudecode.c b/sys/share/uudecode.c index 801aba412..5a8d19ac0 100644 --- a/sys/share/uudecode.c +++ b/sys/share/uudecode.c @@ -43,6 +43,9 @@ * * Modified 05 Jan 2024 to avoid K&R function declarations, marked KR_PROTO. * + * Modified 09 Apr 2026 to change mode variable from (int) to (unsigned int) + * to match prototype of sscanf %o + * * $NHDT-Date: 1432512787 2015/05/25 00:13:07 $ $NHDT-Branch: master $:$NHDT-Revision: 1.7 $ */ From 4f5146306b9efa0dc32d819821649e0c3608254b Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 9 Apr 2026 10:20:01 -0400 Subject: [PATCH 410/442] follow-up: punctuation --- sys/share/uudecode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sys/share/uudecode.c b/sys/share/uudecode.c index 5a8d19ac0..9d3a4802b 100644 --- a/sys/share/uudecode.c +++ b/sys/share/uudecode.c @@ -44,9 +44,9 @@ * Modified 05 Jan 2024 to avoid K&R function declarations, marked KR_PROTO. * * Modified 09 Apr 2026 to change mode variable from (int) to (unsigned int) - * to match prototype of sscanf %o + * to match prototype of sscanf %o. * - * $NHDT-Date: 1432512787 2015/05/25 00:13:07 $ $NHDT-Branch: master $:$NHDT-Revision: 1.7 $ + * $NHDT-Date: 1775744389 2026/04/09 14:19:49 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.21 $ */ #ifndef lint From 8b2b749f6fefc3c59d6d6163f56cd571f021b77a Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 9 Apr 2026 10:28:52 -0400 Subject: [PATCH 411/442] quiet a warning observed when compiling consoletty The warning was observed during CI build: [warning]NetHack\sys\windows\consoletty.c(2573,29): Warning C4127: conditional expression is constant --- sys/windows/consoletty.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sys/windows/consoletty.c b/sys/windows/consoletty.c index 7831d89e0..bb67541a7 100644 --- a/sys/windows/consoletty.c +++ b/sys/windows/consoletty.c @@ -2541,6 +2541,9 @@ void early_raw_print(const char *s) * */ + +DISABLE_WARNING_CONDEXPR_IS_CONSTANT + void nethack_enter_consoletty(void) { int width; @@ -2776,6 +2779,8 @@ void nethack_enter_consoletty(void) } #endif /* TTY_GRAPHICS */ +RESTORE_WARNING_CONDEXPR_IS_CONSTANT + /* this is used as a printf() replacement when the window * system isn't initialized yet */ From 699c31459bc9d6a6a31f51e30e5e280c205a5514 Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 9 Apr 2026 10:32:24 -0400 Subject: [PATCH 412/442] follow-up: fix placement location --- sys/windows/consoletty.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sys/windows/consoletty.c b/sys/windows/consoletty.c index bb67541a7..600cc5b13 100644 --- a/sys/windows/consoletty.c +++ b/sys/windows/consoletty.c @@ -2777,10 +2777,11 @@ void nethack_enter_consoletty(void) console.is_ready = TRUE; nhUse(apisuccess); } -#endif /* TTY_GRAPHICS */ RESTORE_WARNING_CONDEXPR_IS_CONSTANT +#endif /* TTY_GRAPHICS */ + /* this is used as a printf() replacement when the window * system isn't initialized yet */ From 335cd65d5ac9bf4e595159d5f9b4be95722f9dea Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 9 Apr 2026 14:04:28 -0400 Subject: [PATCH 413/442] some further Windows startup tweaking --- sys/windows/windmain.c | 31 +++++++++++++++++++------------ win/win32/NetHackW.c | 7 +++++++ 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/sys/windows/windmain.c b/sys/windows/windmain.c index 37a202378..e80d40cca 100644 --- a/sys/windows/windmain.c +++ b/sys/windows/windmain.c @@ -198,6 +198,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ #endif gh.hname = "NetHack"; /* used for syntax messages */ + set_emergency_io(); #ifndef MSWIN_GRAPHICS early_init(argc, argv); /* already in WinMain for MSWIN_GRAPHICS */ #endif @@ -219,7 +220,6 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ nethack_enter_consoletty(); consoletty_open(1); #endif - set_emergency_io(); #ifdef EARLY_CONFIGFILE_PASS rcfile_interface_options(); @@ -237,10 +237,6 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ choose_windows( windowtype); /* sets all the window port function pointers */ - init_nhwindows(&argc, argv); - /* if (GUILaunched || IsDebuggerPresent()) */ - getreturn_enabled = TRUE; - #if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) /* Save current directory and make sure it gets restored when * the game is exited. @@ -252,19 +248,19 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ set_default_prefix_locations( argv[0]); /* must be re-done after initoptions_init() * which clears out gp.fqn_prefix[] */ - iflags.windowtype_deferred = TRUE; + // iflags.windowtype_deferred = TRUE; program_state.early_options = 1; early_options(&argc, &argv, &dir); program_state.early_options = 0; - initoptions(); + /* if (GUILaunched || IsDebuggerPresent()) */ + getreturn_enabled = TRUE; + initoptions(); #if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) chdir(gf.fqn_prefix[HACKPREFIX]); #endif - - check_recordfile((char *) 0); /* did something earlier flag a need to exit without starting a game? */ if (windows_startup_state > 0) { @@ -292,7 +288,21 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ && (strstri(argv[0], "nethackw.exe") || GUILaunched)) iflags.windowtype_locked = TRUE; #endif +#ifdef WINCHAIN + commit_windowchain(); +#endif + init_nhwindows(&argc, argv); +#ifdef TTY_GRAPHICS + if WINDOWPORT(tty) { + int i; + for (i = 0; i < 20; ++i) { + nh_delay_output(); + } + + /* wait_synch(); */ + } +#endif #ifdef DLB if (!dlb_init()) { pline("%s\n%s\n%s\n%s\n\n", copyright_banner_line(1), @@ -318,9 +328,6 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ iflags.use_background_glyph = FALSE; if (WINDOWPORT(mswin)) iflags.use_background_glyph = TRUE; -#ifdef WINCHAIN - commit_windowchain(); -#endif // init_nhwindows(&argc, argv); diff --git a/win/win32/NetHackW.c b/win/win32/NetHackW.c index 56d8bb7c7..b2f76409f 100644 --- a/win/win32/NetHackW.c +++ b/win/win32/NetHackW.c @@ -211,6 +211,13 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, free(savefile); } GUILaunched = 1; + /* emergency IO */ + windowprocs.win_raw_print = mswin_raw_print; + windowprocs.win_raw_print_bold = mswin_raw_print_bold; + mswin_nh_input_init(); + windowprocs.win_nhgetch = mswin_nhgetch; + windowprocs.win_wait_synch = mswin_wait_synch; + /* let nethackw_main do the argument processing */ nethackw_main(argc, argv); /* not reached */ From 3b7c0e8a19b4cff4f98b0e07cad0d01b18e219aa Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 9 Apr 2026 14:14:49 -0400 Subject: [PATCH 414/442] follow-up: more Windows startup --- win/win32/NetHackW.c | 1 - 1 file changed, 1 deletion(-) diff --git a/win/win32/NetHackW.c b/win/win32/NetHackW.c index b2f76409f..5a945af5c 100644 --- a/win/win32/NetHackW.c +++ b/win/win32/NetHackW.c @@ -214,7 +214,6 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, /* emergency IO */ windowprocs.win_raw_print = mswin_raw_print; windowprocs.win_raw_print_bold = mswin_raw_print_bold; - mswin_nh_input_init(); windowprocs.win_nhgetch = mswin_nhgetch; windowprocs.win_wait_synch = mswin_wait_synch; From 4dd47db3ad4e5f2ef1de943772326234d68ffaab Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 9 Apr 2026 15:08:07 -0400 Subject: [PATCH 415/442] yet more Windows startup; also safestartup vestige --- include/winprocs.h | 2 +- src/earlyarg.c | 6 ++++++ src/options.c | 10 ++++------ src/windows.c | 2 +- sys/windows/windmain.c | 5 +++-- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/include/winprocs.h b/include/winprocs.h index 014ee1e09..45463205e 100644 --- a/include/winprocs.h +++ b/include/winprocs.h @@ -11,7 +11,7 @@ #endif enum wp_ids { wp_tty = 1, wp_X11, wp_Qt, wp_mswin, wp_curses, - wp_chainin, wp_chainout, wp_safestartup, wp_shim, + wp_chainin, wp_chainout, wp_shim, wp_hup, wp_guistubs, wp_ttystubs, #if defined(AMIGA) wp_amii, wp_amiv, diff --git a/src/earlyarg.c b/src/earlyarg.c index 14a44f770..78feae94a 100755 --- a/src/earlyarg.c +++ b/src/earlyarg.c @@ -424,6 +424,12 @@ scores_only(int argc, char **argv, const char *dir) (void) whoami(); /* set up default plname[] */ #endif prscore(argc, argv); +#ifdef MSWIN_GRAPHICS + /* NetHackW can also support WINDOWPORT(curses) now, so check */ + if (WINDOWPORT(mswin)) { + wait_synch(); + } +#endif nh_terminate(EXIT_SUCCESS); /* bypass opt_terminate() */ /*NOTREACHED*/ diff --git a/src/options.c b/src/options.c index bada22768..c39100f94 100644 --- a/src/options.c +++ b/src/options.c @@ -7331,12 +7331,10 @@ initoptions_finish(void) * Option processing can take place before a user-decided WindowPort * is even initialized, so check for that too. */ - if (!WINDOWPORT(safestartup)) { - if (iflags.hilite_delta && !wc2_supported("statushilites")) { - raw_printf("Status highlighting not supported for %s interface.", - windowprocs.name); - iflags.hilite_delta = 0; - } + if (iflags.hilite_delta && !wc2_supported("statushilites")) { + raw_printf("Status highlighting not supported for %s interface.", + windowprocs.name); + iflags.hilite_delta = 0; } #endif update_rest_on_space(); diff --git a/src/windows.c b/src/windows.c index b8cc8ea8d..a36b0eaa4 100644 --- a/src/windows.c +++ b/src/windows.c @@ -333,7 +333,7 @@ choose_windows(const char *s) if (tmps) free((genericptr_t) tmps) /*, tmps = 0*/ ; - if (windowprocs.win_raw_print == def_raw_print || WINDOWPORT(safestartup)) + if (windowprocs.win_raw_print == def_raw_print) nh_terminate(EXIT_SUCCESS); } diff --git a/sys/windows/windmain.c b/sys/windows/windmain.c index e80d40cca..8da9ddec9 100644 --- a/sys/windows/windmain.c +++ b/sys/windows/windmain.c @@ -244,6 +244,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ if (getcwd(orgdir, sizeof orgdir) == (char *) 0) error("NetHack: current directory path too long"); #endif + getreturn_enabled = TRUE; initoptions_init(); // This allows OPTIONS in syscf on Windows. set_default_prefix_locations( argv[0]); /* must be re-done after initoptions_init() @@ -251,10 +252,10 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ // iflags.windowtype_deferred = TRUE; program_state.early_options = 1; + /* if (GUILaunched || IsDebuggerPresent()) */ early_options(&argc, &argv, &dir); program_state.early_options = 0; - /* if (GUILaunched || IsDebuggerPresent()) */ - getreturn_enabled = TRUE; + initoptions(); #if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) From e005c26fbd9c511b1459ccd2dd07b8d24f124c05 Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 10 Apr 2026 09:24:10 -0400 Subject: [PATCH 416/442] another safestartup vestige --- src/options.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/options.c b/src/options.c index c39100f94..ffaaf03e2 100644 --- a/src/options.c +++ b/src/options.c @@ -5482,9 +5482,6 @@ can_set_perm_invent(void) #ifdef TTY_PERM_INVENT if ((WINDOWPORT(tty) -#ifdef WIN32 - || WINDOWPORT(safestartup) -#endif ) && !go.opt_initial) { perm_invent_toggled(FALSE); /* perm_invent_toggled() From 832db5b9e193343e6e1a5491160afa03d33247b6 Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 10 Apr 2026 09:26:24 -0400 Subject: [PATCH 417/442] define TTY_PERM_INVENT in Windows console build --- include/windconf.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/windconf.h b/include/windconf.h index b7330668c..81ad8baeb 100644 --- a/include/windconf.h +++ b/include/windconf.h @@ -33,6 +33,7 @@ #define OPTIONS_AT_RUNTIME /* build info done at runtime not text file */ #define EARLY_CONFIGFILE_PASS +#define TTY_PERM_INVENT /* * ----------------------------------------------------------------- From 9172b7eb096a599b3cd9948d150ce7d08e861ac2 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Fri, 10 Apr 2026 16:39:16 +0300 Subject: [PATCH 418/442] Add tutorial and tips to Windows nethackrc template --- sys/windows/nethackrc.template | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sys/windows/nethackrc.template b/sys/windows/nethackrc.template index 60db0ba77..6529b1505 100644 --- a/sys/windows/nethackrc.template +++ b/sys/windows/nethackrc.template @@ -57,6 +57,12 @@ OPTIONS=!autopickup # Set the default boulder symbol to '0' OPTIONS=boulder:0 +# Disable the prompt for a tutorial +#OPTIONS=!tutorial + +# Disable game tips +#OPTIONS=!tips + # Highlight menu lines with different colors. You need to define the colors # with MENUCOLOR lines. # Toggle menucolor use on or off From fffbf4c2e5df478413083feaf3d635e85ab1fb3e Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 10 Apr 2026 10:36:36 -0400 Subject: [PATCH 419/442] Windows option update for map_mode Ensure preference_update() is called --- src/options.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/options.c b/src/options.c index ffaaf03e2..4d04efc1c 100644 --- a/src/options.c +++ b/src/options.c @@ -1978,6 +1978,8 @@ optfn_map_mode( */ op = string_for_opt(opts, negated); if (op != empty_optstr && !negated) { + int save_map_mode = iflags.wc_map_mode; + if (!strcmpi(op, "tiles")) iflags.wc_map_mode = MAP_MODE_TILES; else if (!strncmpi(op, "ascii4x6", sizeof "ascii4x6" - 1)) @@ -2012,6 +2014,11 @@ optfn_map_mode( allopt[optidx].name, op); return optn_err; } + if (wc_supported("map_mode")) { + if (!iflags.wc_map_mode + || save_map_mode != iflags.wc_map_mode) + preference_update("map_mode"); + } } else if (negated) { bad_negation(allopt[optidx].name, TRUE); return optn_err; From 3acb327115439bdacbe0e2afcb5a329cfafaa9b1 Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 10 Apr 2026 10:53:53 -0400 Subject: [PATCH 420/442] resolve a warning on one platform --- win/tty/wintty.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/win/tty/wintty.c b/win/tty/wintty.c index 4cbe65a2a..fb9ec4dbf 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -3559,8 +3559,8 @@ assesstty( short *offx, short *offy, long *rows, long *cols, long *maxcol, long *minrow, long *maxrow) { - boolean inuse_only = (invmode & (int) InvInUse) != 0, - show_gold = (invmode & (int) InvShowGold) != 0 + boolean inuse_only = ((int) invmode & (int) InvInUse) != 0, + show_gold = ((int) invmode & (int) InvShowGold) != 0 && !inuse_only; int perminv_minrow = tty_perminv_minrow + (show_gold ? 1 : 0); From e8290bc27501570cd2cd97ea286a099f3b133457 Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 10 Apr 2026 11:14:22 -0400 Subject: [PATCH 421/442] try testing a Windows 11 snapdragon build in CI --- azure-pipelines.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index bbb299e0f..1966218f5 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -28,6 +28,11 @@ strategy: imageName: 'windows-latest' toolchainName: vs buildTargetName: all + windows-visualstudio-arm64: + imageName: 'windows-latest' + toolchainName: vs + buildTargetName: all + buildPlatform: 'ARM64' # Targeting Snapdragon windows-mingw: imageName: 'windows-2025' toolchainName: mingw From 45b01cdb9ab1d33de2d66e52239ac28d596353d5 Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 10 Apr 2026 11:30:46 -0400 Subject: [PATCH 422/442] nomenclature of a couple of tasks in CI --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 1966218f5..7c7eea340 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -24,11 +24,11 @@ strategy: imageName: 'macOS-15' toolchainName: clang buildTargetName: all - windows-visualstudio: + windows-x64-vstudio: imageName: 'windows-latest' toolchainName: vs buildTargetName: all - windows-visualstudio-arm64: + windows-arm64-vstudio: imageName: 'windows-latest' toolchainName: vs buildTargetName: all From b264e17cf25ff2784f0e0ecdb7757c9b28fe2752 Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 11 Apr 2026 01:02:42 -0700 Subject: [PATCH 423/442] fix use-after-free bug in curses shutdown A year ago a use-free-bug was reported for curses. I don't use ASAN so haven't reproduced it, but I think this should fix it. If the RIP window is deleted after the map window has gone away, the code from commit 5e572d3d5f2581d14c29bd0071c6be0d7fbdce5e (post 3.6.7) would execute and access the internals of the deleted map window. --- win/curses/curswins.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/win/curses/curswins.c b/win/curses/curswins.c index 9fe04edd4..52e21db37 100644 --- a/win/curses/curswins.c +++ b/win/curses/curswins.c @@ -224,6 +224,10 @@ curses_destroy_win(WINDOW *win) delwin(win); if (win == activemenu) activemenu = NULL; + /* during shutdown, RIP window could still be active after mapwin goes + away; so, avoid 'if (mapwin)' above when deleting RIP window later */ + if (win == mapwin) + win = mapwin = NULL; curses_refresh_nethack_windows(); nhUse(dummyht); } From 85f03deca9f8c5104757e1ba695d883a341c2c7d Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 11 Apr 2026 06:37:10 -0400 Subject: [PATCH 424/442] refine a dog_eat impossible() Have the impossible message indicate whether the pointed-to edog struct itself is still intact (not overwritten somewhere, such as misuse of a stale or bad pointer) versus the apport field itself being assigned an out-of-whack value some place. --- src/dogmove.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/dogmove.c b/src/dogmove.c index d0005ab78..f4c7b4bcb 100644 --- a/src/dogmove.c +++ b/src/dogmove.c @@ -316,9 +316,13 @@ dog_eat(struct monst *mtmp, edog->apport += (int) (200L / ((long) edog->dropdist + svm.moves - edog->droptime)); if (edog->apport <= 0) { - impossible("dog_eat: pet apport <= 0 (%d, %d, %ld, %ld)", + impossible("dog_eat: pet apport <= 0 (%d, %d, %ld, %ld, %ud, %ud)", edog->apport, edog->dropdist, edog->droptime, - svm.moves); + svm.moves, + /* check whether edog struct got clobbered; + these two values should always match if + edog content is still intact */ + mtmp->m_id, edog->parentmid); edog->apport = 1; } } From f29cea4a35c6cda21c1272f3bf50dcafc278bd05 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 11 Apr 2026 07:17:20 -0400 Subject: [PATCH 425/442] another impossible() update for dog_eat() --- src/dogmove.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/dogmove.c b/src/dogmove.c index f4c7b4bcb..0cfa6f23d 100644 --- a/src/dogmove.c +++ b/src/dogmove.c @@ -313,12 +313,15 @@ dog_eat(struct monst *mtmp, /* It's a reward if it's DOGFOOD and the player dropped/threw it. We know the player had it if invlet is set. -dlc */ if (dogfood(mtmp, obj) == DOGFOOD && obj->invlet) { + int prior_apport = edog->apport; + edog->apport += (int) (200L / ((long) edog->dropdist + svm.moves - edog->droptime)); if (edog->apport <= 0) { - impossible("dog_eat: pet apport <= 0 (%d, %d, %ld, %ld, %ud, %ud)", + impossible("dog_eat: pet apport <= 0 (%d, %d, %ld, %ld, %d, %ud, %ud)", edog->apport, edog->dropdist, edog->droptime, svm.moves, + prior_apport, /* check whether edog struct got clobbered; these two values should always match if edog content is still intact */ From f6e341546db0d9903a58f87205b1e81619dd6a41 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 11 Apr 2026 11:10:47 -0400 Subject: [PATCH 426/442] thinko --- src/dogmove.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dogmove.c b/src/dogmove.c index 0cfa6f23d..1218c305e 100644 --- a/src/dogmove.c +++ b/src/dogmove.c @@ -318,7 +318,7 @@ dog_eat(struct monst *mtmp, edog->apport += (int) (200L / ((long) edog->dropdist + svm.moves - edog->droptime)); if (edog->apport <= 0) { - impossible("dog_eat: pet apport <= 0 (%d, %d, %ld, %ld, %d, %ud, %ud)", + impossible("dog_eat: pet apport <= 0 (%d, %d, %ld, %ld, %d, %u, %u)", edog->apport, edog->dropdist, edog->droptime, svm.moves, prior_apport, From 9d27cfe8b819af6b216b0745a1460b08039af9dd Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sat, 11 Apr 2026 18:37:45 +0300 Subject: [PATCH 427/442] Prevent selecting all options in #optionsfull menu No-one ever intentionally selected all entries in the options menu. Prevent it from happening by accident. --- doc/fixes3-7-0.txt | 1 + src/options.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 25782946e..7a152e09e 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1594,6 +1594,7 @@ wizard mode: history menu for #wizwish and WIZKIT monster priests and wizards did not cast spells prevent phaseable monsters hiding deep inside nondiggable walls blessed potion of see invisible does not guarantee the intrinsic +prevent selecting all options in #optionsfull menu Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/options.c b/src/options.c index 4d04efc1c..a2dc7cedf 100644 --- a/src/options.c +++ b/src/options.c @@ -8862,7 +8862,7 @@ doset(void) /* changing options via menu by Per Liboriussen */ enhance_menu_text(buf, sizeof buf, pass, bool_p, &allopt[i]); add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, buf, MENU_ITEMFLAGS_NONE); + ATR_NONE, clr, buf, MENU_ITEMFLAGS_SKIPINVERT); } add_menu_str(tmpwin, ""); @@ -9067,7 +9067,7 @@ doset_add_menu( indent = !any.a_int ? " " : ""; Sprintf(buf, fmtstr, indent, option, value); add_menu(win, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, buf, MENU_ITEMFLAGS_NONE); + ATR_NONE, clr, buf, MENU_ITEMFLAGS_SKIPINVERT); } From 80cc5ecb29940b23f8baf3e28b0eda363add6a4b Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 11 Apr 2026 14:08:36 -0400 Subject: [PATCH 428/442] some windows cleanup at end of game --- sys/windows/consoletty.c | 6 +++++- sys/windows/windmain.c | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/sys/windows/consoletty.c b/sys/windows/consoletty.c index 600cc5b13..988e7663a 100644 --- a/sys/windows/consoletty.c +++ b/sys/windows/consoletty.c @@ -870,7 +870,8 @@ back_buffer_flip(void) do_anything |= do_wide_content; } else { #endif - if (strcmp((const char *) back->utf8str, + if (back->utf8str && front->utf8str + && strcmp((const char *) back->utf8str, (const char *) front->utf8str)) do_anything |= do_utf8_content; #ifdef UTF8_FROM_CORE @@ -1184,6 +1185,7 @@ consoletty_open(int mode UNUSED) really_move_cursor(); nhUse(debugvar); } +extern void set_emergency_io(void); void consoletty_exit(void) @@ -1191,8 +1193,10 @@ consoletty_exit(void) free_custom_colors(); free((genericptr_t) console.front_buffer); free((genericptr_t) console.back_buffer); + console.front_buffer = console.back_buffer = 0; free((genericptr_t) console.localestr); free((genericptr_t) console.orig_localestr); + set_emergency_io(); } int diff --git a/sys/windows/windmain.c b/sys/windows/windmain.c index 8da9ddec9..a5bf3ae65 100644 --- a/sys/windows/windmain.c +++ b/sys/windows/windmain.c @@ -101,7 +101,7 @@ void update_file(const char *, const char *, const char *, const char *, BOOL); void windows_raw_print_bold(const char *); -staticfn void set_emergency_io(void); +void set_emergency_io(void); staticfn void stdio_wait_synch(void); staticfn void stdio_raw_print(const char *str); staticfn void stdio_nonl_raw_print(const char *str); From 8cf6c628626d85d3b189c8da81c753864e1d4ac1 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sat, 11 Apr 2026 23:05:41 +0300 Subject: [PATCH 429/442] Don't put non-m-prefix command msg into history --- src/cmd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cmd.c b/src/cmd.c index 8457f8b14..c0718e4e4 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -3704,7 +3704,8 @@ rhack(int key) * the former call to help_dir() (for 'bad_command' below). */ if (was_m_prefix) { - pline("The %s command does not accept '%s' prefix.", + custompline(SUPPRESS_HISTORY, + "The %s command does not accept '%s' prefix.", tlist->ef_txt, which); } else { uchar ch = tlist->key; From c7ccd4ca5dfaa47f4eb9cd07667f68bc5b3dbc18 Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 11 Apr 2026 17:35:35 -0700 Subject: [PATCH 430/442] add pet->droptime check to monster sanity check I don't see where loading or saving bones makes any attempt to fix up pet->droptime at all. --- src/mon.c | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/src/mon.c b/src/mon.c index c0d92e6c3..a245757be 100644 --- a/src/mon.c +++ b/src/mon.c @@ -6,6 +6,7 @@ #include "hack.h" #include "mfndpos.h" +staticfn void pet_sanity_check(struct monst *, const char *); staticfn void sanity_check_single_mon(struct monst *, boolean, const char *); staticfn struct obj *make_corpse(struct monst *, unsigned); staticfn int minliquid_core(struct monst *); @@ -45,14 +46,28 @@ extern const struct shclass shtypes[]; /* defined in shknam.c */ || !svl.level.flags.deathdrops \ || (svl.level.flags.graveyard && is_undead(mdat) && rn2(3))) - -#if 0 +#if 0 /* potentially of historical interest */ /* part of the original warning code which was replaced in 3.3.1 */ const char *warnings[] = { "white", "pink", "red", "ruby", "purple", "black" }; #endif /* 0 */ +staticfn void +pet_sanity_check( + struct monst *mtmp, + const char *msgarg) +{ + if (has_edog(mtmp)) { + struct edog *edog = EDOG(mtmp); + + if (edog->droptime > svm.moves) + impossible("insane pet #%u has droptime (%ld)" + " in the future (%ld) (%s)", + mtmp->m_id, edog->droptime, svm.moves, msgarg); + /* TODO: verify some of the other edog fields */ + } +} staticfn void sanity_check_single_mon( @@ -116,8 +131,12 @@ sanity_check_single_mon( if (mtmp->isminion && !has_emin(mtmp)) impossible("minion without emin (%s)", msg); /* guardian angel on astral level is tame but has emin rather than edog */ - if (mtmp->mtame && !has_edog(mtmp) && !mtmp->isminion) - impossible("pet without edog (%s)", msg); + if (mtmp->mtame) { + if (!has_edog(mtmp) && !mtmp->isminion) + impossible("pet without edog (%s)", msg); + else + pet_sanity_check(mtmp, msg); + } /* steed should be tame and saddled */ if (mtmp == u.usteed) { const char *ns, *nt = !mtmp->mtame ? "not tame" : 0; @@ -133,7 +152,7 @@ sanity_check_single_mon( if (mtmp->mtrapped) { if (mtmp->wormno) { - /* TODO: how to check worm in trap? */ + ; /* TODO: how to check worm in trap? */ } else if (!t_at(mx, my)) impossible("trapped without a trap (%s)", msg); } @@ -6056,4 +6075,12 @@ flash_mon(struct monst *mtmp) gv.viz_array[my][mx] = saveviz; newsym(mx, my); } + +/* cleanup for 'onefile' processing */ +#undef LEVEL_SPECIFIC_NOCORPSE +#undef KEEPTRAITS +#undef mstoning +#undef livelog_mon_nam +#undef BREEDER_EGG + /*mon.c*/ From 26453eaea0305be94c33881f73a4b32ed6941983 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 12 Apr 2026 15:54:14 +0300 Subject: [PATCH 431/442] Allow polearming a monster in unlit space ... as long as you can see the monster, eg. via infravision --- src/apply.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/apply.c b/src/apply.c index 3abfc5b6c..0fa915a42 100644 --- a/src/apply.c +++ b/src/apply.c @@ -52,7 +52,7 @@ staticfn boolean check_jump(genericptr_t, coordxy, coordxy); staticfn boolean is_valid_jump_pos(coordxy, coordxy, int, boolean); staticfn boolean get_valid_jump_position(coordxy, coordxy); staticfn boolean get_valid_polearm_position(coordxy, coordxy); -staticfn boolean find_poleable_mon(coord *, int, int); +staticfn boolean find_poleable_mon(coord *); static const char no_elbow_room[] = "don't have enough elbow-room to maneuver."; @@ -3280,7 +3280,7 @@ static const char /* find pos of monster in range, if only one monster */ staticfn boolean -find_poleable_mon(coord *pos, int min_range, int max_range) +find_poleable_mon(coord *pos) { struct monst *mtmp; coord mpos = { 0, 0 }; /* no candidate location yet */ @@ -3289,13 +3289,12 @@ find_poleable_mon(coord *pos, int min_range, int max_range) int glyph; impaired = (Confusion || Stunned || Hallucination); - rt = isqrt(max_range); + rt = isqrt(gp.polearm_range_max); lo_x = max(u.ux - rt, 1), hi_x = min(u.ux + rt, COLNO - 1); lo_y = max(u.uy - rt, 0), hi_y = min(u.uy + rt, ROWNO - 1); for (x = lo_x; x <= hi_x; ++x) { for (y = lo_y; y <= hi_y; ++y) { - if (distu(x, y) < min_range || distu(x, y) > max_range - || !isok(x, y) || !cansee(x, y)) + if (!get_valid_polearm_position(x, y)) continue; glyph = glyph_at(x, y); if (!impaired @@ -3401,7 +3400,7 @@ could_pole_mon(void) cc.x = u.ux; cc.y = u.uy; - if (!find_poleable_mon(&cc, min_range, max_range)) { + if (!find_poleable_mon(&cc)) { if (hitm && !DEADMONSTER(hitm) && sensemon(hitm) && mdistu(hitm) <= max_range && mdistu(hitm) >= min_range) return TRUE; @@ -3454,7 +3453,7 @@ use_pole(struct obj *obj, boolean autohit) pline(where_to_hit); cc.x = u.ux; cc.y = u.uy; - if (!find_poleable_mon(&cc, min_range, max_range) && hitm + if (!find_poleable_mon(&cc) && hitm && !DEADMONSTER(hitm) && sensemon(hitm) && mdistu(hitm) <= max_range && mdistu(hitm) >= min_range) { cc.x = hitm->mx; From 635dca05d592a1bb66d3dbe8e26f7719a785a78b Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 12 Apr 2026 18:59:00 +0300 Subject: [PATCH 432/442] Shapeshifters change shape less Non-vampire shapeshifters were changing shape a lot; often you could have a chameleon turning into a different monster each turn. Now monster shape changing is actually their "special" power, and it takes 3-12 turns until they can do it again. --- doc/fixes3-7-0.txt | 1 + src/mon.c | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 7a152e09e..4a0bb71e4 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1595,6 +1595,7 @@ monster priests and wizards did not cast spells prevent phaseable monsters hiding deep inside nondiggable walls blessed potion of see invisible does not guarantee the intrinsic prevent selecting all options in #optionsfull menu +shapeshifters change shape less Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/mon.c b/src/mon.c index a245757be..789ca8276 100644 --- a/src/mon.c +++ b/src/mon.c @@ -4878,8 +4878,10 @@ decide_to_shapeshift(struct monst *mon) if (!is_vampshifter(mon)) { /* regular shapeshifter; 'ptr' is Null */ - if (!rn2(6)) + if (!mon->mspec_used && !rn2(6)) { dochng = TRUE; + mon->mspec_used = 3 + rn2(10); + } } else if (!(mon->mstrategy & STRAT_WAITFORU)) { /* The vampire has to be in good health (mhp) to maintain * its shifted form. From 8d6ab2f9abf1b2681a532b4aad8e00096ce8b8c9 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 12 Apr 2026 19:55:09 +0300 Subject: [PATCH 433/442] Allow m-prefixing #annotate This is the same as m-prefixing #overview --- doc/Guidebook.mn | 3 +++ doc/Guidebook.tex | 4 ++++ src/cmd.c | 2 +- src/dungeon.c | 2 ++ 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 03aefb6f9..aec4cbd6f 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -1247,6 +1247,9 @@ Autocompletes. Default key is \(oqM-A\(cq, and also \(oq\(haN\(cq if .op number_pad is on. +.lp "" +Preceding #annotate with the \(oqm\(cq prefix is the same as +#overview with the prefix. .lp "#apply " Apply (use) a tool such as a pick-axe, a key, or a lamp. Default key is \(oqa\(cq. diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index ec7c5d80e..8051d8117 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -1286,6 +1286,10 @@ dungeon level. All levels with annotations are displayed by the ``\texttt{\#overview}'' command. Autocompletes. Default key is `\texttt{M-A}', and also `\texttt{\textasciicircum N}' if \textit{number\textunderscore pad} is on. +\\ +%.lp "" +Preceding \#annotate with the `\texttt{m}' prefix is the same as +\#overview with the prefix. %.lp \item[\#apply] Apply (use) a tool such as a pick-axe, a key, or a lamp. diff --git a/src/cmd.c b/src/cmd.c index c0718e4e4..de20c283b 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -1673,7 +1673,7 @@ struct ext_func_tab extcmdlist[] = { { M('a'), "adjust", "adjust inventory letters", doorganize, IFBURIED | AUTOCOMPLETE | GENERALCMD, NULL }, { M('A'), "annotate", "name current level", - donamelevel, IFBURIED | AUTOCOMPLETE | GENERALCMD, NULL }, + donamelevel, IFBURIED | AUTOCOMPLETE | GENERALCMD | CMD_M_PREFIX, NULL }, { 'a', "apply", "apply (use) a tool (pick-axe, key, lamp...)", doapply, CMD_M_PREFIX, NULL }, { C('x'), "attributes", "show your attributes", diff --git a/src/dungeon.c b/src/dungeon.c index 5b172b815..e0bd72d08 100644 --- a/src/dungeon.c +++ b/src/dungeon.c @@ -2564,6 +2564,8 @@ query_annotation(d_level *lev) int donamelevel(void) { + if (iflags.menu_requested) + return dooverview(); query_annotation((d_level *) 0); return ECMD_OK; } From 7ed50f6ef8b3ddfa6875ff7585f17be5ab3da54d Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 12 Apr 2026 21:03:21 -0700 Subject: [PATCH 434/442] wand of stasis tweaks When wand of stasis is zapped multiple times, keep the longest duration rather than replace duration with each zap. When current level is under the effect of a wand of stasis, have the wizard-mode command #timeout say so. --- src/timeout.c | 10 +++++++++- src/zap.c | 11 ++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/timeout.c b/src/timeout.c index a8e5606b4..af974c84e 100644 --- a/src/timeout.c +++ b/src/timeout.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 timeout.c $NHDT-Date: 1756531249 2025/08/29 21:20:49 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.205 $ */ +/* NetHack 3.7 timeout.c $NHDT-Date: 1776080125 2026/04/13 03:35:25 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.207 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2018. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2112,6 +2112,14 @@ wiz_timeout_queue(void) if (any_visible_region()) { visible_region_summary(win); } + if (svl.level.flags.stasis_until >= svm.moves) { + putstr(win, 0, ""); + Sprintf(buf, "Level is no-teleport for %ld %s.", + svl.level.flags.stasis_until - svm.moves + 1L, + (svl.level.flags.stasis_until - svm.moves > 0L) + ? "turns" : "more turn"); + putstr(win, 0, buf); + } display_nhwindow(win, FALSE); destroy_nhwindow(win); diff --git a/src/zap.c b/src/zap.c index 83f25195a..ac6d8e760 100644 --- a/src/zap.c +++ b/src/zap.c @@ -2556,11 +2556,16 @@ zapnodir(struct obj *obj) known = !!obj->dknown; (void) findit(); break; - case WAN_STASIS: + case WAN_STASIS: { + long tmp_until = svm.moves + (long) rn1(21, 10); + /* no immediately obvious effect, and no message so that it isn't - distinguishable from other NODIR wands that produce no message */ - svl.level.flags.stasis_until = svm.moves + rn1(21, 10); + distinguishable from other NODIR wands that produce no message; + for multiple zaps, keep the longest duration rather than latest */ + if (tmp_until > svl.level.flags.stasis_until) + svl.level.flags.stasis_until = tmp_until; break; + } case WAN_CREATE_MONSTER: /* create_critters() returns True iff hero sees a new monster appear */ if (create_critters(rn2(23) ? 1 : rn1(7, 2), From 337c5189dd7bdbab102e167dd8c502c144ad4756 Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 13 Apr 2026 06:39:54 -0400 Subject: [PATCH 435/442] store relative timestamps in save and bones --- include/extern.h | 2 ++ src/restore.c | 10 +++++++++- src/save.c | 27 ++++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/include/extern.h b/include/extern.h index 67a938731..fe1b93b1b 100644 --- a/include/extern.h +++ b/include/extern.h @@ -2827,6 +2827,8 @@ extern void freedynamicdata(void); extern void store_savefileinfo(NHFILE *) NONNULLARG1; extern void store_savefileinfo(NHFILE *) NONNULLARG1; extern int nhdatatypes_size(void); +extern void moves_to_relative_time(long *); +extern void relative_time_to_moves(long *); #if 0 extern void assignlog(char *, char*, int); extern FILE *getlog(NHFILE *); diff --git a/src/restore.c b/src/restore.c index 191db7ddd..8f131d111 100644 --- a/src/restore.c +++ b/src/restore.c @@ -35,6 +35,7 @@ staticfn boolean restgamestate(NHFILE *); staticfn void rest_bubbles(NHFILE *); staticfn void restore_gamelog(NHFILE *); staticfn void restore_msghistory(NHFILE *); +staticfn void rest_adjust_levelflags(void); #endif /* @@ -1106,7 +1107,7 @@ getlev(NHFILE *nhfp, int pid, xint8 lev) Sfi_dest_area(nhfp, &svu.updest, "lev-updest"); Sfi_dest_area(nhfp, &svd.dndest, "lev-dndest"); Sfi_levelflags(nhfp, &svl.level.flags, "lev-level_flags"); - + rest_adjust_levelflags(); if (svd.doors) { free(svd.doors); svd.doors = 0; @@ -1302,6 +1303,13 @@ getlev(NHFILE *nhfp, int pid, xint8 lev) program_state.in_getlev = FALSE; } +void +rest_adjust_levelflags(void) +{ + /* adjust timestamps */ + relative_time_to_moves(&svl.level.flags.stasis_until); +} + /* "name-role-race-gend-algn" occurs very early in a save file; sometimes we want the whole thing, other times just "name" (for svp.plname[]) */ void diff --git a/src/save.c b/src/save.c index a7ea05e7a..e2c0ac5f9 100644 --- a/src/save.c +++ b/src/save.c @@ -31,7 +31,7 @@ staticfn void save_gamelog(NHFILE *); staticfn void savegamestate(NHFILE *); staticfn void savelev_core(NHFILE *, xint8); staticfn void save_msghistory(NHFILE *); - +staticfn void save_adjust_levelflags(void); #if defined(HANGUPHANDLING) #define HUP if (!program_state.done_hup) #else @@ -509,7 +509,9 @@ savelev_core(NHFILE *nhfp, xint8 lev) save_stairs(nhfp); Sfo_dest_area(nhfp, &svu.updest, "lev-updest"); Sfo_dest_area(nhfp, &svd.dndest, "lev-dndest"); + save_adjust_levelflags(); Sfo_levelflags(nhfp, &svl.level.flags, "lev-level_flags"); + rest_adjust_levelflags(); Sfo_int(nhfp, &svd.doors_alloc, "lev-doors_alloc"); /* don't rely on underlying write() behavior to write @@ -556,6 +558,13 @@ savelev_core(NHFILE *nhfp, xint8 lev) return; } +void +save_adjust_levelflags(void) +{ + /* adjust any timestamps */ + moves_to_relative_time(&svl.level.flags.stasis_until); +} + staticfn void savelevl(NHFILE *nhfp) { @@ -1033,6 +1042,22 @@ save_msghistory(NHFILE *nhfp) /* note: we don't attempt to handle release_data() here */ } +void +moves_to_relative_time(long *timestamp) +{ + long prevts = *timestamp; + + *timestamp = prevts - svm.moves; +} + +void +relative_time_to_moves(long *timestamp) +{ + long prevts = *timestamp; + + *timestamp = svm.moves + prevts; +} + /* also called by prscore(); this probably belongs in dungeon.c... */ void free_dungeons(void) From 4665b6c13c592c0511fd7312cd013903f7329ba0 Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 13 Apr 2026 06:47:10 -0400 Subject: [PATCH 436/442] follow-up staticfn define caused test to succeed when it shouldn't have --- include/extern.h | 1 + src/restore.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/include/extern.h b/include/extern.h index fe1b93b1b..fdaff14c4 100644 --- a/include/extern.h +++ b/include/extern.h @@ -2733,6 +2733,7 @@ void restore_gamelog(NHFILE *); boolean restgamestate(NHFILE *); void restore_msghistory(NHFILE *); #endif +extern void rest_adjust_levelflags(void); /* ### rip.c ### */ diff --git a/src/restore.c b/src/restore.c index 8f131d111..ac68c879e 100644 --- a/src/restore.c +++ b/src/restore.c @@ -35,7 +35,6 @@ staticfn boolean restgamestate(NHFILE *); staticfn void rest_bubbles(NHFILE *); staticfn void restore_gamelog(NHFILE *); staticfn void restore_msghistory(NHFILE *); -staticfn void rest_adjust_levelflags(void); #endif /* From c249de0c2ab72391e67aac300497e4b11e2e6eab Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 13 Apr 2026 07:11:47 -0400 Subject: [PATCH 437/442] timestamp change in save files requires EDITLEVEL bump --- include/patchlevel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/patchlevel.h b/include/patchlevel.h index 5ee218653..c2435f23a 100644 --- a/include/patchlevel.h +++ b/include/patchlevel.h @@ -17,7 +17,7 @@ * Incrementing EDITLEVEL can be used to force invalidation of old bones * and save files. */ -#define EDITLEVEL 137 +#define EDITLEVEL 138 /* * Development status possibilities. From 36c3f208a6a5058e979c87f3a315443a4309f314 Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 13 Apr 2026 07:32:54 -0400 Subject: [PATCH 438/442] again store relative timestamps in save and bones --- include/patchlevel.h | 2 +- src/restore.c | 2 ++ src/save.c | 3 +++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/include/patchlevel.h b/include/patchlevel.h index c2435f23a..070c25f4f 100644 --- a/include/patchlevel.h +++ b/include/patchlevel.h @@ -17,7 +17,7 @@ * Incrementing EDITLEVEL can be used to force invalidation of old bones * and save files. */ -#define EDITLEVEL 138 +#define EDITLEVEL 139 /* * Development status possibilities. diff --git a/src/restore.c b/src/restore.c index ac68c879e..7cb18b168 100644 --- a/src/restore.c +++ b/src/restore.c @@ -351,6 +351,8 @@ restmon(NHFILE *nhfp, struct monst *mtmp) if (buflen > 0) { newedog(mtmp); Sfi_edog(nhfp, EDOG(mtmp), "monst-edog"); + /* save or bones held a relative time */ + relative_time_to_moves(&EDOG(mtmp)->droptime); /* sanity check to prevent rn2(0) */ if (EDOG(mtmp)->apport <= 0) { EDOG(mtmp)->apport = 1; diff --git a/src/save.c b/src/save.c index e2c0ac5f9..3937746cd 100644 --- a/src/save.c +++ b/src/save.c @@ -853,7 +853,10 @@ savemon(NHFILE *nhfp, struct monst *mtmp) buflen = EDOG(mtmp) ? (int) sizeof (struct edog) : 0; Sfo_int(nhfp, &buflen, "monst-edog_length"); if (buflen > 0) { + /* we only store relative times in save and bones */ + moves_to_relative_time(&EDOG(mtmp)->droptime); Sfo_edog(nhfp, EDOG(mtmp), "monst-edog"); + relative_time_to_moves(&EDOG(mtmp)->droptime); } buflen = EBONES(mtmp) ? (int) sizeof (struct ebones) : 0; Sfo_int(nhfp, &buflen, "monst-ebones_length"); From 4bd3f8398ec1002a9dc0293a825dc3f3faea8563 Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 13 Apr 2026 07:35:23 -0400 Subject: [PATCH 439/442] whitespace cleanup: tabs to spaces --- src/restore.c | 4 ++-- src/save.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/restore.c b/src/restore.c index 7cb18b168..f2e8deba7 100644 --- a/src/restore.c +++ b/src/restore.c @@ -351,8 +351,8 @@ restmon(NHFILE *nhfp, struct monst *mtmp) if (buflen > 0) { newedog(mtmp); Sfi_edog(nhfp, EDOG(mtmp), "monst-edog"); - /* save or bones held a relative time */ - relative_time_to_moves(&EDOG(mtmp)->droptime); + /* save or bones held a relative time */ + relative_time_to_moves(&EDOG(mtmp)->droptime); /* sanity check to prevent rn2(0) */ if (EDOG(mtmp)->apport <= 0) { EDOG(mtmp)->apport = 1; diff --git a/src/save.c b/src/save.c index 3937746cd..c94fbed1b 100644 --- a/src/save.c +++ b/src/save.c @@ -853,10 +853,10 @@ savemon(NHFILE *nhfp, struct monst *mtmp) buflen = EDOG(mtmp) ? (int) sizeof (struct edog) : 0; Sfo_int(nhfp, &buflen, "monst-edog_length"); if (buflen > 0) { - /* we only store relative times in save and bones */ - moves_to_relative_time(&EDOG(mtmp)->droptime); + /* we only store relative times in save and bones */ + moves_to_relative_time(&EDOG(mtmp)->droptime); Sfo_edog(nhfp, EDOG(mtmp), "monst-edog"); - relative_time_to_moves(&EDOG(mtmp)->droptime); + relative_time_to_moves(&EDOG(mtmp)->droptime); } buflen = EBONES(mtmp) ? (int) sizeof (struct ebones) : 0; Sfo_int(nhfp, &buflen, "monst-ebones_length"); From efaabc3203f72edfa586b5ae941c80cc363f0e29 Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 13 Apr 2026 07:53:51 -0400 Subject: [PATCH 440/442] more storing only relative times in save & bones --- include/patchlevel.h | 2 +- src/restore.c | 3 +++ src/save.c | 7 +++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/include/patchlevel.h b/include/patchlevel.h index 070c25f4f..75e1d3311 100644 --- a/include/patchlevel.h +++ b/include/patchlevel.h @@ -17,7 +17,7 @@ * Incrementing EDITLEVEL can be used to force invalidation of old bones * and save files. */ -#define EDITLEVEL 139 +#define EDITLEVEL 140 /* * Development status possibilities. diff --git a/src/restore.c b/src/restore.c index f2e8deba7..2c277de58 100644 --- a/src/restore.c +++ b/src/restore.c @@ -353,6 +353,7 @@ restmon(NHFILE *nhfp, struct monst *mtmp) Sfi_edog(nhfp, EDOG(mtmp), "monst-edog"); /* save or bones held a relative time */ relative_time_to_moves(&EDOG(mtmp)->droptime); + relative_time_to_moves(&EDOG(mtmp)->hungrytime); /* sanity check to prevent rn2(0) */ if (EDOG(mtmp)->apport <= 0) { EDOG(mtmp)->apport = 1; @@ -552,6 +553,8 @@ restgamestate(NHFILE *nhfp) #endif /* SFCTOOL */ newgamecontext = svc.context; /* copy statically init'd context */ Sfi_context_info(nhfp, &svc.context, "gamestate-context"); + relative_time_to_moves(&svc.context.seer_turn); + relative_time_to_moves(&svc.context.digging.lastdigtime); svc.context.warntype.species = (ismnum(svc.context.warntype.speciesidx)) ? &mons[svc.context.warntype.speciesidx] : (struct permonst *) 0; diff --git a/src/save.c b/src/save.c index c94fbed1b..eaae62fc9 100644 --- a/src/save.c +++ b/src/save.c @@ -270,7 +270,12 @@ savegamestate(NHFILE *nhfp) program_state.saving++; /* caller should/did already set this... */ uid = (unsigned long) getuid(); Sfo_ulong(nhfp, &uid, "gamestate-uid"); + moves_to_relative_time(&svc.context.seer_turn); + moves_to_relative_time(&svc.context.digging.lastdigtime); Sfo_context_info(nhfp, &svc.context, "gamestate-context"); + relative_time_to_moves(&svc.context.seer_turn); + relative_time_to_moves(&svc.context.digging.lastdigtime); + Sfo_flag(nhfp, &flags, "gamestate-flags"); urealtime.finish_time = getnow(); urealtime.realtime += timet_delta(urealtime.finish_time, @@ -855,8 +860,10 @@ savemon(NHFILE *nhfp, struct monst *mtmp) if (buflen > 0) { /* we only store relative times in save and bones */ moves_to_relative_time(&EDOG(mtmp)->droptime); + moves_to_relative_time(&EDOG(mtmp)->hungrytime); Sfo_edog(nhfp, EDOG(mtmp), "monst-edog"); relative_time_to_moves(&EDOG(mtmp)->droptime); + relative_time_to_moves(&EDOG(mtmp)->hungrytime); } buflen = EBONES(mtmp) ? (int) sizeof (struct ebones) : 0; Sfo_int(nhfp, &buflen, "monst-ebones_length"); From 8c8121eb3c7bca165d7ac02b6fdc88cdaf61faa6 Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 13 Apr 2026 09:08:53 -0400 Subject: [PATCH 441/442] move a pair of functions from save.c to restore.c A utility that links only with restore.o needs to find them. --- include/extern.h | 4 ++-- src/cfgfiles.c | 2 ++ src/restore.c | 15 +++++++++++++++ src/save.c | 16 ---------------- util/sfctool.c | 2 +- 5 files changed, 20 insertions(+), 19 deletions(-) diff --git a/include/extern.h b/include/extern.h index fdaff14c4..66a5120b4 100644 --- a/include/extern.h +++ b/include/extern.h @@ -2734,6 +2734,8 @@ boolean restgamestate(NHFILE *); void restore_msghistory(NHFILE *); #endif extern void rest_adjust_levelflags(void); +extern void moves_to_relative_time(long *); +extern void relative_time_to_moves(long *); /* ### rip.c ### */ @@ -2828,8 +2830,6 @@ extern void freedynamicdata(void); extern void store_savefileinfo(NHFILE *) NONNULLARG1; extern void store_savefileinfo(NHFILE *) NONNULLARG1; extern int nhdatatypes_size(void); -extern void moves_to_relative_time(long *); -extern void relative_time_to_moves(long *); #if 0 extern void assignlog(char *, char*, int); extern FILE *getlog(NHFILE *); diff --git a/src/cfgfiles.c b/src/cfgfiles.c index 89ffbd4ab..2ba0c2fde 100644 --- a/src/cfgfiles.c +++ b/src/cfgfiles.c @@ -1889,6 +1889,7 @@ vconfig_error_add(const char *str, va_list the_args) config_erradd(buf); } +#ifndef SFCTOOL void rcfile(void) { @@ -2022,6 +2023,7 @@ config_unmatched_ignored(void) return TRUE; return FALSE; } +#endif /* SFCTOOL */ #ifdef SYSCF #ifdef SYSCF_FILE diff --git a/src/restore.c b/src/restore.c index 2c277de58..33066d662 100644 --- a/src/restore.c +++ b/src/restore.c @@ -1313,6 +1313,21 @@ rest_adjust_levelflags(void) /* adjust timestamps */ relative_time_to_moves(&svl.level.flags.stasis_until); } +void +moves_to_relative_time(long *timestamp) +{ + long prevts = *timestamp; + + *timestamp = prevts - svm.moves; +} + +void +relative_time_to_moves(long *timestamp) +{ + long prevts = *timestamp; + + *timestamp = svm.moves + prevts; +} /* "name-role-race-gend-algn" occurs very early in a save file; sometimes we want the whole thing, other times just "name" (for svp.plname[]) */ diff --git a/src/save.c b/src/save.c index eaae62fc9..fd6dfae2f 100644 --- a/src/save.c +++ b/src/save.c @@ -1052,22 +1052,6 @@ save_msghistory(NHFILE *nhfp) /* note: we don't attempt to handle release_data() here */ } -void -moves_to_relative_time(long *timestamp) -{ - long prevts = *timestamp; - - *timestamp = prevts - svm.moves; -} - -void -relative_time_to_moves(long *timestamp) -{ - long prevts = *timestamp; - - *timestamp = svm.moves + prevts; -} - /* also called by prscore(); this probably belongs in dungeon.c... */ void free_dungeons(void) diff --git a/util/sfctool.c b/util/sfctool.c index ab1ad4d97..9c9423861 100644 --- a/util/sfctool.c +++ b/util/sfctool.c @@ -96,7 +96,7 @@ int util_strncmpi(const char *s1, const char *s2, size_t sz); #ifdef UNIX #define nethack_exit exit ATTRNORETURN void nh_terminate(int) NORETURN; /* bwrite() calls this */ -static void chdirx(const char *, boolean); +//static void chdirx(const char *, boolean); #else ATTRNORETURN extern void nethack_exit(int) NORETURN; #ifdef WIN32 From 10a47c0713a21dbaeebac8d3a8dfba0805fcb2b8 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Mon, 13 Apr 2026 16:54:44 +0300 Subject: [PATCH 442/442] Remove unused curses define --- include/wincurs.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/wincurs.h b/include/wincurs.h index 4c5b7f19c..ad1a333fa 100644 --- a/include/wincurs.h +++ b/include/wincurs.h @@ -26,8 +26,6 @@ extern WINDOW *activemenu; /* curses window for menu requesting a #define SCROLLBAR_BACK_COLOR CLR_BLACK #define HIGHLIGHT_COLOR CLR_WHITE #define MORECOLOR CLR_ORANGE -#define STAT_UP_COLOR CLR_GREEN -#define STAT_DOWN_COLOR CLR_RED #define MESSAGE_WIN 1 #define STATUS_WIN 2 #define MAP_WIN 3