All the conduct feedback via "you ..." is directed at the character, so
seeing "you have rerolled your character 3 times" in conduct disclosure
looked strange. Change that to "your character was rerolled 3 times."
Switching symset in simple options menu did not reset the
map glyphs corectly. Just call the same routine for resetting
visuals as in the #optionsfull command.
Having it rolled into decl_globals_init() was
causing some early options to misbehave because
those early options were trying to use program_state
fields to determine their behavior, but decl_globals_init()
was resetting the program_state fields that were being used
to do that.
Issue reported by Shrigis1: hero at or close to full HP stepped on
a not-yet-discovered rolling boulder trap and was killed; end-of-game
disclosure reported "You could have avoided a one-shot death via
saving-grace."
Originally, saving grace was completely hidden, so the disclosure
line wouldn't have been included. Once disclosure was incorporated,
having saving grace not work for traps triggered by the hero seems
counter-intuitive, regardless of whether player is aware of the trap.
This makes saving grace apply to any damage applied by losehp(). The
original purpose of unexpectedly survivng by the skin of one's teeth
is still maintained.
Aside from having more of a chance of surviving via saving-grace,
the main difference is that some players might try for "survived by
saving-grace" as an unofficial conduct. As long as we don't start
tracking that as a conduct, I don't think this is a big deal.
Fixes#1499
../sys/windows/windmain.c:226:12: warning: address of array 'gc.chosen_windowtype' will always
evaluate to 'true' [-Wpointer-bool-conversion]
226 | if (gc.chosen_windowtype && *gc.chosen_windowtype)
../sys/windows/consoletty.c:873:27: warning: address of array 'back->utf8str' will always evaluate
to 'true' [-Wpointer-bool-conversion]
873 | if (back->utf8str && front->utf8str
| ~~~~~~^~~~~~~ ~~
../sys/windows/consoletty.c:873:45: warning: address of array 'front->utf8str' will always evaluate
to 'true' [-Wpointer-bool-conversion]
873 | if (back->utf8str && front->utf8str
| ~~ ~~~~~~~^~~~~~~
Issue reported by Tomsod nearly six months ago: missile damage against
hero was not honoring the Half_physical_damage attribute when hero had
that.
Fixes#1459
Change the classification of new weaponstatus, armorstatus, and
terrainstatus from 'OptS_Status' to 'OptS_Advanced' to keep the menu
for the 'O' command shorter. Doesn't affect 'mO'.
Including them in 'O' might make more players notice them though.
armorstatus, and terrainstatus
This adds three special status items to show at a glance what the hero
is wielding, wearing, and standing on.
Each of the three items has its own boolean option rather than try to
fix them in with the existing opttional status conditions. After a
lot of testing, I think the weapon and armor ones will prove useful
but the terrain one probably won't be.
Presently it is implemented for tty and curses. When I developed it
six years ago, it was also working for X11 but I'm not able to test
the resurrection of that part so have left it out.
This removes or updates several true rumors that have become
misleading or inaccurate due to changes to the mechanics they were
describing, and adds several new rumors describing mechanics that
are either new, or are intended to be a larger part of the game
than they were in previous versions.
More oracularities describing the new mechanics are needed, but I
haven't managed to get into the write frame of mind to write in the
Oracle's voice yet...
Any sort of iron footwear now protects from traps, and polymorph
traps change one sort of iron footwear into a different sort of
iron footwear (removing any incentive to try to farm them, because
you will see both options very quickly).
When playtesting NetHack heavily, I observed that most of the time
it wasn't placing much strain on my wrists, but Sokoban was an
exception: travel, farmove, etc. can normally be used to avoid the
need to spam keys, but they don't work while pushing a boulder, and
the boulders often need to be pushed along precise routes, so you
have to tap out every movement. This becomes particularly straining
when pushing in the last few boulders, as you have to push them a
long way along the goal corridor.
This commit adds rolling boulder traps to Sokoban that will
automatically roll boulders along the goal corridor, meaning that
you don't have to push them there manually. This considerably
reduces the number of keystrokes needed to solve Sokoban, without
making any significant change to the difficulty of the levels.
Some of the designs had to change slightly in order to make room
for them, but not in a way that meaningfully changes the solution.
Rolling boulder traps now react to having boulders pushed onto them;
the boulder will roll until reaching the trap's launch spot
(resetting the trap if the boulder was removed), or until it hits a
wall otherwise.
Because they can now be reset, they aren't removed when stepping on
them with a misplaced boulder (although no boulder moves unless
there's one on the appropriate spot).
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.
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.
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.
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 5e572d3d5f (post 3.6.7)
would execute and access the internals of the deleted map window.
../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
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.
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);
}
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);
}
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.
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.
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.
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.
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.
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.
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.
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.
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.
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...
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.
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;
| ^~~~~~~
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.
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.
- 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.
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.
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
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.
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.
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.
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.
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.
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.
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.
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.
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).
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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 <x,y>, 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.
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.
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.
Pull request from greg-kennedy: when a characteristic is repaired via
restore ability, set exercise and abuse for that characteristic to 0.
Closes#1403
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.
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.
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.
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.
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.
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.
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).
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.
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.
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.
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:<standard input>:34: error: cannot open 'tmac.nh ': No such file or directory
troff:<standard input>:35: error: cannot open 'doc/tmac.nh': No such file or directory
Fixes warning in category "syntax" from the forthcoming groff 1.24.
troff:<standard input>:790: warning: ignoring a space on input line after output line continuation escape sequence
[94 more occurrences]
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
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
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.
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.
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.
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.
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.
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.
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.)
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.
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
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.
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.
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.)
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.
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
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.
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.
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.
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.
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).
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.
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.
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.
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.
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.
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.
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 <aosdict@gmail.com>,
and allows the +1 or -1 luck adjustments of early game, such as full moon,
to have an effect.
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.
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.
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 593a93d254) 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
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
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
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 <https://nethack-yanis.github.io/yanis/4724.html>.
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.
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
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.
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.
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).
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.
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.
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.
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#1460Fixes#1461
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().
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.
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.
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.
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).
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.
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.
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.
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.
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.
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
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.
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);
| ^~~
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.
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).
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
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.
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.
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
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.
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.
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.
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).
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.
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.
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.
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.
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.
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
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
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
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
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/".
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.
> 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
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"
Issue reported by NullCGT: if the spot in front of a drawbridge held
water and got frozen, sanity checking for ice-melt timer would issue
complaints about melt timer for non-ice whenever the bridge was open.
Ice in front of closed bridge was handled ok, but ice beneath an open
bridge issued a spurious warning each turn if the sanity_check option
(wizard mode-only) was on.
Fixes#1440
Pull request from Feyorsh: in the sample run-time configuration
ile, doc/config.nh, the regular expression for "cursed" has bare
arentheses that should be quoted.
Fixes#1439
Part of the reasoning behind thrown objects not hitting mimics is
that a character who doesn't know there's a mimic there wouldn't
aim at it. But if you know there's a monster there (e.g. via
telepathy or monster detection), you would aim at it. Pushing a
boulder at it and hearing a monster works too (which is important
in cases where a mimic is trapped behind a boulder in the Sokoban
corridors).
Issue reported by vultur-cadens: 3.7's revised handling for initial
characteristic allocation included an unintended change from 3.6's.
I don't pretend to understand how characteristic allocation really
works. This should restore handling for values which are too low.
Fixes#1431
Issue reported by janne-hmp: examining an object on the map while
halluicinating might operate on an object whose name is Null since the
random object could be one that holds an extra description for item
shuffling at game start. Attempting to format the object led to a
crash.
I wasn't able to reproduce the crash, possibly because MacOS produces
the string "(null)" for sprintf("%s",NULL) instead of dereferencing
the Null pointer.
Perhaps random object selection for display should reject the extra
description objects in some classes. This susbstitutes a different
object if examining the map encounters one of those.
Fixes#1436
Data typo affecting '?' command's "longer explanation of game options".
Change the indication of menu_objsym's default value from [5] to [4]
since 4 is the actual default value in the code and the Guidebook.
Pull request from Umbire: starting gear for angelic beings should
use recently added silver mace instead of old ordinary mace.
It's simpler to just type in the change than to merge the commit.
Fixes#1433
Issue reported by Umbire: a gas spore that got swallowed and killed
didn't die but exploded anyway, with the explosion affecting the map
instead of being contained in the swallower.
There was code to handle that but it wasn't being executed. This fix
feels unclean but seems to work.
I couldn't reproduce the survival of the gas spore but since that
isn't wanted I won't worry about it.
Fixes#1434
Issue reported by by vultur-cadens: a vampire lord or lady might change
to wolf form while flying over water or lava, ending flight and dropping
into that water or lava. It would then drown or burn up, revert to
vampire leader form and resume flying, then be teleported since it was
past the check for being in flight.
The fix is pretty staightforward. It is still possible to force wolf
form with the monpolycontrol option, leaving the wolf standing on water
(didn't test for lava) and then drowning on its next move, where it will
revert to vampire form but no longer teleport away. There's no need for
a wizard mode hack to behave more stringently.
Fixes#1435
Don't record hallucinated monsters as having been seen up close or as
photographed.
Treat a tourist's starting pet has having been photographed prior to
bringing the camera and dog or cat into the dungeon.
No extra points to tourist when first long worm tail is photographed.
EDITLEVEL is incremented again, for extra context to track starting
pet.
More details in https://github.com/NetHack/NetHack/issues/1430
track photographed monsters using a distinct bit
also adds a pair of new context fields to track the total number of monsters seen up close,
and the total number of monsters photographed.
So, if somebody wants to add unique end-of-game disclosure statements for tourists that relate to
those, the groundwork should be there.
NOTE: This increments EDITLEVEL, so existing save and bones files will become outdated.
Fixes#1430
From "Monsters trapped in pits cannot kick" two weeks ago. Avoid
uhitm.c:5505:9: warning: unannotated fall-through between switch labels [-Wimplicit-fallthrough]
Recent clang wants C23's [[fallthrough]] attribute rather than just
the lint '/*FALLTHRU*/' comment.
OpenSUSE Tumbleweed ncurses 6.5 requires the one in
/usr/include/ncursesw/curses.h, if ncursesw is being used.
Otherwise, several needed function prototypes are not there.
Fixes#1427
Add some basic spellcasting stuff to the tutorial: read a spellbook,
cast a spell. If the hero doesn't have enough energy, just adds
a note saying so.
Remove/restore the known spells when entering/leaving the tutorial.
Seems like several new players did not find the "hidden" stairs.
Add iron bars to show a glimpse of them - the hero still needs
to find the secret door in to the room.
There are warnings within the qt6 header files if c++20 is not used, for example:
usr/include/x86_64-linux-gnu/qt6/QtCore/qfuturesynchronizer.h:21:5: warning: use of the 'nodiscard' attribute is a C++20 extension [-Wc++20-attribute-extensions]
21 | Q_NODISCARD_CTOR_X("Use future.waitForFinished() instead.")
| ^
/usr/include/x86_64-linux-gnu/qt6/QtCore/qcompilerdetection.h:972:43: note: expanded from macro 'Q_NODISCARD_CTOR_X'
972 | # define Q_NODISCARD_CTOR_X(message) [[nodiscard(message)]]
| ^
1 warning generated.
qmake6 --version
QMake version 3.1
Using Qt version 6.8.3 in /usr/lib/x86_64-linux-gnu
The blindness overhaul branch was created before towel weight got
changed, then unintentionally put the old weight back when it was
finally merged.
Increase weight to towel from 2 to 5 again.
Fixes#1416
Stop attacking if target isn't there anymore. Already handled for
two-weapon in normal form, not for multi-attacks in poly'd form and
for multi-attack monster vs hero or monster vs other monster.
I didn't attempt to reproduce the reported problem. This fix is
based on code inspection.
Also prevent monsters that have hug or engulf attacks from knocking
target back with other attacks since that prohibits the grab/engulf
from being able to hit.
In the context of sanity checking, an extra pass though the inventory
of every monster wielding a weapon is completely negligible, but it
is trivial to avoid so take it out.
-> ##[warning]The windows-2019 runner image is being deprecated, consider switching to windows-2022(windows-latest) or windows-2025 instead. For more details see https://github.com/actions/runner-images/issues/12045.
GCC15 switched its default to -std=gnu23 and there's a bug in
pdcursesmod as a result. That impacts MSYS2/Mingw64 NetHack builds.
See: https://github.com/Bill-Gray/PDCursesMod/issues/333
The suggestion there is to force --std=gnu17 as a workaround.
otmp can be 0 in mk_artifact. In fact, it is explicitly
being set to 0 three lines above the recently added call
to permapoisoned().
The static analyzer was griping also.
Have save_mtraits() clear wielded weapon when attaching monster
attributes to a corpse object.
And have monster sanity check verify that wielded weapon is in the
monster's inventory.
I saw a mimic disguised as an octagonal amulet and wished for an
amulet of that shape to see what it was trying to tempt me with.
I got a random amulet instead of one with the requested description.
That was happening for any valid shape (it's expected behavior for
invalid descriptions, where only "amulet" matches).
Issue reported by ars3niy: if a mimic was given the shape of a
gold piece it gets reported as 2 gold pieces but the message was
|A gold pieces appears next to you.
Avoid article "A" prefix, and use plural verb "appear" instead of
singular "appears", yielding
|Gold pieces appear next to you.
Fixes#1413
This started out as an indentation fix but ended up tweaking a couple
of comments. The other value adjustments all use 'n += X' rather than
directly modify 'tmp', so this changed more than just the indentation.
../win/X11/winX.c: In function ‘init_standard_windows’:
../win/X11/winX.c:2769:46: warning: passing argument 2 of ‘XtAppSetErrorHandler’ makes ‘__attribute__((noreturn))’ qualified function pointer from unqualified [-Wdiscarded-qualifiers]
2769 | (void) XtAppSetErrorHandler(app_context, X11_error_handler);
| ^~~~~~~~~~~~~~~~~
In file included from ../win/X11/winX.c:27:
/usr/include/X11/Intrinsic.h:1771:5: note: expected ‘__attribute__((noreturn)) void (*)(char *)’ but argument is of type ‘void (*)(char *)’
1771 | XtErrorHandler /* handler */ _X_NORETURN
| ^
Part 4 of implementing wish spreading. (This is now a complete
implementation, although the details are likely to change - but it
makes sense to commit something with the right balance properties,
and then tweak it based on feedback from playtesting.)
This helps to make the Amulet of Yendor feel special, and restores
approximately the same average number of wishes per game as existed
prior to the nerf to wands of wishing.
Placing the wish in allmain helps to avoid the wish happening at an
awkward place in the game's control flow, and is simpler than
testing every possible mechanism for gaining items for bugs (message
order is a common issue when trying to place it in addinv-related
functions, and this also avoids issues with the wished-for item
immediately invalidating an assumption that was made by the calling
code).
It is possible that this would be better as an invoke effect,
although I like the impact of picking up the Amulet and immediately
being given a wish.
This is partly for balance reasons (so that clearing the Castle
helps towards the level 14 Quest unlock) and partly as a clue to
players spoiled on previous versions that the Castle wand now works
differently.
Part 3 of implementing wish spreading. These items are each worth
most of a wish for non-illiterate games (most games wish for a
magic marker, and a magic lamp gives a wish 80% of the time).
The placement of the random item could be better (currently it is
purely random, which is occasionally interesting but often boring),
but this will serve as a base for experimenting with the balance
properties of the moved wishes.
Part 1 of implementing wish spreading. Vlad's throne is now
guaranteed to eventually give a wish, but has a range of other
powerful (and mostly bad) effects, like removing intrinsics, that
can be much harder to deal with than typical throne effects.
This causes the difficulties to get out of sequence, but we agreed
that this is a less important rule than keeping monster IDs stable.
Breaks save and bones files, because it changes monster IDs.
This is both a bugfix (the hero would be unlikely to aim their
throw at what appeared to be an object) and a balance fix (it was
possible to, somewhat tediously, defend yourself from mimics by
throwing gold pieces at them, and for many players this became
standard strategy in shops, negating the threat from mimics).
These two types of monster were extreme outliers in terms of where
they appeared versus how lethal they were (3-4 times as deadly as
other monsters that appeared at similar depth, based on statistics
from actual play), so their generation depth has been manually
modified.
Breaks save and bones files (because monster type IDs have to be
sorted numerically by difficulty, and changing difficulties thus
changes the IDs, but the IDs are used to identify the monsters in
save files).
These were unbalancing the game a) in the Castle and b) if they
woke up unique monsters (most notably the Wizard of Yendor).
I considered adding a difficulty check, but this commit instead
just directly fixes the symptoms. (It doesn't make sense for the
Castle to contain a monster that would kill or be killed by its
inhabitants: they should have died long before the hero arrived.
So for liches/zombies to exist in the Castle at all, there must be
a truce.)
The visual studio compiler behaves diffently with _Generic than with
gcc on Linux _Generic around long and ulong.
On Windows they aren't recognized as one of the stdint types.
On Linux gcc, it considers them equivalent to int64_t and uint64_t.
Leave it out of the _Generic to avoid the behaviour difference between
platforms/compilers.
This is the third of a series of savefile-related changes.
This adds early-days experimental support for a completely optional
'sfctool' utility (savefile conversion tool), to be able to export
a savefile's contents into a more portable format. There are likely
to be bugs at this stage. In this initial first-attempt, the export
format is a very simple ascii output.
NetHack can be built entirely, without also building this tool.
NetHack has no dependencies on the tool.
Attempts were made to minimize duplication of existing NetHack code.
To achieve that, unfortunately, #ifdef SFCTOOL and #ifndef SFCTOOL
had to be sprinkled around through some of the existing NetHack
source code, so that it could be re-used for building the utility.
The process for building the sfctool typically recompiles the source
files with #define SFCTOOL and a distinct object file with SF- is
produced.
sfctool notes:
Universal ctags is used and required to produce the sfctool utility.
Some targets were added to the Unix and Windows Makefiles to
facilitate the build process.
make sfctool
That should build a copy in util.
Note: At present, the Unix Makefiles do not copy sfctool over to the
NetHack playground during 'make install' or 'make update'.
Until that gets resolved by someone, The tool will
have to be manually copied there by the builder/admin if
desired.
cp util/sfctool ~/nh/install/games/lib/nethackdir/sfctool
Also, a separate Visual Studio sfctool.sln solution was written and
placed in sys/windows/vs. That has has only very limited testing.
Usage:
i) To convert an existing savefile to an exportascii format
that co-resides with the savefile:
sfctool -c savefile
That *must* be executed on the same platform / architecture /
data model that produced the save file in the first place.
ii) To unconvert an existing exportascii format export file to a
historical format savefile that can then be used by NetHack:
sfctool -u savefile
That must be executed on the same target platform / architecture /
data model that was used to build the NetHack that will
utilize the save file that results.
A Windows example:
sfctool -c Fred.NetHack-saved-game
That should result in creation of Fred.NetHack-saved-game.exportascii
from existing savefile:
%USERPROFILE%\AppData\Local\NetHack\3.7\Fred.NetHack-saved-game
A Unix example:
sfctool -c 1000wizard
That should result in creation of 1000wizard.exportascii.gz
from existing savefile in the playground save directory:
1000wizard.gz
Current Mechanics:
1. Makefile recipe, or script uses universal ctags to produce
util/sf.tags.
2. util/sftags is built and executed to read util/sf.tags and
generate: include/sfproto.h and src/sfdata.c.
3. util/sfctool is built from the following:
generated file compiled with -DSFCTOOL:
src/sfdata.c -> sfdata.o
existing files compiled with -DSFCTOOL:
util/sfctool.c -> sfctool.o
util/sfexpasc.c -> sfexpasc.o
src/alloc.c -> sf-alloc.o
src/monst.c -> sf-monst.o
src/objects.c -> sf-objects.o
src/sfbase.c -> sfbase.o
src/sfstruct.c -> sfstruct.o
src/nhlua.c -> sf-nhlua.o
util/panic.c -> panic.o
src/date.c -> sf-date.o
src/decl.c -> sf-decl.o
src/artifact.c -> sf-artifact.o
src/dungeon.c -> sf-dungeon.o
src/end.c -> sf-end.o
src/engrave.c -> sf-engrave.o
src/cfgfiles.c -> sf-cfgfiles.o
src/files.c -> sf-files.o
src/light.c -> sf-light.o
src/mdlib.c -> sf-mdlib.o
src/mkmaze.c -> sf-mkmaze.o
src/mkroom.c -> sf-mkroom.o
src/o_init.c -> sf-o_init.o
src/region.c -> sf-region.o
src/restore.c -> sf-restore.o
src/rumors.c -> sf-rumors.o
src/sys.c -> sf-sys.o
src/timeout.c -> sf-timeout.o
src/track.c -> sf-track.o
src/version.c -> sf-version.o
src/worm.c -> sf-worm.o
src/strutil.c -> strutil.o
This is the second of a series of changes related to save/restore.
No EDITLEVEL bump has been included, because although the code
is changed extensively by this, the content of the savefiles have
not been changed.
Push the use of the structlevel bwrite() and mread() function use
out of the core and into sfstruct.c. This is groundwork for upcoming
changes.
In the core, replace the bwrite() and mread() calls with the
use of type-specific savefile output (Sfo) and savefile
input (Sfi) macros. The macros are defined in a new header file
savefile.h, which also contains the prototypes for the sfo_* and
sfi_* functions that the macros ultimately expand to. The functions
themselves are in src/sfbase.c.
On C99, each Sfo or Sfi macro expansion refers directly to the
corresponding type-specific sfo_* or sfi_* function.
If C23 or later is is use, the majority (all but 3 types) of the
macros refer to a single _Generic output routine sfo(nhfp, dt, tag),
and a single _Generic input routine sfi(nhfp, dt, tag), which handles
the dispatch of the type-specific underlying functions. This was
somewhat experimental, but turned out to be practical because the
compiler would gripe if the type for a variable was not included in
the _Generic when passed as an argument, so it could be fixed.
This alters the savefile verication process by having a common set
return values for the related functions such as uptodate(),
check_version(), etc. The new return values return more information
about savefile incompatibilities, beyond failure/sucess. The
additional information will be useful for an upcoming addition.
The expanded return values are:
SF_UPTODATE (0) everything matched and looks good
SF_OUTDATED (1) savefile is outdated
SF_CRITICAL_BYTE_COUNT_MISMATCH (2) critical size count mismatch
SF_DM_IL32LLP64_ON_ILP32LL64 (3) Windows x64 savefile on x86
SF_DM_I32LP64_ON_ILP32LL64 (4) Unix 64 savefile on x86
SF_DM_ILP32LL64_ON_I32LP64 (5) x86 savefile on Unix 64
SF_DM_ILP32LL64_ON_IL32LLP64 (6) x86 savefile on Windows x64
SF_DM_I32LP64_ON_IL32LLP64 (7) Unix 64 savefile on Windows x64
SF_DM_IL32LLP64_ON_I32LP64 (8) Windows x64 savefile on Unix 64
SF_DM_MISMATCH (9) some other mismatch
The callers in the core have been adjusted to deal with the expanded
return values.
Other miscellaneous inclusions:
- go.oracle_loc -> svo.oracle_loc.
- add a bit (1UL << 30) to called SFCTOOL_BIT as groundwork
for changes to follow.
invent.c
.\invent.c(3668): warning C5287: operands are different enum types 'inv_modes' and 'inv_mode_bits'; use an explicit cast to silence this warning
.\invent.c(3669): warning C5287: operands are different enum types 'inv_modes' and 'inv_mode_bits'; use an explicit cast to silence this warning
Noticed a strange oversight in knockback: Hero hitting a monster
did not cause knockback, unless polymorphed into a monster.
Add knockback chance if we're using a weapon, not twoweaponing,
and dealing some damage.
Weapons that can do knockback are lucern hammer, bec de corbin,
dwarvish mattock, (silver) mace, morning star, war hammer,
club, quarterstaff, aklys, flail, pick-axe, and grappling hook.
+15 wasn't very impactful in the late game and late mid-game, but
was much too lethal in the early game (wearing the ring for a while
near the start of the game would make the game unwinnable as very
out-of-depth monsters spawned, and they would still be there even
after removing the ring and usually capable of one-shotting an
early-game character). This commit changes it to a doubling of
level difficulty rather than a flat increase: that makes it more
relevant in the late-game where a +25 or +50 might potentially have
an impact, and more survivable in the early game (although it still
spawns monsters that are difficult for the point in the game, there
is now a chance that you might survive long enough to be able to
take the ring off and clear off all the out-of-depth monsters that
spawned).
The Guidebook states that the default values for 'role', 'race',
'gender', and 'alignment' are "random" but that's wrong. Omitting
those options results in interactive prompting.
The two options are very similar but probably mutually exclusive
except when using look-here and look-into-container (both via ':')
with the default setting for 'sortloot', or with inventory when
'sortpack' has been toggled off.
This removes 'use_menu_glyphs' and changes 'menu_objsyms' from a
boolean to a compound taking six possible values:
| 0: no object symbols in menus,
| 1: append object class symbol to object header lines (same as old
|menu_objsyms boolean),
| 2: include object symbol in menu entry lines for objects (same as
|recently added use_menu_glyphs),
| 3: both 1 and 2,
| 4: display as #2 but only if the menu lacks class header lines,
| 5: if header lines are present, display as #1; if headers are not
|present, then display as #4 (which will implicitly be #2).
Default is #4.
Effectively replaces the options portion of pull request #1406 and
retains the functionality, but not as default for normal menus.
Guidebook.tex is only partially updated. Someone else will need to
finish that.
Pull request from NullCGT: add 'use_menu_glyphs' option to be able
show the object class symbol in menus of objects.
tty:
|a ? scroll of identify (instead of 'a - scroll of identify'),
curses:
| a) ? scroll of identify (instead of ' a) scroll of identify').
This commit fixes a bit of formatting in wintty.c.
Closes#1406
Try to exercise ^A more when running the fuzzer. Also ^P, although
that is tty-centric.
I couldn't notice any difference in behavior so this doesn't seem to
be very useful.
End of line comments split across lines should start with '*' on the
continuation lines. Otherwise clang-format, if we ever run it again,
will mangle them by shifting the start of the comment from the end
of its line to be a new block comment on the next line.
[There are lots of these which ought to be fixed; I just happended to
be looking at monst.h.]
If there is at least one genocided or extinct type of monster, final
disclosure asks if you want to see the list. It was using "ynaq"
for the choice of answer, where 'a' is used to prompt for sort order
rather than "all". Change it to only include the 'a' choice if there
are more than 1 of either category or 1 each of both categories (since
they're listed interspersed with each other, sorting is relevant for
the one-of-each situation).
It seems surpristing that no one has noticed this since the code that
is responsible has been present for six months. Inventory list at
end of game included bogus "? - (list likely candidates)".
heaputil reported an attempt to free a null pointer at line 1314 of
cursdial.c (in menu_display_page()).
curses_break_str() can return Null but its callers aren't prepared
for that. Make it return an empty string that can be passed to
free() instead.
Iterating over a large set of locations in a selection caused a memory leak.
Lua couldn't do garbage collection in the middle of the iterator function,
so it eventually ran out of space, and just quietly dropped stuff.
A comment in rm.h claimed that secret doors can't be trapped so I
used door flag D_TRAPPED to handle secret doors that should be shown
as trees instead of walls. But the comment was inaccurate and secret
doors can be trapped.
Such trapped secret doors in ordinary rooms ended up being shown as
trees too. Switch from using D_ARBOREAL in levl[][].doormask to new
levl[][].arboreal_sdoor which overloads levl[][].candig.
Also, wizard mode wishing for secret doors needed updating to allow
creating trapped ones (at wall or door locations).
This ought to update EDITLEVEL but I think existing save files can
live with secret door display issues. Untrapped secret doors in
garden-fill rooms will end up becoming trapped.
Replaces the fix for github issue #1309
Commit 050846ada9 checked for
re_alloc(NULL,n) and returned alloc(n) for that case. After testing
MONITOR_HEAP and heaputil, the original code worked as intended.
I'm not sure what was going wrong yesterday.
Switch back to the previous code. I could have used 'git revert'
but haven't.
I hope this is temporary. nhrealloc() intends to deal with
realloc(NULL, size) but something isn't working correctly. The
code in alloc.c looks right so the problem might be in heaputil.
However, the code there looks ok too.
Not sure why my earlier attempt was unsuccessful. This one isn't as
comprehensive but is simpler and better yet, works as intended.
When saving a level or exiting the program, objects can be deleted
directly rather than having to pass though the objs_deleted list.
If the yn_function() delivers its impossible about returning a result
that isn't considered to be viable, put the prompt into paniclog.
The updated comment contains my guess about what it going wrong, and
I'm fairly sure it is correct. But I don't know how to fix it unless
we change ^A to just repeat the last command without attempting to
also repeat whatever followed.
At the moment, users will occasionally get strange outcome from ^A.
'unsigned long' isn't big enough to hold a pointer in my configuration,
and the old "only micros are sure to support %p format" is long out of
date. Just assume that everyone has %p these days, and provide a hook
to avoid it for anyone who doesn't. (Opt-out instead of opt-in.)
'heaputil' is producing a lot of complaints. This fixes one of them,
about freeing memory that was never allocated. In this case, it's
when removing an overview annotation for a level. The annotation
is using dupstr_n() and not being recorded due to dupstr_n() being
placed after MONITOR_HEAP undefines the macro that overrides alloc().
There's only one use of dupstr_n(), and its length checking isn't
needed there, so just switch to dupstr() and comment out the
implementation of dupstr_n(). I left the prototype in extern.h;
that's harmless.
If dupstr_n() needs to be resurrected, a second MONITOR_HEAP-aware
version should be implemented, with corresponding macro to choose
which one to use.
Warning about missing parantheses when mixing '+' and '?:'. It didn't
cause 'make' to quit but resulted in incorrect score-in-progress values
eing generated.
Provide a way to bypass a debugger when initiating fuzzing.
nethack -D --debug:fuzzer # run fuzzer in wizard mode
nethack --debug:fuzzer # run it in normal mode
nethack [-D] -@ --debug:fuzzer # skip role/race/&c selection
This is the first of several savefile-related changes to
follow later. This one is groundwork for those later changes.
Remove internal compression schemes (RLECOMP and ZEROCOMP)
and discard the savefile_info struct that was primarily used to
convey which internal compression schemes had been in use.
Relocate some struct definitions into appropriate header files
for use by code to come in later changes.
Remove the two struct size-related fields from version_info and
from the nmakedefs_s. Instead, include a series of bytes near the
beginning of the savefile, representing the size of each
struct or base data type that impacts the historical savefile
content. Those are referred to as the "critical bytes".
(Related note: the "you" struct required two bytes, low and high,
due to its size).
Compare those critical bytes in a savefile against the NetHack
build that is reading the savefile. This allows mismatch detection
early in the savefile-reading process, and a clean exit, rather than
proceeding to read nonsensical values from the file. Include some
feedback on what the first mismatch was when encountering
one.
For arrays stored in the savefile, use loop-logic in the core
to write/read the array elements one at a time, rather than in
a single blob. This will be required for changes to follow later.
(impacts artiexist[], artidisco[], svd.dungeons[], svl.level_info[],
svl.level.locations[][], msrooms[] field of mapseen, svb.bases[],
svb.disco[] objects[], svm.mvitals[], svs.spl_book[], svd.doors[],
go.oracle_loc[], utrack[], wgrowtime[])
This also adds data model to the long version information.
This invalidates existing save and bones files due to the changes in
the information at the start of the file.
When I reworked amnesia to not forget levels or objects, I removed
the forgetting from the mind flayer attacks. I intended to add
something to replace it, but forgot ...
Issue reported by elunna: when a room gets converted into a theme
room with fill type Garden, its walls are changed to trees but any
secret doors in those walls are still displayed as regular walls.
This adds a new D_ARBOREAL flag for secret doors, used to force them
to be displayed as a tree instead of a wall.
Fixes#1309
Grimtooth is now permanently poisoned, protects the wielder from
poison, and can be invoked to throw poison.
Permapoison code comes from xNetHack by copperwater <aosdict@gmail.com>.
Pull request from copperwater: reorganize the theme rooms data so
that a room or a fill can be chosen by name, and when in wizard mode,
consult environment variables THEMERM and THEMERMFILL during level
creation to provide control over which theme rooms/room fills to
generate.
I reverted a commit that did a bunch of reformatting to themerms.lua
because to caused substantial merge conflicts. I will redo at least
part of it.
Closes#1384
The themed room code previously assumed that on any given level, at
least one room or fill would resolve as OK to generate there. However,
that's not a great assumption to make, and if it happened to be broken,
the first themed room or fill would arbitrarily be executed, even though
it wasn't eligible. Fix that by setting the initial pick to nil, and
raising an impossible if it's still nil after trying to choose a random
room.
A common pain point I encounter when working on themed rooms is making
specific rooms generate. The only ways to do this were mass commenting
out the rooms not being tested, or hacking in different room frequency
values (even more annoying when testing a fill, not a room, or testing
pure-function rooms/fills that have no frequency).
This change solves that problem by allowing a wizard-mode user to define
THEMERM or THEMERMFILL environment variables to make specific rooms or
fills generate.
The first part of this change is converting all themed rooms and fills
that were plain functions into tables, and converting their comment
names into actual names in those tables. The names are not intended to
be shown during gameplay, but instead serve as values that THEMERM or
THEMERMFILL can be matched to to generate those rooms. It's no longer
possible to have a function themeroom; this will raise an impossible.
As far as I'm concerned, this is a good change because it allows some
code simplification of themerooms_generate and makes it easier to add
difficulty or eligibility parameters to rooms.
The second part of this change is adding a new nh.debug_themerm function
to make the environment variable values accessible to themerms.lua. I
looked for an existing way to do this but didn't see one (nh.variable
is the closest but appears to be for variables that get saved).
The final part is inserting behavior into the actual themeroom
generation code that changes how they generate when either a room or a
fill is set. I don't think it's safe to generate every single room with
the requested type or fill - that might lead to cases where the stairs
or a magic portal cannot generate. So it creates ordinary rooms half of
the time, which still results in plenty of themed rooms on levels.
Another thing to note is that any themed room using filler_region will
still only pick a fill 30% of the time. If one specifies both a fill and
a room that uses filler_region, many of those rooms will appear without
a themed fill.
Added the silver mace to be the base weapon type of Demonbane. It is appropriate that an artifact weapon designed to slay Demons would be made of (or plated with) silver. This helps to offset the damage reduction when Demonbane was changed from a longsword to a mace and makes it more specialized against silver-haters.
Set the probability to 2, equal to that of a silver spear.
Increased the weight of the silver mace by 120% -- equal to the weight increase from a normal spear to a silver spear. (Assuming the weapon is silver plated rather than made entirely out of silver.)
Increased the base cost to 60, a similar increase as spear to silver spear, to be an even number between silver spear and silver saber.
Monsters will prefer silver maces over regular maces.
Otherwise, identical in function to a normal mace.
If user has changed the stone glyph to something other than a space
(or uses a tileset), Sokoban levels showed the unreachable stone outside
the map area. Prevent marking those areas as seen, so the stone
glyphs aren't shown.
Author: PatR <rankin@nethack.org>
Date: Mon Apr 7 13:58:28 2025 -0700
fix issue #1404 - re-tamed feral pet starves
Issue reported by k21971: changes in 'struct edog' initialization
resulted in re-taming of a feral former pet producing a tame monst
that immediately dies of starvation.
I didn't look at the earlier behavior, just forced hunger to be
initialized separately from other edog fields.
Fixes#1404
Fix up a few comments in the monster throwing code. And change a
couple 'if (!Blind)' checks to use 'if (canseemon(magr))' instead
of that the player won't be told about a returning aklys hitting an
invisible monster ("it") on the arm.
Issue reported by k21971: a gnome throwing a wielded aklys at the
hero was killed when failing to catch its return. Bookkeeping for
dead monsters got messed up, then a crash occurred.
This fixes things. Instead of a comment stating that the thrower
might be dead, kill it off.
This has been sitting around for a long time. It prevents at least
one fuzzer exit.
Before adding this, I did trigger one yn_function() 'impossible'
while doing ordinary testing but wasn't able to reproduce that, so
am still not sure what is going on.
I hope this doesn't break anything. There seemed to be one or two
misplaced 'end' statements, but after some massaging I'm not sure
about this anymore.
There were lots of wide lines; those are easy. The Water-surrounded
vault had very inconsistent indentation so was harder to untangle.
This was discovered when a game of xNetHack crashed with stack smashing
detected during dumplog creation after an ascension. I traced the
problem to a wish with a very long string the player had made much
earlier in the game ("greased very blessed holy rustproof unlit historic
thoroughly +5 very cloak of protection named it would be a shame if
something happened to me wearing this cloak"), which is further recorded
in an even longer form in the chronicle as 'wished for "X", got "Y"'.
That string does get truncated, but since the gamelog strings are
dynamically allocated, they can be longer than BUFSZ.
When show_gamelog was subsequently called, it didn't use any bounds
checking, which allowed its stack-allocated buffer to overflow. Changing
the offending sprintf to snprintf and limiting it to the buffer size
appears to fix this issue. It will truncate the string at BUFSZ-1
characters and therefore will be expressed in the dumplog as an
incomplete string, but 1) that was happening anyway because the gamelog
string already doesn't capture the entire "wished for X, got Y" message
on such a wish, and 2) this should only ever happen for very long
wishes.
Most things that can be dug or chopped can only have that done by one
of the two types of digging/chopping tools: pick-axe or axe. Since
closed door can be broken open via either type, mention the type of
implement in the final "you break through the door" message by adding
"with your <uwep>."
After updating the --dumpweights code in hack.c to insert "pair of"
for gloves and boots and "set of" for dragon scales, I've switched
it to use simple_typename() instead.
Turns out that that routine also lacked handling for 'pair|set of'.
And it was generating "coin of gold piece". Fix those.
Roughly half of the gems are "<gem>" and the others "<gem> stone",
so the --dumpweights output is different by more than just pair/set.
Experience-level and experience-points, if enabled, could be
highlighted via 'up' or 'changed' rules in initial display after
restore. I tried 'down' rule too but didn't produce with that.
I don't understand what was going on but was able to reproduce it
and then fix it via the trial and error method....
Those places that use get_table_str_opt() to get an optional string
value can instead use a function that returns a string.
This can be used for example in quest data lua table, or some table
fields in the lua api bindings, or the dungeon definition.
For example, in quest.lua
text = "You again sense %l pleading for help.",
could be replaced with
text = function() return "You again sense %l pleading for help."; end,
which of course allows using lua to build the string.
There were couple reports of doors being generated in a corner
of a room. This happened for randomly generated irregular rooms,
because the code that was deciding if a corridor starting or
ending location was good did not handle irregular rooms at all.
This changes the corridor code so it can now generate start and
end points properly for any valid position in irregular rooms,
instead of only on the edges. This means the corridor sometimes
meanders a bit more than before, because it tries to find
the end point away from the edge of the room rectangle.
Also added is sanity checking for the randomly generated rooms
and corridors level, testing for door placement and room connectivity.
And another fix for a rare special case where dig_corridor
created a zero-tile long corridor; the entrance door was placed,
but there was nothing behind it.
Fixes github #1269 and #1385
Fix warnings
objnam.c: In function ‘wizterrainwish’:
objnam.c:3536:54: warning: variable ‘didblock’ set but not used [-Wunused-but-set-variable]
3536 | boolean madeterrain = FALSE, badterrain = FALSE, didblock, is_dbridge;
| ^~~~~~~~
Disarming a chest trap was setting obj->tknown = 0 even though the
hero just discovered that it isn't trapped.
Triggering a chest trap behaved similarly. Since there are no
repeating chest traps, hero should know that the chest whose trap
just went off is no longer trapped.
chest_trap() didn't document its return value but was clearly meant
to return True if the chest was destroyed. It didn't handle that
correctly when the chest was being carried. However, none of the
callers actually use the return value. [This fix tracks whether the
chest gets deleted; a better fix would be to destroy an exploding
chest even when it is being carried.]
If 'autounlock' is set to test a chest for traps, skip "check for
traps?" when tknown is set; go directly to "disarm trap?" if the
chest is trapped, skip that too if it isn't.
If wand of probing hits a chest, set the tknown bit.
We've had reports of a couple of issues building against musl libc.
Issues reported:
- build procedures utilize col for Guidebook-creation, and col
is deprecated in distros that use musl libc
- some of the CRASHREPORT code is using library functions that
are not available in the musl libc environment. The reported
functions were backtrace() and backtrace_symbols(), which use
header file /usr/include/execinfo.h.
So we'll try to accommodate this. Since we don't have a means of
autodetecting the musl libc situation during the build (as of yet), the
builder will have to specify 'make musl=1' on the make command line.
Specifying 'musl=1' on the make command line will:
1. ensure that NOCRASHREPORT gets defined in the C preprocessor.
2. set COLCMD to be '../util/stripbs' instead of 'col -bx'.
Related to GitHub #1393
We've had reports of a couple of issues building against musl libc.
Issues reported:
- build procedures utilize cat for Guidebook-creation, and cat
is deprecated in distros that use musl libc.
- some of the CRASHREPORT code is using library functions that
are not available in the musl libc environment. The reported
functions were backtrace() and backtrace_symbols(), which use
header file /usr/include/execinfo.h.
So we'll try to accommodate this. Since we don't have a means of
autodetecting the musl libc situation during the build (as of yet), the
builder will have to specify 'make musl=1' on the make command line.
Specifying 'musl=1' on the make command line will:
1. ensure that NOCRASHREPORT gets defined in the C preprocessor.
2. set COLCMD to be '../util/stripbs' instead of 'col -bx'.
Closes#1393
Issue reported by elunna: Using the 'F' prefix against a displacer
beast prevented swapping places.
This doesn't use the suggested fix. It is quite short but there is
a large diff due to change in indentation and reformatting several
comments because of that.
Attacking a displacer beast either with or without 'F' might miss,
hit, or swap places. It won't "harmlessly attack thin air."
Fixes#1377
Protect carried items as well as hero when carrying the Mitre of
Holiness. Already handled when wearing that artifact.
This might make it be too strong. At the time that it was given
the carrued attribute, there was no such thing as carried items
providing any protection to other carried items.
"You have disabled loading of bones levels" (during play) and
"You disabled loading of bones levels" (end of game disclosure)
both clearly refer to the player rather than the hero.
"You have never encountered a bones level" is accurate for current
hero but not necessarily accurate for the player. Rephrase it.
Also, if OPTIONS=!bones is set and the hero just died, extend
"You disabled loading of bones levels" during disclosure to
"You disabled loading and storing of bones levels" (even in the
case where bones wouldn't be saved anyway).
Initially diagnosed in an xnethack fuzzer crash - unblock_point
shouldn't be called when a closed door becomes non-closed, because it's
possible that there's a gas cloud on the space which means it still
blocks vision. These always need to be recalc_block_point. A number of
them were fixed, but when I went through all the xnethack ones, I found
some that were unchanged from upstream NetHack. I reproduced the sanity
check impossibles usually by breathing gas at a door as an iron golem
and then opening or destroying the door to trigger the unblock_point
call.
The use of recalc_block_point in wizterrainwish was not triggering this
bug, but the previous code there basically duplicated
recalc_block_point.
It wasn't clear to me how selection.room() handles room edges and
unusual terrain in the room, so I looked at the code and wrote down how
it behaves for posterity.
I don't believe the one room currently capable of getting a random fill
while already containing some odd terrain, "Blocked center", actually
has unusual terrain in the room fill. This is because filler_region
creates an irregular region (i.e. a room containing the ROOM points in a
square ring around the blocked center). The points in the middle don't
share the same roomno, so they won't be returned in the selection
created by selection.room(). But there's no reason a room couldn't be
added in the future which does specify some nonstandard terrain and then
a themeroom fill.
Not sure how long this has existed without triggering any issues, but
when I was testing out a themed room wider than it was tall, I ran into
rn2-of-a-negative-number impossibles. Traced it to here, where it was
trying to subtract the width of the mapfrag from ROWNO to figure out
which y-value it should place the map on. The correct behavior is to
subtract the height of the mapfrag.
It is possible to create a bidirectional teleportation trap by making a
pair of teleportation traps with a fixed destination of each other's
coordinate. Moving or hurtling onto such a trap correctly materializes
the hero on top of the other trap without triggering it, but for some
reason I didn't dig into, sitting down to trigger the first trap does
also trigger the second one at the destination end, causing you to
counterintuitively teleport twice and end up back where you started.
Fix this by stopping tele_trap() from doing anything if it's called
recursively, using a static variable like spoteffects() does for the same
purpose. I had to adjust a bit of other tele_trap code to remove its
sole early return.
If a player initially goes petless then later obtains a pet, it's absent
from the game chronicle. Fix that by adding a livelog for it.
This required a bit of restructuring in create_familiar() that I wanted
to do anyway: removing the kludge of decrementing u.uconduct.pets when a
figurine has been deployed but isn't actually going to come out tame.
Calling initedog() /after/ deciding whether it's needed or not prevents
a first-pet livelog being produced for a figurine that didn't come out
tame.
The guardian angel code, which avoids calling initedog(), can never be
the hero's first pet anyway because it only appears tame when the hero
has already broken petless conduct. But while checking, I noticed a
duplicate comment, so I removed that.
I was working on another patch involving a message that could be printed
for either a monster or the player (using a struct monst * variable that
either holds the monster or &gy.youmonst), but wasn't able to easily use
pline_mon for the message since the mx and my of youmonst aren't kept
updated as the hero moves. (In my testing, they were always 0, but it's
not clear if they will remain 0 throughout the game, or if that's a bad
assumption to make.)
Allow this in the future by checking for youmonst in pline_mon and
setting the coordinates to 0,0 explicitly so no relative coordinate
message gets printed when it's about the hero.
I'm not sure if it's a reasonable assumption that no messages that could
ever be passed to pline_mon for a player would ever need to note
"(here)" when accessiblemsg is turned on. If that's the case, the
correct thing would be to set the coordinates to u.ux, u.uy instead of
0,0.
Most recent version of XQuartz, same as before. Unfortunately,
newer version of macOS => newer version of Xcode and its command
line tools => newer version of clang => emulating newer version of
gcc which defaults to a more recent version of StdC, I suppose, or
perhaps our hints are specifying that. Whichever, it has resulted
in a bunch of complaints about XtOffset() used in win/X11/winX.c:
|warning: performing pointer subtraction with a null pointer has\
undefined behavior [-Wnull-pointer-subtraction]
Adding -wno-null-pointer-subtraction to X11FLAGS silences them,
but that would require figuring out which versions of gcc and
clang added -Wnull-pointer-subtraction and its negation. Revising
XtOffset() to include the ptrdiff_t casts eliminates the warnings,
avoiding the need for version conditionals to deal with X11FLAGS.
Bug description of #1386 by @copperwater on GitHub:
"When generating a random monster from a class using des.monster(),
the G_NOGEN in their statblock is suppressed, but because every monster
of this class has frequency 0, none of them are actually eligible to get
picked. mkclass ends up returning a null pointer and create_monster has
to pick a random monster instead.
This affects the following levels (all the ones that use random sea monsters):
Healer quest start
Healer quest locate
Plane of Water (difficult to notice, since it has lots of specific sea monsters and only 5 random ones)
This can be pretty easily viewed by going to the Healer quest start and
detecting monsters: there is a shark and a giant eel, which are
specifically defined, but the remaining random sea monster that should
be there is absent."
Add a tracking array mclass_maxf[MAXMCLASSES] (about 61 entries, the
first not being used), and fill it one time in init_mongen_order() with
the maximum frequency value seen of any monster in that class.
Any mclass_maxf[] entry of zero represents that entire class of monsters
having no positive frequency value.
Detect that in mkclass_aligned(), and use it to work around the situation
to produce the monster being sought by the Lua level description file.
Reported by k21971: applying an axe toward a location that contained
both a tree and a boulder (or statue) would use the axe to break the
boulder/statue rather than chop down the tree.
Different code is used to finish the dig/chop than is used to decide
whether the tool is appropriate for its target.
Fixes#1383
Issue reported by Umbire: if a mind flayer got turned to stone by
hitting a hero who is polymorphed into a cockatrice and the first
tentacle drain missed but a subsequent one hit, any remaining ones
would keep being applied even though the mind flayer was dead.
This works but doesn't feel right to me. A more substantial change
to mhitm_ad_drin() didn't work as expected so I've settled for this.
Fixes#1378
Issue reported by elunna: nethack has become confused about
resistances held by poly'd hero.
resists_xxxx() got changed to check worn and carried equipment
so was no longer accurate for use when changes shape.
Fixes#1382
When hunting for player's run-time config file under MacOS/OSX,
nethack looks for .nethackrc (or $HOME/.nethackrc), then if not
found it looks for
"$HOME/Library/Preferences/NetHack Defaults", and finally for
"$HOME/Library/Preferences/NetHack Defaults.txt".
When none of those exists, the last choice has been being left in
configfile[] and can get used in messages.
The menu for entering the tutorial includes a tip about setting
OPTIONS=!tutorial in the config file, but it was showing the third
choice rather than the first when none of them are found. Change
config file name setup to remember the first name rather than the
last when it represents a non-existant/not-yet-existent file, so
that the tip recommnends the standard Unix name rather than the
Mac-specific one.
Instead of packing a coordinate into unsigned long, store the goal in
a coord struct, making the code a bit cleaner. Monster struct is
of course slightly bigger, but that should not really matter.
No change in monster behaviour.
Breaks saves and bones.
The array was ending up ordered the same on different qsort
implementations.
This incorporates the mlet value into the sort value for comparison.
That guarantees that everything stays ordered by mlet, followed by
difficulty (and would even if something ever got misplaced in
monsters.h). It means the "they are equal" zero return for differing
mlet values is not required, and has been removed.
This removes the "they are equal" zero returns for G_NOGEN | G_UNIQ
monsters, so they will still get sorted rather than left at the
whatever array element they happened to be at (which I don't think
should be an issue?).
An assumption of monster generating code is that monsters within a class
appear in increasing order of difficulty. This wasn't the case with
some monsters, but swapping the monsters around is rather intrusive,
and doesn't really lend to changing monster difficulties when needed.
As a result, for example ghouls would not randomly generate when they
should have at certain level difficulties (where a ghoul is weak enough
to generate but an ettin zombie would be too strong).
Keep a separate array of monster indexes sorted correctly, generate it
when required by mkclass()
Description of this bug via copperwater <aosdict@gmail.com>
Noticed while testing an earlier fix - after your pet consumes a large mimic
corpse don't have it remain leashed once it begins to mimic something
that isn't leashable
Tutorial code doesn't handle saving and reloading the game gracefully,
and manually saving has been disabled in there already.
Also disable automatic saving in the tutorial when the terminal goes away.
Main problem was there was no condition applied to this message, so
anyone would hear it even if they were deaf. Even assuming a cry of pain
is something that could be seen, the message was still printed when the
hero couldn't see the gremlin.
This puts both a deafness check and a range check on that cry (if a
gremlin somehow takes light damage on the other side of the map behind
many walls, it doesn't make much sense to hear its cry), and provides an
alternate message if the hero can't hear it, but can see it. The
alternate message does rely on the hero being able to /see/, not just
spot, the gremlin and the light it's shying away from -- if you can only
sense it, there is no special message.
This implements a TODO to return an object's material as text rather
than as an int when a Lua file requests all the details about an
object's objclass. That is as simple as looking it up in materialnm[].
With that done, it's possible to clean up the one use where a Lua file
looks up the material of an object it generated, in the
"water-surrounded vault" themed room, previously an inflexible 19 but
which can now be compared directly to "glass". It also enables
shortening the comments that follow since the branches of the if
statement are now obvious.
Report the effect of suit and/or robe on spell casting during
attribute enlightenment.
Doesn't attempt to include other armor slots. That's complicated
and would end up being too verbose.
Some variants were already using a similar approach
using a struct called 'ebones', so adopt the same naming
so NetHack-3.7, hardfought, and some variants are using
the same name.
As before there are fields in the struct that are not
currently used by NetHack-3.7, but the intent is that
hardfought save and bones files can be loaded by
NetHack-3.7 without code modification, for debugging
bug reports.
This invalidates existing save and bones files.
Issue reported by elunna: the definition of the Mitre of Holiness
specifies that carrying it should confer fire resistance but that
didn't work.
The Mitre's definition (added in 3.1.0) has always included that,
but such a capability had never been implemented. Wearing it didn't
confer fire resistance either--its definition doesn't bother to
specify a 'defend' attribute since the 'carry' one should cover that.
This adds carrying capability for damage types fire, cold, sleep,
disintegration, electrity, poison, acid, and petrification. Fire is
still specified by the Mitre; none of the others are currently used.
Fixes#1362
There are two hardfought code additions that render save and bones files incompatible
with the upstream NetHack-3.7, and that makes testing with hardfought
save and bones files more challenging than it needs to be, when
investigating and troubleshooting bug reports.
Add some unused fields to advance towards achieving save file parity with
hardfought, which is a significant source of play-testing for NetHack-3.7.
1) the elbereth field addition to u_conduct
This adds an unused placeholder field named 'hf_reserved1', at the appropriate
place in u_conduct to achieve struct field parity with the one in use on
hardfought.
2) hardfought adds a field to struct monst:
char former_rank[25]; /* for bones' ghost rank in their former life */
Instead of adding that to every monst, this adds a new mextra struct
named 'former', which currently contains the equivalent 25-character
field called 'rank' which can hold the content that was in the
former_rank[25] field. That way, the field will only be added when it
is needed.
A pull request https://github.com/k21971/NetHack37/pull/2 has been
done on hardfought to do it the same way (untested there as of yet).
Even though NetHack-3.7 does not utilize that information presently,
this will be a further step toward allowing hardfought-generated save
and bones files to be used for troubleshooting, without modification,
on a similar architecture running stock NetHack-3.7 code.
That savefile parity won't be achieved until the after the
hardfought pull-request mentioned above (or equivalent) is merged.
As this change will not be compatible with existing save and bones
files, it will be accompanied with an EDITLEVEL increment.
Tame earth elemental picked up a no_charge object from a shop and moved
it out of the shop, causing "no_charge obj not inside tended shop"
impossible. Non-tame monsters picking up no_charge items cleared that
bit, so make the same happen for pets.
> if (strlen(simpleoname) > BUFSZ - sizeof "the ")
> simpleoname[sizeof "the "] = '\0';
The second line should have been
| simpleoname[strlen(simpleoname) - sizeof "the "] = '\0';
but fixing that isn't adequate. The BUFSZ limit is not valid when
dealing with object names since xname() leaves room for a prefix so
doesn't return the start of a BUFSZ-sized buffer.
Strangely enough, the complaint that caused me to add those two lines
isn't being triggered any more. Some other change at the same time,
perhaps splitting
Strcpy(simpleoname, obufp = the(simpleoname));
into
obufp = the(simpleoname);
Strcpy(simpleoname, obufp);
pacified the analyzer. However, it didn't resolve the valid complaint
that inserting "the " might result in overflow.
I've added a comment about simpleonames(), ansimpleoname(), and
thesimpleoname() about the possible overflow, but I don't think that
such overflow can actually happen when user-applied object name is
being suppressed.
I got confused and thought that this one (actually pair) was more
complicated than it actually is. have_mixed_leadin is used in an
ordinary way, but resetting it to false happens in spots where it
can't be used again. The analyzer complains that the assignments
don't do anything useful.
When testing the analyzer lint fixes for pager.c, I noticed that //
wasn't finding the data.base entry for stairs when examining the up
stairs on level 1. It is labelled "branch stairs up" which doesn't
match "stair*".
This construct triggered several complaints about passing Null to
Strcpy(simpleoname, obufp = the(simpleoname));
Changing that to
obufp = the(simpleoname);
Strcpy(simpleoname, obufp);
prevents it, but the original complaint is bogus and the "fix"
doesn't do anything to deal with Null arguments.
A couple of other changes introduce different code in order to get
different behavior. I updated from llvm-16 to llvm-19 but didn't
eliminate any of the spurious complaints.
Clear "next" boulder so that when pushing a pile of boulders, only
the first message for each of the 2nd, 3rd, &c will be formatted as
"next boulder". If any of them trigger additional messages, those
messages will use normal "boulder".
Commit 1acc2727 helped ensure that the which_armor(mtmp, W_SADDLE)
test at the top of put_saddle_on_mon() wouldn't lead to an obj
leak.
This commit covers off the adjacent can_saddle() test in
put_saddle_on_mon(), because if that failed, it could also lead
to a memory leak of the saddle obj passed by the caller.
- have put_saddle_on_mon() create and use its own saddle obj
if a NULL saddle obj is passed, instead of having to do that
in the caller.
- where an existing saddle obj needs to be passed from the caller,
ensure that the caller has done its own can_saddle(mon) check prior
to calling put_saddle_on_mon(), so that the can_saddle() test
in put_saddle_on_mon() won't fail.
- lastly, add an impossible() to put_saddle_on_mon() to catch
a failure when a saddle obj is passed from the caller and either
test has failed, just in case. That should not happen with any of
the existing cases now, but it will provide some bullet-proofing
for new code, new callers.
makemon() has a 1% chance to bestow a worn saddle when creating any
rideable monster. If that chance kicked in on a knight's starting
pony, an extra saddle would end up being created but not worn nor
in inventory nor on floor so not be freed when the game ended.
That 1% chance also overrode saddle suppression for pauper knights.
There wouldn't be any extra saddle but their pony could start with
one, against intent.
Have makedog() (which is only used for starting pet) tell makemon()
to suppress inventory when creating the initial pet.
Overzealous change yesterday. For use_defensive(), the unicorn
horn case already has guards for Null item and the added one
issues bogus panic() when a unicorn or ki-rin uses its own horn.
In file included from ../include/config.h:723:0,
from ../include/hack.h:10,
from files.c:8:
../include/global.h:519:24: error: expected ')' before '<=' token
#define unctrl(c) ((c) <= C('z') ? (0x60 | (c)) : (c))
^
../lib/pdcursesmod/curses.h:1686:16: note: in expansion of macro 'unctrl'
PDCEX char *unctrl(chtype);
^~~~~~
Define a macro NH_C to provide a shorter & simpler way to test for
which C standard the build is being carried out under (c99 or c23).
NH_C > 202300L Being compiled under C23 or greater
NH_C > 199900L Being compiled under C99 or greater
NH_C > 198900L Being compiled under C89 or greater,
or C std could not be determined.
While NetHack only requires c99, we've been taking advantage
of some c23 features (attributes), if they are available,
to allow the use of ATTRNORETURN/NORETURN and FALLTHROUGH on
compilers other than gcc.
Also add some comment documentation to tradstdc.h about NetHack's
use of c99.
The sys/unix/Makefile.top change overcomes a warning in the
Makefile-generated nhlua.h. That warning arises under some compilers
that rely on attribute [[noreturn]] ahead of a declaration
(NetHack macro ATTRNORETURN), rather than the trailing gcc
__attribute((noreturn)) (NetHack macro NORETURN). The sed command
is modified to include ATTRNORETURN at the start of the declaration
in addition to the NORETURN at the end of the declaration, in the
generated file. That's the same combination that's used for the
declaration of other functions that don't return.
I'm not really sure about this one. insert_branch(branch,) is
specified as not accepting a Null pointer and doesn't have any
defense against it, but the know level setup seems to allow a null
pointer through. I'm not sure whether this is the right fix.
If one or more boulders were next to lava and hero broke a wand of digging
next to that location, the boulder(s) stayed over the lava causing a sanity
checking error.
The missing break meant that executation fell through to the default
case and reset xlocale and ylocale to 0. The comment states that
this is for the fuzzer; I have no idea whether this fix matters to it.
Verifying that strlen(string) isn't too long, then allocating and
copying strlen(string)+1 draws a complaint about strcpy() overflowing
its output buffer.
Not an issue for regular play, but could matter for config file and
sysconf manipulation.
Picked arbitrarily; there weren't any unresolved analyzer complaints
for trap.c. I wonder why the onefile analysis isn't complaining here.
'in_sight' may have been relevant before the trapeffect_xyz() code
was split apart, but it isn't useful for trapeffect_hole() despite
the comment about it.
release_holding_trap() is fairly convoluted and the complaints being
addressed here were relevant.
This cleans up analyzer feedback in shk.c, based on the set of
warnings specified by hints/MacOS.370 and the lower level hints it
applies, rather than anything specified during periodic 'onefile'
processing.
shk.c is the only file I've analyzed, to try again to figure out
how to suppress the old complaint that has been causing a special
case in Genonefile. It isn't triggering during my testing but that
might be due to something in use by onefile but not by normal hints.
I'm running ccc-analyzer from llvm-16; I don't know whether that
matches Genonefile.
Casting charm monster with pets nearby could reset the edog struct.
If the pet ate a mimic corpse and was pretending to be something else
when that edog reset happened, the sanity checking would issue an impossible.
The error happened because meating was reset, but the pet appearance was not,
but the edog struct reset is the part being wrong. Lets not do that.
To reproduce, turn on sanity checking, create a tame dog, give it a mimic corpse
to eat, #wizcast charm monster next to it.
Reported by paxed. A potion of oil, that was already in the midst of exploding,
got picked up through spot_effects(), which led to it merging with
another potion of oil and the freeing of the original obj.
The original obj pointer was still held by breakobj(), and breakobj()
proceeded to delete the obj (again).
Function nesting:
1 spelleffects()
2 -> weffects()
3 -> bhit()
4 -> bhitpile()
5 -> bhito(obj ...)
6 -> hero_breaks(obj ...)
7 -> breakobj(obj ...)
8 -> explode_oil(obj ...)
9 -> splatter_burning_oil()
10 -> explode()
11 -> zap_over_floor()
12 -> melt_ice()
13 -> spot_effects()
14 -> pickup()
15 -> pickup_object(obj ...)
16 -> pick_obj(obj ...)
17 -> addinv(obj ...)
18 -> addinv_core0(obj ...)
19 -> merged(obj ...)
20 -> obfree(obj ...)
21 -> dealloc_obj(obj ...)
8 -> delobj(obj ...)
9 -> delobj_core(obj ...)
10 -> obfree(obj ...)
11 -> dealloc_obj(obj ...)
12 -> impossible("obj already deleted)
This marks the exploding potion with LOST_EXPLODING, so that it won't
get picked up, or merged with another object during the long
sequence of functions, and that should take care of 15-21 above.
Previously, the code for monster healing was repeated every time it
was needed; this commit sends it all through a common function, which
will make it easier to make changes to how monster healing works in
the future.
This is just a code reorganisation and won't have any gameplay
effect unless I made a mistake.
The new EXTRA_SANITY_CHECK for a monster mimicking a monster. It
falsely triggered if the hero was hallucinating. Just add an
assertion that the monster index is within valid range.
The report (sent directly to devteam) stated that the bump-into-mimic
code might crash when bumping into a mimic that is masqueraing as
some other monster. Mimics don't actually do that, but the Wizard
of Yendor mimics another monster via Double Trouble. All I got from
it though is
|Wait! That's <other monster>!
which won't crash but is a fairly useless message.
This changes it to be
|Wait! That <other monster> is <the Wizard of Yendor>!
which seems a bit bland but provides useful information.
Use ESHK(shkp)->bill_p consistently. The bill[] array field is used
to initialize the bill_p pointer field rather than be used directly
when manipulating shop bills.
While in there, get rid of some '#if DUMB' from pre-standard C days.
I couldn't reproduce the reported problem but the backtrace suggests
that defsyms[monst->mappearance] was probably out of bounds so that
nh_snprintf() got bad data. That might conceivably happen if the
glyph didn't match the mimic's mappearance, but I not sure how that
would occur.
This avoids using mappearance as an index into defsyms[] and should
give an impossible if that situation does come up.
This time the no_charge object was being carried by a pet.
To trigger, drop an object into shop, decline to sell it,
pick up an object belonging to shopkeeper, decline to buy it,
wait for your pet to pick up the no_charge object you dropped,
teleport out of the shop, wait for shopkeeper to walk out of
the shop.
- Add a vision sanity checking routine
- Recalc block point when digging a door for temporary clouds
- Add recalc_block_point after cvt_sdoor_to_door, because doorways
on the Rogue level have no doors, and otherwise the sanity checking
would complain. This doesn't actually change how the Rogue level
vision works, as it uses a different vision system
- Monster using a trap in a secret corridor revealed the corridor,
but didn't unblock the vision unless you saw the location
Sanity checking was complaining about a no_charge obj in untended shop.
Angry shopkeeper was accepting thrown items as no_charge objects:
To reproduce the impossible, kick down the shop door angering
the shopkeeper. While the shopkeeper is still in their shop,
throw an item they don't want into the shop. Wait for the shopkeeper
to get out of the shop.
Move the anger checking before the sell auto-accept code,
so the shopkeeper will charge for the object.
remove_object cleared the vision when the last boulder was removed
from a location, without considering temporary [poison] clouds.
This particular case happened when pushing a boulder.
We can't just unconditionally unblock vision for a location when a boulder
falls into a pool, because the location may also have a (poison) cloud on it.
Apparently, restoring of saved games on Windows has been
broken since 1f36b98b, 'selectsaved' extension from Oct 10.
That change was altering the names of the files saved on disk
to a new format introduced at that time, but the game was not
opening a savefile with that same name, and the restore failed.
The code that renamed the savefile to match the internal name
was not part of 1f36b98b, it already existed prior to the new
internally-stored format.
To get things functional, this commit disables the code that
carries out the renaming of the on-disk savefile to match the
internal name in the savefile entirely, at least for now.
This relates to GitHub issue #1346 item 2.
Even though most of these are cast to void (but not all), the
mips cross-compiler seems determined to warn about them anyway.
Suppress that particular warning altogether to quiet the build.
That is not the ideal approach, but if the normal way of whitelisting
individual cases isn't working, I'm not sure of another course of
action.
Commit ba731a346b "fix shop steal when
teleporting your engulfer" mentioned that there was no longer any
message given if engulfer+hero got teleported. Add such. It is a
bit lame but the situation is rare enough that it should suffice.
A couple of option processing functions, one of which was called in file.c, were
recently added to sys/unix/unixmain.c, but the wasm build does not include unixmain.c,
it uses sys/libnh/libnhmain.c.
Transcribe the functions into sys/libnh/libnhmain.c.
Also, do not #include "wintty.h" for NOTTYGRAPHICS builds.
Remove start_screen() and end_screen() from the
Window-port interface.
They were only ever used by tty, and there was a comment
carried to several window-ports about how they "really
should go away. They are tty-specific"
term_start_screen() and term_end_screen() are part of
terminal/NO_TERMS supporting routines now.
Add a note about NO_TERMS to include/wintty.h for clarity.
Rename tty_startup and tty_shutdown to term_startup() and
term_shutdown(). They are found in termcap.c for !NO_TERMS
like most of the other term_ routines, as well as having
versions for several of the NO_TERMS platforms. They aren't
part of the tty_interface called from the core. The tty
implementation does call and rely on them.
Remove some conditional #ifdef's around term_shutdown()
(formerly tty_shutdown()) and just ensure that all the
tty platforms have an implementation that they can link
with, even if it is just a stub presently.
Put the protype for nethack_exit in extern.h to reduce
maintenance to a single spot, and remove it from other
locations. A warning in the msdos cross-compile led to
this change.
Issue reported by ostrosablin: having Kick enabled as one of the
values for the 'autounlock' option succeeded it prompting "kick it?"
when walking into a locked closed door, but answering "yes" behaved
the same as answering "no".
There's bound to be a better way of fixing this, but this works.
Fixes#1360
Dented pots got their own encyclopedia entry, so they shouldn't still
match to "helmet". Even without this change, they match the "dented pot"
entry correctly, but only by virtue of it appearing earlier in the
encyclopedia.
Inverting the match to "~dented pot" isn't necessary since it isn't
something that would otherwise match "helmet", so just remove it.
Noticed when I summoned a quantum mechanic in wizard mode with a
starting character who should have no armor protection against their
teleport attack, but every touch resulted in "You are not affected". It
turns out the if statement checking for armor protection is backwards,
so you were never affected when you have no protection and were almost
always affected when you had good protection.
This appears to date back to when the all-purpose 'negated' variable was
removed and "You are not affected" moved to after the negation check;
the new conditional kept the ! by mistake.
Using #monster to make the steed use the breath weapon often
failed because the steed did not want to breathe at weak or
too strong monsters.
Make #monster force the steed use the breath, and if there is
no targets available, make the steed make some noise as feedback.
The tin-eating context was pointing to a non-existent object,
causing an error when the fuzzer somehow managed to continue eating
the freed tin object.
Clear the pointer when the tin leaves inventory or the object
is deleted.
Picking up a shop item and not paying it, getting swallowed
by a monster, and then teleporting the monster out of the shop
with you in it, the shopkeeper didn't notice the theft.
But the object was not marked as paid either.
Also prevent giving a message of the swallower disappearing
and appearing when it was teleported. (Although now there's
no message given, so something should be added ...)
If water walking boots haven't been discovered yet and underwater
hero rises to the surface when putting a pair on, discover them.
(Sinking while removing such on water already discovers them.)
Options processing can be early, even before ttyDisplay is allocated.
If we find that TTY_PERM_INVENT initialization is happening too early,
just set a marker (iflags.perm_invent_pending) to try again a bit later.
The changes in win/share are just to be able to sucessfully
reproduce the original issue on Windows. It was easily reproduced
on Unix, just by building with TTY_PERM_INVENT in include/config.h
and setting OPTIONS=perm_invent in config file.
This originated with a bug in NerfHack in which the developer specified
an inventory for a quest nemesis, but neglected to include the Bell of
Opening in it. Since monsters' inventory contents from makemon() were
tossed out completely, this caused a situation where the Bell was
deleted and the game was unwinnable. The first part of this change is
guarding against that by adding mdrop_special_objs before discarding the
inventory. This does create a possibility where if the programmer *does*
specify a nemesis get the Bell item in their inventory, while neglecting
to remove its special case generation in makemon.c, it would generate
twice - but two Bells is better than none.
Working on that fix led me to think about a limitation of the current
sp_lev.c behavior. You could either have a monster generate with its
species-typical inventory by not specifying an inventory for it, or you
could have it generate with custom inventory but then have to use that
to clumsily reproduce the normal inventory's complex chances and
conditionals in mongets(). So the remainder of this commit implements
another flag for des.monster(), keep_default_invent, that allows for
more flexibility in two ways:
1. When des.monster() contains an inventory function and
keep_default_invent is true, the monster will retain everything it
gets from makemon() and the objects in the inventory function are in
ADDITION to those. This is useful for augmenting a monster's default
kit with something to make them more threatening, or just more loot.
2. When des.monster contains no inventory function and
keep_default_invent is false, the monster will get NO inventory even
if its species is normally supposed to. I'm not sure where exactly
this would be used, but it doesn't hurt to have it available.
When keep_default_invent is not specified at all, the behavior remains
the same as it is now - if inventory is provided, default items are
discarded, and if not, they are kept.
When the des.monster() statements for the vampire ladies were changed to
use the lua-table form, the coordinate argument was not given the coord=
name in the table, so the lua loader was ignoring it and the vampire
ladies were placed on random spaces on the level. Fix this by supplying
the coord=; testing shows that they now appear back in the niches.
Also lowercase the monster species id "Vampire Lady" to "vampire lady".
The uppercase didn't affect the species being generated but having the
id be the same case as in monsters.h is consistent with how it's done
everywhere else.
Noticed when testing the recent bec de corbin change which makes ravens
generate as peaceful; if you happened to enter medusa-3 while wielding
one, all the ravens are peaceful. Even without one, if you entered the
level as a neutral character, some of them would randomly be peaceful
due to matching alignment. But in the medusa-3.lua file, the ravens are
all unconditionally flagged as hostile.
The reason for this behavior is that the lua loading code does not
recognize "hostile" (instead peaceful=0 needs to be set), so it does
nothing and leaves the ravens to generate as if it had been unspecified.
It appeared to affect only these ravens; no other des.monster() uses
hostile=1 instead of peaceful=0.
This bug has been around in the 3.7 development branch since the change
to Lua, but doesn't happen in 3.6 because the des parser does interpret
"hostile" as meaning never peaceful.
I considered augmenting lspo_monster so that it could handle "hostile"
and treat it like peaceful=0, but figure it's probably better not to
have two different booleans that control the same flag (what if someone
specified peaceful = 1 and hostile = 1?)
Noticed a few corners in some bigroom maps were | instead of -, which
doesn't have any gameplay effect but was mildly annoying for what I was
doing at the time (copying the maps out into documentation that is
supposed to show what the maps look like in-game).
There are other special levels out there that still use | for corners;
this doesn't address those, only the bigrooms.
My commit 82f0b1e8ea to make monsters which had nowhere
to move would panic attack the hero if possible, broke the
special unicorn handling; they avoid being in-line with hero,
so often had nowhere to move...
Fixes#1344
Remove the XP gain for tourist seeing a new type of monster
nearby, as it apparently made tourists a bit harder by forcing
early level gains.
Monsters next to hero are still marked as seen close-up, but
fix the code so it doesn't count undetected monsters.
Tourists still gain XP from "taking photos" of new types of monsters,
but only if they haven't seen the monster close up before.
(No actual photos are taken.)
Aka issue #1339 take II
For hero-owned container with some unpaid items, the itemized
shopping bill had a spurious index into the traditional shopping
bill (since it wasn't in that bill due not being unpaid). When mixed
with unpaid items that weren't in the container, that could cause
bill corruption while updating the traditional bill during payment,
leading to impossible warnings.
Fixes#1339Fixes#1350
Content:
* Document that the command has a default file list.
* Demote "COMMANDS" section to "Operations" subsection. The former term
is (1) too easily confused with Unix commands and (2) not a widely
used section heading in man pages.
Style:
* Italicize command names.
* Italicize file names.
* Use idiomatic man page cross references.
* Present operation and option letters in alphabetical order.
* Render option descriptions as full sentences.
* Set bug list as a bulleted list.
Markup:
* Break input lines at sentence boundaries.
* Favor use of man(7) font selection and alternation macros over roff(7)
font selection escape sequences.
* Drop numerous extraneous paragraphing macro calls. See subsection
"Horizontal and vertical spacing" of groff_man(7).
* Replace use of *roff requests to break lines and vertically space with
calls of paragraphing macros, which is what they're for. Two things
the page author didn't know: `.sp 1` already implies a break, so the
preceding `.br` was redundant. `.sp 1` without an argument already
means to vertically space by 1 vee; that is, the "1" argument was
superfluous. It was a bad idea anyway because the default
inter-paragraph spacing in man(7) is not one vee, but 0.4v--this
matters when typesetting. It has also been the case since 1979.
* Use `RS` and `RE` macros instead of a literal tab to achieve a
relative inset. Use of the macros is more idiomatic.
* Use `EX` and `EE` to attempt to set the examples in a monospaced font
family. These are extensions and are silently ignored by formatters
that don't support them.
groff_man(7):
.EX
.EE Begin and end example. After .EX, filling is disabled and a
constant‐width (monospaced) font is selected. Calling .EE
enables filling and restores the previous font.
.EX and .EE are extensions introduced in Ninth Edition Unix.
Documenter’s Workbench, Heirloom Doctools, and Plan 9
troffs, and mandoc (since 1.12.2) also support them.
Solaris troff does not. See subsection “Use of extensions”
in groff_man_style(7).
* Kill off useless trailing space on input line.
Follow Unix idioms and the guidelines presented in groff_man_style(7).[1]
* Present multiple synopses since the command has multiple operation
modes accessed via mutually inexpressible command letters. See the
POSIX standard for copious precedent.
* Stop implying that file name arguments are accepted alongside the `I`
option; see line 236 of util/dlb_main.c.
* Stop spacing around synopsis punctuation where unnecessary.
* Set metasyntactic variables (parameters) in italics, not roman or
bold.
* Spell ellipsis idiomatically for pleasant typesetting.
* Use `\c` escape sequence to force adjacency of tar-like option letters
to the mandatory operation letter.
* Use singular, not plural, for repeatable argument. The ellipsis does
the grammatical work of pluralization for us.
[1] Full disclosure: I wrote much of (the current form of) that man page.
Portions of these man pages seem at one time to have been dynamically
selected, but the mechanism for doing so appears to be commented out in
the source tree: see "NHGREP" in sys/{unix,vms}/Makefile.doc.
Wrap them in *roff "ignore blocks" to keep their noise from cluttering
the man page actually seen by the users.
Issue reported by ars3niy: with the relatively new container
handling, buying multiple items when some were inside a container
sometimes triggered impossible "unpaid_cost: object wasn't on any
bill" warnings and not buy all intended items. Once that occurred,
subsequent inventory display would repeat the warning.
A couple weeks back, I managed to produce a save file which would
reproduce the problem when 'p' was issued, but failed to figure
out why that was happening. I accidentally deleted that save file
and it took quite a lot of further attempts to get another one.
I still don't understand why this fix is needed, but with it in
place the save file no longer triggers any problems. I'm marking
the issue fixed but that could be premature.
Fixes#1339
Instead of flat, have bin, lib and include folders for
the native DOS pieces.
If you have been cross-compiling for MSDOS, you will
need to carry out the following to bring things up-to-date:
sys/msdos/fetch-cross-compiler.sh
make CROSS_TO_MSDOS=1 WANT_DEBUG=1 package
When glyph updates are on, pline can be called recursively
when the vision is being fully recalculated. This caused
the recursively called pline to output raw_pline text.
Not sure if this is the correct way to fix it, but can't really
turn off or block the glyph update notices either ...
Fuzzer encountered impossible "timer sanity: melt timer on non-ice",
when vault guard entered the vault, changing the vault wall unconditionally
into room floor; in this case the vault wall was ice with a melting timer
attached.
Delete any melting ice timers on that location when turning it into floor.
Dipping a lit potion of oil into another potion could
turn the potion of oil into another potion; this resulted
in "burn_object: unexpected obj" impossible after the lit timer
ran out.
Just make an explosion if trying to dip a lit potion of oil.
If freedynamicdata() gets called twice, for whatever reason, a "double free" can occur.
warning: 44 ./nptl/pthread_kill.c: No such file or directory
(gdb) bt
#0 __pthread_kill_implementation (no_tid=0, signo=6, threadid=<optimized out>) at ./nptl/pthread_kill.c:44
#1 __pthread_kill_internal (signo=6, threadid=<optimized out>) at ./nptl/pthread_kill.c:78
#2 __GI___pthread_kill (threadid=<optimized out>, signo=signo@entry=6) at ./nptl/pthread_kill.c:89
#3 0x00007ffff7c8b26e in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#4 0x00007ffff7c6e8ff in __GI_abort () at ./stdlib/abort.c:79
#5 0x00007ffff7c6f7b6 in __libc_message_impl (fmt=fmt@entry=0x7ffff7e148d7 "%s\n")
at ../sysdeps/posix/libc_fatal.c:132
#6 0x00007ffff7ceefe5 in malloc_printerr (str=str@entry=0x7ffff7e17bf0 "free(): double free detected in tcache 2")
at ./malloc/malloc.c:5772
#7 0x00007ffff7cf154f in _int_free (av=0x7ffff7e49ac0 <main_arena>, p=<optimized out>, have_lock=0)
at ./malloc/malloc.c:4541
#8 0x00007ffff7cf3d9e in __GI___libc_free (mem=0x555555ad82a0) at ./malloc/malloc.c:3398
#9 0x00005555557c12e9 in free_rect () at rect.c:48
#10 0x00005555557d77a2 in freedynamicdata () at save.c:1240
#11 0x0000555555682754 in nh_terminate (status=0) at end.c:1671
#12 0x000055555589af15 in opt_terminate () at ../sys/unix/unixmain.c:768
#13 0x000055555589af7a in after_opt_showpaths (dir=0x0) at ../sys/unix/unixmain.c:796
#14 0x0000555555693dd9 in do_deferred_showpaths (code=0) at files.c:4491
#15 0x0000555555778405 in initoptions () at options.c:6948
#16 0x0000555555899cd9 in main (argc=2, argv=0x7fffffffdad8) at ../sys/unix/unixmain.c:151
This helps avoid a potential chicken-and-egg scenario
with the system configuration file (sysconf).
If sysconf wasn't accessible at the expected location, it
caused an immediate exit, without relaying any helpful
information. That happened even when using:
'nethack --showpaths'
That's particularly unhelpful, because the --showpaths
output might have been useful towards understanding where
NetHack was looking for such things.
That left you without an easy recourse to identify where
the game is looking for the sysconf file. That might be
especially troublesome if you didn't build the game
yourself.
Be more consistent in the use of path separators.
Add a second version of Makefile variables that contain paths,
one with a trailing separator, and one without (prefixed with R_
for use in Makefile rules).
Also, in dat/luahelper,
Updates due to correspond to the Makefile.nmake changes.
Add Makefile variable AWK to use $(AWK) instead of hardcoded awk.
Use by:
make -f dat/luahelper [target]
Target examples:
Visual Studio nmake : make -f dat/luahelper devhelp-nmake >file.txt
msys2 GNUmakefile : make -f dat/luahelper devhelp-msys >file.txt
Xcode project.pbxproj: make -f dat/luahelper devhelp-xcode >file.txt
all: generate txt files for all the above
GitHub 1343 report by @ars3niy:
"When you are blind and see with telepathy a monster whom you then hear read
a scroll, said monster turns into an "I". While it reveals which one exactly
read the scroll, it is strange that you can no longer see it with telepathy
until it moves to another square."
Fixes#1343
There was a transcription error in the comments in cstd.h for
the standard list of header files, where only the description
remained for <stdlib.h>, not the name of the file itself.
Remove several extraneous inclusions of the standard C99 headers.
Tested on the following afterwards:
Linux (using hints/linux.370) including tty, curses, qt6, and X11
macOS (using hints/macOS.370) including tty, curses, qt5, and X11
Windows MSYS2 using sys/windows/GNUmakefile
Windows Visual Studio using sys/windows/Makefile.nmake
msdos cross-compile on Ubuntu using djgpp cross-compiler
Issue reported by k21971: winter wolf cub and hellhound pup were
defined with alignment -5 (chaotic) while winter wolf and hellhound
were 0 (neutral).
K2 suggested that winter wolf plus cub both be neutral and hellhound
plus pup both be chaotic but I've gone another way: both cub and
pup are now 0 and both adults are -5.
Fixes#1340
Various bits I had in progress before Michael's commit.
Mainly forget engravings when bones are saved instead of leaving them
flagged as seen for the next hero who gets the level.
Since EDITLEVEL is being incremented for the previous patch anyway,
add the "name_from" bit to the obj struct now, as groundwork for
the code change mentioned in a TODO comment in bones.c:
/* strip user-supplied names */
/* Statue and some corpse names are left intact,
presumably in case they came from score file.
[TODO: this ought to be done differently--names
which came from such a source or came from any
stoned or killed monster should be flagged in
some manner; then we could just check the flag
here and keep "real" names (dead pets, &c) while
discarding player notes attached to statues.] */
if (has_oname(otmp)
&& !(otmp->oartifact || otmp->otyp == STATUE
|| otmp->otyp == SPE_NOVEL
|| (otmp->otyp == CORPSE
&& otmp->corpsenm >= SPECIAL_PM))) {
free_oname(otmp);
}
Also, the bitfield per-byte groupings identified in the struct
weren't accurately reflected, so rearrange the Bitfields,
and correct the per-byte groupings.
This invalidates existing save files and bones.
GitHub issue reported by ars3niy:
https://github.com/NetHack/NetHack/issues/1303
@ars3niy commented on Oct 27:
Stepping on a square with a dust or "graffiti" engraving while blind
produces no message because presumably you can't read them by swiping
the floor with your hands, however the engraving glyph still shows up on
the map afterwards. While this helps zen players, it looks like a bug.
@ville-v commented 3 days ago:
Searching while blind also reveals the engravings. Here is a save file
demonstrating the issue.
[...]
This adds an erevealed bit to engravings, to accompany the the eread
bit that is already there.
eread: refers to the text of the engraving
erevealed: refers to the engraving map symbol
Hopefully, this resolves issue 1303 without creating additional bugs.
This invalidates existing save files and bones.
Fixes#1303
Issue reported by elunna: the message given when zap_over_floor()
hits iron bars with lightning or acid was substituting a couple of
words or phrases in the wrong order, resulting in
|The {melt|dissolve} iron bars somewhat but remain intact.
when the iron bar location is flagged as non-diggable. It should be
|The iron bars {melt|dissolve} somewhat but remain intact.
Not mentioned: the corresponding message for locations that aren't
flagged as non-diggable used "melt" unconditionally. Change it to
keep "melt" for lightning but switch to "corrode away" for acid.
Fixes#1342
After finding a trap on a chest or a large box, remember it
as trapped: "You see here a trapped large box."
Randomly generated chests and boxes can be obviously trapped.
Allow defining obviously trapped containers via lua.
Invalidates saves and bones.
A fix in Janurary to avoid appending engraving text or headstone
text when examining a map location where a monster or object covers
the engraving or headstone inadvently broke the /e and /E variations
of the '/' command, which is intended to list such text even when
covered.
This build command will include line number info, gdb.exe or nhgdb.bat in the package:
make CROSS_TO_MSDOS=1 WANT_DEBUG=1 package
This build command will not include line number info, gdb.exe or nhgdb.bat in the package:
make CROSS_TO_MSDOS=1 package
We can't rely on doopen_indir return value to
check whether hero moved and opened a door.
Instead, explicitly check whether the door is still
closed, and whether hero moved.
The ones below marked "left,revisit" might be worthy of code changes
explode.c:removed: arg sx,sy passed to breaks() are coordxy and so are the parameters
invent.c: removed: discover_artifact() parameter is xint16, so change cast to match
mail.c: removed: gv.viz_rmin[] contains coordxy's, and enexto() 2nd param is too
mkmaze.c: removed: x, y are coordxy and so are cc->x, cc->y
mkroom.c: removed: tx, ty are coordxy and so are parameters to occupied()
mplayer.c: left,revisit: x,y declared int, but mk_mplayer() params are coordxy
sp_lev.c 2890: left alone: x,y declared int, but is_ok_location() params coordxy
sp_lev.c 4114: left alone: cast from lua_Integer
sp_lev.c 4123: left alone: cast from lua_Integer
sp_lev.c 4650: left alone: cast from lua_Integer returned from lua_tointeger
sp_lev.c 4765: left alone: cast from int_Integer returned from lua_tointeger
sp_lev.c 4772: left alone: cast from int_Integer returned from get_table_int
teleport.c 366: left alone: cast from int returned from rn2()
timeout.c 2171: left alone: cast from long
vault.c: left,revisit: guardx,guardy declared int, but bestcc fields are coordxy
worm.c 791: left,revisit: wseg.wx, wseg.wy are coordxy, so are nx, ny, but not
ox, oy; why are ox, oy needed at all?
worm.c 955: left alone: wx, wy are coordxy, wseg_at() params are int x, int y
zap.c 5061: left alone: cast from long
The WANT_DEBUG=1 will cause the cross-compile to include line
number information in the NetHack executable, useful for
backtraces and gdb debugging sessions.
How a developer can use the optional deploy-to-dosbox target:
make CROSS_TO_MSDOS=1 WANT_DEBUG=1 dosbox=/mnt/c/dosbox deploy-to-dosbox
where dosbox= points to the directory which will be mounted for
your drive in dosbox
THe deploy-to-dosbox recipe ensures that a target copy of gdb.exe
ends up alongside nethack.exe at the target, including:
- placing the source code that gdb requires on the target
in the nhsrc subfolder.
- an nhgdb.bat that supplies the right switches to gdb
for locating the NetHack sources.
ENHANCED_SYMBOLS is defined by default in config.h.
The msdos build tried to #undef ENHANCED_SYMBOLS
in tilemap.c, but doing it in there created a mismatch
between the data struct definition for glyph_map in wintype.h
and the initializers generated in tilemap.c
Move the msdos build catch for ENHANCED_SYMBOLS to
one single place in config1.h so that the code and data agree.
Issue reported by loggersviii: attempting #untrap from an adjacent
doorway can move the hero diagonally out of the doorway.
A followup comment by elunna pointed out that a monster's attack that
results in knockback can produce similar result.
Fixes#1305
Issue reported by elunna: if the 'fireassist' option is on and the
quiver contains ammo, 'f' while wielding an aklys switches to the
ammo's launcher instead of throwing the aklys.
Fixes#1337
Luck from sacrificing is now limited by the value of the sacrifice.
This fixes two exploits, both of which rely on getting luck up to
maximum as soon as you have an altar, a luckstone, and a few
rations, via altar-camping until you accumulate enough luck. One of
them is to use the resulting luck to throw off the balance of combat
via using it to make hit chance calculations irrelevant. The other
is to use it to get crowned early in the game; in particular,
getting crowned pre-Sokoban is often viable and, especially for
chaotic characters, solves most of the game's difficulty at that
point (because the intrinisics and weapon are enough to carry a
character to the Castle given even mediocre luck with finding armor).
After this commit, becoming crowned very early in the game is more
difficult (likely requiring unicorns and identified gems), and the
hit chance gain from luck becomes a more gradual gain over the
course of the game rather than all happening immediately upon
finding the altar and luckstone.
In addition to making the game more balanced, this also discourages
grinding by reducing the incentive for altar-camping, so it will
hopefully make it more fun as well.
In 3.6, artifact gifts are often either a) entirely useless or
b) gamebreaking, neither of which is really ideal from a balance
perspective.
This commit aims to make artifact gifts more useful in the early
game by greatly increasing the chance for situational artifacts to
generate positively enchanted. However, the most powerful
artifacts will now only be gifted if you offer a high-value corpse,
meaning that they are only likely to be accessible later in the
game. The selection of which artifact to gift has become more
complicated in order to a) increase the chance that it fits the
character and b) reduce cheese strategies (e.g. it is no longer
possible for elves to force the gifting of Stormbringer as the
first sacrifice gift).
Issue reported by ars3niy: pets with reflection or ranged attacks
would only attack floating eyes when rolling the 10% random chance
that other pets have even though they could have always safely
attacked.
This fixes the situation for melee attacks by pets who have
reflection.
dog_move() is too complicated for my feeble brain to cope with the
ranged attack aspect. Pets still won't use ranged attacks against
floating eyes.
With the fix for reflection, I discovered that silver dragons
would be subjected to floating eyes' passive paralysis even when
their breath attack was suppressed. (It wouldn't impact them, due
to reflection, but the message about the floating eye being hit by
its reflected gaze was being delivered without being preceded by
any message since no attack had taken place yet.) This fixes that.
\#1336 is still open
Ensure that the destination selection for intentional
teleport begins at the hero, rather than starting at
a place on the map stored from a prior travel command.
The location of the call to dobjsfree() in freedynamicdata()
appears to have been non-ideal.
After getting complaints from a leak-sensing tool after
#quit, the source of the leaks was investigated.
The call to dobjsfree() had been placed immediately following
a call to dmonsfree(), and it did clear out the go.objs_deleted
chain at that point.
Further investigation revealed that the following functions
later on in freedynamicdata() were then adding more deleted
objects to the go.objs_deleted chain:
free_current_level();
freeobjchn(gi.invent);
freeobjchn(gm.migrating_objs);
Move the call to dobjsfree() to a location after those listed
above.
For the wizard-mode command #migratemons at the
"How many random monsters to migrate to next level? [0]" prompt,
allow a negative number to cause it to use existing monsters
already on the level for the forced migration, up until the
absolute value of the number, instead of random new monsters
as it does for a positive number.
For example, specify -20 to force-migrate 20 existing monsters
already on the map.
I don't think this solves the recent light source reports,
but it changes a couple of things in an attempt to get more
information.
1. Having gy.youmonst.m_id field always be zero makes it tough
to distinguish it from uninitialized memory, or a random memory
value. This changes the m_id for the hero's gy.youmonst.m_id
to always hold the identifier 1, instead of 0.
2. write_ls was taking the stashed pointer in the light source,
and using it to immediately extract the m_id field and search
for that m_id. This changes the approach slightly, to actually
try and locate the stashed pointer itself in one of the monster
chains. Only if the monster pointer is located, do we dereference
it to obtain the m_id field.
3. For the interim, mark the saved ls with another set bit when
there has been a failure to locate the monst. At this time,
no code is acting on that bit, but it can be seen in a debug
session.
Hopefully, the next report will provide enough information to
understand the scenario a little better.
Using 'f', if hero is wielding a polearm, and a monster is in range,
don't switch away even if we do have ammo in quiver and a launcher
in the inventory.
Merge the recent change in the effect of blessed scroll of taming on
already tame monsters with the earlier change of any taming on already
tame monsters. Non-blessed has a chance of boosting monst->mtame by 1
when it is less than 10, more likely the lower the current value is.
For blessed, boost by 2 after that, so possibly by 3 if it is very low.
Make spell of charm monster when skilled or expert in enchantment
spells behave the same as blessed scroll of taming. [I'm not too sure
about this; it may make the spell too powerful.]
Experience equivalent to killing a monster is gained when starting a turn
adjacent to and being able to see the monster.
Breaks saves.
Idea and parts of code via dNetHack
When tutorializing a command that uses a ^X notation, show Ctrl-X
instead. Also show an explanation of the ^X nearby.
The only ctrl-key combination that can currently happen in the tutorial,
without rebinding keys, is the kick-command.
Fixes#1327
Sidenote: The main window size calculations are getting stupid.
It would be better to find out the widget sizes and shift
the splitter up, instead of letting it just take up half of the main window.
Idea and part of the code by Richard Henschel
GNU make looks first for a file called GNUmakefile, ahead of
looking for Makefile and then makefile.
Renaming sys/windows/Makefile.mingw32 to sys/windows/GNUmakefile
allows:
o src/GNUmakefile (for use by GNU make) and src/Makefile (for use
Microsoft nmake) to both reside in the src folder during build.
o src/GNUmakefile will be used by GNU make, without having to
explicitly specify "-f GNUmakefile" on the GNU make command line.
o src/Makefile will be used by Microsoft nmake, without having to
explicitly specify "-f Makefile" on the Microsoft nmake command line.
For the gcc build, the movemement of sys/windows/GNUmakefile needs
to be copied to src/GNUmakefile as part of the build process (see
sys/windows/build-msys2.txt).
For the Microsoft Visual Studio command line build with nmake,
sys/windows/Makefile.nmake needs to be copied to src/Makefile as
part of the build process (see sys/windows/build-nmake.txt).
They are both copied to the src folder from their respective
repository source file names when the nhsetup.bat file is used.
When a mimic in door form is hit by a wand of locking or wand of
opening or corresponding spell, bring it out of concealment like
was recently done for being zapped while in chest form. And give
some feedback rather than just changing the mimic's form to 'm'.
Give more detailed feedback when bumping into a mimic while moving.
The pull request was:
https://github.com/NetHack/NetHack/pull/1331
by @guillaumebrunerie
The text by @guillaumebrunerie that accompanied the pull request was:
I have been working on a browser/mobile port of NetHack 3.7 using
cross-compilation to WebAssembly (it is very playable already, you can
try it at https://guillaumebrunerie.github.io/nethack/).
[screen shot]
The existing code for compiling to WebAssembly was a great help, although
it wasn't fully up to date and was missing a number of things in order
to be able to create a proper window port (for instance there was no way
to set 'iflags.window_inited' to true, 'print_glyph' was not working
properly as its signature changed, and various other things).
This pull request contains various fixes and additions that I found were
needed/helpful.
Changes:
* export more constants/pointers/globals.
* fix various types that are incorrect.
* disable compression of save files, as it uses fork which isn't supported
in WebAssembly.
* include 'genl_player_setup' when 'SHIM_GRAPHICS' is defined, in order to
make it possible to reuse the existing player selection code.
* move initialization of JavaScript global constants up, as I was running
in some issue when 'raw_print' was being called before initialization.
* change various compilation options for Emscripten, in particular it now
generates an ES6 module (easier to use in a modern Javascript project)
and exports more methods from Emscripten (for instance to be able to use
the virtual file system when saving).
* simplify the implementation of the main loop to avoid 'setTimeout' which
can be pretty slow in the browser when called many times.
* change the way pointer arguments are being sent to JavaScript in 'getArg'
(they were being sent as a pointer to the pointer itself on the stack,
which doesn't really make sense, now the pointer itself is sent).
Because newsym() would be called only on the head position of the worm,
if hilite_pet was on, the segments and head of a long worm would have
mismatched highlighting in the immediate aftermath of taming or
untaming.
There was an issue with Windows mingw build because the function
prototypes were not available. Place them into a distinct
header file nhregex.h and include it from extern.h, and
available for cppregex.cpp to include without the rest of
extern.h (which can give some problems with c++).
gcc has recognized various "magic comments" for white-listing
occurrences of implicit fallthrough in switch statements for
a long time:
The range and shape of "falls through" comments accepted are
contingent upon the level of the warning. (The default level is =3.)
-Wimplicit-fallthrough=0 disables the warning altogether.
-Wimplicit-fallthrough=1 treats any kind of comment as a "falls through" comment.
-Wimplicit-fallthrough=2 essentially accepts any comment that contains something
that matches (case insensitively) "falls?[ \t-]*thr(ough|u)" regular expression.
-Wimplicit-fallthrough=3 case sensitively matches a wide range of regular
expressions, listed in the GCC manual. E.g., all of these are accepted:
/* Falls through. */
/* fall-thru */
/* Else falls through. */
/* FALLTHRU */
/* ... falls through ... */
etc.
-Wimplicit-fallthrough=4 also, case sensitively matches a range of regular
expressions but is much more strict than level =3.
-Wimplicit-fallthrough=5 doesn't recognize any comments.
Plenty of other compilers did not recognize the gcc comment convention,
and up until now the compiler warning for detecting unintended
fallthrough had to be suppressed on other compilers. That's because the code
in NetHack has been relying on the gcc approach, and only the gcc approach.
The C23 standard introduces an attribute [[fallthrough]] for the
functionality, when implicit fallthrough warnings have been enabled.
Several popular compilers already support that, or a very similar attribute
style approach, today, even ahead of their C23 support:
C compiler whitelist approach
--------------------------- -------------------------------------
C23 conforming compilers [[fallthrough]]
clang versions supporting
standards prior to
C23 __attribute__((__fallthrough__))
Microsoft Visual Studio
since VS 2022 17.4.
The warning C5262 controls
whether the implict
fallthrough is detected and
warned about with
/std:clatest. [[fallthrough]]
This adds support to NetHack for the attribute approach by inserting a
macro FALLTHROUGH to the existing cases that require white-listing, so
other compilers can analyze things too.
The definition of the FALLTHROUGH macro is controlled in include/tradstdc.h.
The gcc comment approach has also been left in place at this time.
Issue reported by elunna: sleeping mimics can grab the hero, and
zapping a concealed mimic with a wand of sleep describes the target
as a mimic but doesn't bring it out of concealment.
The grab-when-asleep case is reasonable. It's a reflexive counter-
attack by a magical creature. And the mimic wakes up in the process.
But the mimic wasn't being brought out of concealment. Do that.
Unconceal mimics hit by wand of sleep unless already sleeping.
Fixes#1299
Issue reported by youkan700: shape change anomalies. Shapechangers
could change shape despite active protection-from-shape-changers if
hero wore two rings of protection from shape changers and took one
off. Shapechangers who migrated to a not-yet-visited level that
eventually got visited with protection from shape changers in effect
would be stuck in their current shape, even if the PfSC attribute
got toggled off and back on.
The issue included suggested fixes and those are what I've used. I
noticed a third case that only applies to wizard mode: if player
used #wizintrinsic to set a timed value for PfSC, monsters wouldn't
resume changing shape after it timed out, unless/until it got toggled
on and back off via a PfSC ring or hero left the level and returned.
Fixes#1312
If a vampire in fog cloud form moves under a closed door and then
before moving further gets killed and revives in vampire form,
destroy the door instead of moving the vampire to a nearby open
spot (which might be a distant spot if the map is crowded). If the
door is trapped, explode the trap. That will damage the vampire but
usually not by enough to kill it.
This probably ought to be generalized to be done for any shape change
at a closed location but I ran out of gas.
In case someone switches from NetHack-3.6 to NetHack-3.7 and does
'make spotless' after the switch instead of before, get rid of out
of date lev_comp and dgn_comp.
Change noit_mon_nam() to work as if it was noit_y_monnam() (without
renaming it or adding yet another monster naming routine) to use
"your <mon>" rather than "the <mon>" when <mon> is a pet that can
be seen (so not the case where "it" gets replaced by "someone" or
"something").
Commit c4a1f298e8 two and a half
months ago gave lawful Angels and Archons a chance to start with
a mace instead of a long sword so that they might get Demonbane.
It was a bit convulted; this redoes it to be more straightforward.
Fix a FIXME in Amulet_off() for removing an amulet of magical
breathing when within a poison gas cloud.
Redo message sequencing for both Amulet_on() and Amulet_off().
Use up an amulet of change if put on while the Unchanging attribute
is active (via #wizintrinsic) instead of wearing it with no effect.
Don't discover amulet of strangulation if put on while already
Strangled (via #wizintrinsic).
Add enlightenment feedback for Sunsword's blocking of becoming blind
from light flashes. It uses an extra property so that wizard mode
can report the reason.
EDITLEVEL is being incremented, so existing save and bones files are
invalidated.
When accessiblemsg is Off, coordiates supplied for various messages
stayed put after becoming stale. If you used 'mO' to toggle that
option On, you could see things like
(2east):'accessiblemsg' option toggled on.
After that, accessibility message coordinates behaved as intended.
Clear a11y.msg_loc.x,y for every pline instead of just when they
are used to augment the current message.
Most players who use accessiblemsg are bound to set it in their
config file rather than toggle it interactively so never noticed.
Misc 1: option.c doesn't need '#include <ctype.h>' because
cstd.h includes it unconditionally. Several other src/*.c are in
the same situation but I didn't touch them.
Misc 2: move set_msg_dir() and set_msg_xy() out of a warning
suppression block that isn't relevant to them.
If eating a tin killed the hero (choked, turned to stone, poly'd into
a new man with new Xp too low to survive) and bones were saved, the
tin remained intact in them.
When hero who is poly'd into metallivore form eats a tin, give a
little extra nutrition for the tin itself. Also, eat it immediately
by skipping the "It smells like <creature>" message and "Eat it? [yn]"
prompt. (The message while eating it also reports <creature>, so
skipping the 'smells' one doesn't end up hiding anything.)
In sys_early_init(), the values for sysopt.gdbpath and
sysopt.greppath were being assigned by calling dupstr()
without ensuring that a value previously assigned by
dupstr() were free()'d.
Also, the sysopt struct definition is changed to sysopt_s,
not because there's anything wrong with the original
struct sysopt sysopt;
but because I couldn't convince the debugger to use the
correct thing when trying to track down the leak.
To update, run "perl DEVEL/nhgitset.pl"
Fixes:
- "nhcommit -a" has been fixed
- NHDT was hardwired in places
- no longer complain about a missing dat directory outside of the
NetHack source tree
- make update of gitinfo atomic
- Replace some hardwired directory separators with OS-dependent constructs
Backwards Incompatibilities:
- NH_DATESUB's DATE() is now Date() to match the other variables
- MSYS2 requires an additional Perl package - the MSYS2 docs have
been updated
New Help System:
- git nhhelp
This command mirrors "git help" for nh* commands.
- See git nhhelp nhsub for general help on substitution variables
New Substitution Variables:
-Brev()
An aBREViation of $PREFIX-Branch$:$PREFIX-Revision$ - this
may help get line length under control in file headers.
-Assert(TYPE=VALUE)
If TYPE does not match VALUE, do not substitute on this line.
TYPE P checks VALUE against nethack.substprefix
-Project(arg)
Returns nethack.projectname if there is no arg and an uppercase
version if arg is uc.
Other New Features:
- Add nethack.projectname
- Documentation updates - see "git nhhelp nhsub"
- On checkout or merge of a branch, check for nhgitset version updates
and provide an optional message to the user.
- Move NH_DATESUB substitutions here from cron job to keep dates in sync
- PREFIX-* keywords now available in NH_DATESUB templates
- Support use of nhgitset.pl from a different repo; note that update
checks will be dependent on keeping the original source repo up-to-date
and in the same location.
This is additional groundwork related to
https://github.com/NetHack/NetHack/issues/1320
This additional groundwork just puts some safeguards
in place to make it rather tough to end up with an
instant death from handling a cockatrice corpse in
your inventory without appropriate protection.
At this point, still no actual petrification will occur.
Wishing is powerful, so if you cannot safely handle a cockatrice
corpse, then have a wish for one result in the corpse materializing
on the floor rather than in your inventory.
Resolves#1320
Related to #1309https://github.com/NetHack/NetHack/issues/1309
K2 commented: "This might help - k21971/EvilHack@afed641"
A comment in there states:
"Fix: sections of wall being visible when they shouldn't yet.
This has been a long-standing bug for as long as I can remember, and qt
appears to have figured it out. What was happening: the player would all
of the sudden see a section of wall in an area that they hadn't explored
yet. It was discovered that this was only occurring if that section of
wall had any type of tree up against it."
The fix there attempts to leave trees out of the check_pos non-zero return,
so give that a shot.
I didn't attempt to reproduce the situation myself,
and therefore cannot confirm that this does resolve it.
Feedback on effectiveness or side-effects are welcomed. If someone is
able to confirm that this resolves the issue without creating new
issues, we can close it, otherwise this can be reverted.
GitHub issue #1315 points out that it is possible for
a downstream function to change an object's nobj field
to point to a completely different chain.
The cited example by @vultur-cadens was:
for (obj = gi.invent; obj; obj = obj->nobj)
if (obj->oclass != COIN_CLASS && !obj->cursed && !rn2(5)) {
curse(obj);
++buc_changed;
}
curse() drops the weapon with drop_uswapwep(),
which calls dropx(),
which calls dropy(),
which calls dropz(),
which calls place_object().
place_object alters the nobj pointer, to point to the floor chain:
otmp->nobj = fobj;
fobj = otmp;
The result was that the next loop iteration was then using floor
objects from the floor chain.
This alters several for-loops to use a more consistent approach,
particularly when the obj is being handed off to a function,
where a downstream function might, or might not, alter the nobj
field.
References:
https://github.com/NetHack/NetHack/issues/1315https://www.reddit.com/r/nethack/comments/1gkc9ub/even_if_you_drop_an_item_before_drinking_from_the/
Commit 22884522, ported (from Sporkhack, Evilhack), added the ability
for monsters to maintain awareness of player resistances that they
observed.
Since they will refrain from using a ranged attack that they deem
futile, take the specific monster's seen_resistance field into consideration
when choosing code paths based on the availability of ranged attacks.
Resolves#1307
gnome-terminal was recently fixed to work correctly with
TTY_TILES_ESCCODES (and other similar private-use terminal code
sequences), and thus this isn't an incompatible combination any
more.
Makes Sokoban far less tedious when you don't have to worry about
monsters randomly popping up in the trap hallway while you're pushing
the boulder.
Adds a new exclusion zone for monster generation, and the goodpos
routine avoids the zones when GP_AVOID_MONPOS is used.
I forgot to add the code to flip the exclusion zones when implementing them.
Also improve the zone coordinates so they're correct outside of map contents.
If the saved game menu has more than 13 games, it won't be able to
use 'n' for "new game" and 'q' for "quit". Switch to 'N' and 'Q'
instead of just using the next letters in sequence. Only resort to
next-letters if there are more than 39 games.
tty and curses handle a list of many save files via menu pagination.
X11 does so with one long page possessing a scroll bar. If there
are more than 52 entries, selection via mouse is needed beyond 'Z'.
Qt has one page without any scroll bar so won't provide access to
the full set of save files when there are too many to fit on the
screen.
Slightly simplify yesterday's message adjustment made when a monster
takes off a piece of armor and puts on a different instance of the
same type of armor. ("a|an <something>" -> "another <something>")
On the Windows GUI (nethackw.exe), while running the fuzzer,
if the NetHack window gets minimized, or someone minimizes all
desktop windows, the NetHack window stops responding and won't
even repaint itself. The NetHack process continues to use the
CPU.
A break in the debugger shows that it is caught in a do loop.
I didn't delve into the issue of why minimizing the window
triggers a condition that leads to the endless loop. This
just adds a kludge to exit the loop while fuzzing.
While testing the Demonbane change, I saw
| The Angel of Crom removes a robe and puts on a robe.
which looks a bit silly. Change that to be
| The Angel of Crom removes a robe and puts on another robe.
when the two items have the same formatted description. (The second
robe evidently has a better enchantment than the first one.)
Back then Demonbane was a long sword, lawful Angels and Archons
could get either it or Sunsword as part of their starting equipment.
After it got changed into a mace instead, they could only get
Sunsword. Rather than always giving them a long sword, sometimes
give them a mace so that they get back the chance to start with
Demonbane.
Since mace is quite a bit weaker that long sword against large
opponents, provide a better enchantment for maces given to lawful
minions.
The role, race, gender, and alignment string values are 3 letters
preceded by a dash. I was looking at "name-race-role-gend-algn"
and mistakenly treated them as 4 letters preceded by a dash.
Fixing that changes PL_NSIZ_PLUS and there is one item of that size
written into save files, so the fix invalidates existing save files.
Instead of a menu listing
a - hero1
b - hero2
n - New game
q - Quit
show
a - hero1-role1-race1-gend1-algn1
b - hero2-role2-race2-gend2-algn2
n - New game
q - Quit
or
a - - hero1-role1-race1-gend1-algn1
b - X hero2-role2-race2-gend2-algn2
c - D wizard-role3-race3-gend3-algn3
n - New game
q - Quit
when any game in the list wasn't saved during normal play. (Those
are sorted by character name; the playmode is just coincidence.)
The dash for 'normal' doesn't look great but -/X/D are codes used in
entries written to paniclog. The whole playmode prefix doesn't look
particularly good but I suspect that most players relying on restore
via menu won't see it.
It should work when the character name has dashes in it but that
hasn't been properly tested.
The gender and alignment suffices reflect their value at the time of
save rather than at the start of the game. That might be considered
a bug but it was easiest.
Increments EDITLEVEL; existing save and bones files are invalidated.
sys/windows/windsys.c(419): warning C6387: 'hMod' could be '0':
this does not adhere to the specification for the function 'GetProcAddress'.
sys/windows/windsys.c(437): warning C28159:
Consider using 'GetTickCount64' instead of 'GetTickCount'.
Reason: GetTickCount overflows roughly every 49 days. Code that does not
take that into account can loop indefinitely.
GetTickCount64 operates on 64 bit values and does not have that
problem
sys/windows/consoletty.c: warning C6388: '&reserved' might not be '0':
this does not adhere to the specification for the function 'WriteConsoleA'.
sys/windows/consoletty.c(2514): warning C28159:
Consider using 'IsWindows*' instead of 'GetVersion'. Reason: Deprecated.
Use VerifyVersionInfo* or IsWindows* macros from VersionHelpers.
2024-10-08 11:32:58 -04:00
493 changed files with 44867 additions and 22094 deletions
des.engraving({coord={x,y},type="engrave",text="Note: Outside the tutorial, Ctrl-key combinations are shown prefixed with a caret, like '^"..tut_ctrl_key.."'",degrade=false});
des.engraving({coord={22,11},type="engrave",text="Some items have shuffled descriptions, different each game",degrade=false});
des.engraving({coord={23,11},type="engrave",text="Pick up this scroll, read it with '"..nh.eckey("read").."', and try to remove the armor again",degrade=false});
des.engraving({coord={23,11},type="engrave",text="Pick up this scroll, read it with '"..tut_key("read").."', and try to remove the armor again",degrade=false});
des.engraving({coord={37,3},type="engrave",text="Wield the sling",degrade=false});
des.engraving({coord={36,1},type="engrave",text="Use '"..nh.eckey("fire").."' to fire missiles with the wielded launcher",degrade=false});
des.engraving({coord={36,1},type="engrave",text="Use '"..tut_key("fire").."' to fire missiles with the wielded launcher",degrade=false});
des.engraving({coord={35,4},type="engrave",text="Firing launches items from your quiver; Use '"..nh.eckey("quiver").."' to put items in it",degrade=false});
des.engraving({coord={35,4},type="engrave",text="Firing launches items from your quiver; Use '"..tut_key("quiver").."' to put items in it",degrade=false});
des.engraving({coord={33,4},type="engrave",text="You can wait a turn with '"..nh.eckey("wait").."'",degrade=false});
des.engraving({coord={33,4},type="engrave",text="You can wait a turn with '"..tut_key("wait").."'",degrade=false});
--
des.door({coord={38,6},state="closed"});
des.engraving({coord={39,6},type="engrave",text="You loot containers with '"..nh.eckey("loot").."'",degrade=false});
des.engraving({coord={39,6},type="engrave",text="You loot containers with '"..tut_key("loot").."'",degrade=false});
| adjacentok | boolean | is adjacent location ok, if given one is not suitable?
| ignorewater | boolean | ignore water when choosing location for the monster
| countbirth | boolean | do we count this monster as generated
| appear_as | string | monster can appear as object, monster, or terrain. Add "obj:", "mon:", or "ter:" prefix to the value. |
| inventory | function | objects generated in the function are given to the monster
| appear_as | string | monster can appear as object, monster, or terrain. Add "obj:", "mon:", or "ter:" prefix to the value.
| inventory | function | objects generated in the function are given to the monster (any random inventory it gets is discarded unless keep_default_invent is true)
| keep_default_invent | boolean | if inventory is specified and this is true, those items are in addition to random inventory for this species; if inventory is not specified and this is false, monster gets no starting inventory
|===
Example:
@@ -820,7 +863,7 @@ Example:
=== object
Create an object. Returns the object as an <<Obj>> class.
Create an object and place it somewhere on the map. Returns the object as an <<Obj>> class.
The table parameter accepts the following:
[options="header"]
@@ -839,12 +882,13 @@ The table parameter accepts the following:
| eroded | int | Object erosion
| locked | boolean | Is the object locked?
| trapped | boolean | Is the object trapped?
| trap_known | boolean | If container is trapped, is it obvious?
| recharged | boolean | Is the object recharged?
| 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?
@@ -1123,6 +1167,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:
@@ -1307,6 +1361,8 @@ Example:
=== room
Create a selection of locations inside the (current) room.
Does not include the edges of the room, such as its walls.
Does not do any check on terrain type, so if there are non-ROOM locations inside the room, they remain part of the selection.
Example:
@@ -1337,11 +1393,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
@@ -1530,6 +1588,11 @@ 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>>.
| 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
#define TBR_STAIR 0 /* connection with both ends having a staircase */
#define TBR_NO_UP 1 /* connection with no up staircase */
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.