The default value for obj->corpsenm is NON_PM which is -1, so the
default value of boulder->next_boulder was non-zero instead of 0
as expected. Because of that, boulder object formatting by xname()
was yielding "next boulder" when plain "boulder" was intended.
Until the boulder or one in a pile above it got pushed, then it
was explicitly reset.
When you push a pile of boulders, describe the second and remainder
as "the next boulder" rather than just "the boulder". Matters most
when pushing into water or lava and you keep on pushing when the
first one or more sink into the pool or plug it, but also matters
for an ordinary push where the top-most one moves successfully and
then blocks the continuation attempt to push the second one. It was
somewhat confusing when all the messages said "the boulder" whether
they were referring to the same boulder or different ones.
Multiple pushes on the same move has always been a bit odd, but this
doesn't change that, just the feedback it generates.
Some code from about a month back changed xname() and doname() to
only use a single 'obuf[]'. That was to make sure that perm_invent
update also used at most one obuf. They use some slightly convoluted
code when they called other routines which returned an obuf because
only the most recently allocated one can be explicitly released for
re-use. That works, so I did the same for simpleonames() and
thesimpleoname() and for some reason neglected the convolution for
simpleonames().
For the case where plural was needed, it needs two obufs and tried
to release the first, which is a no-op. So if it was used in a loop
like display_pickinv() uses doname(), it could have cycled through
all the obufs and clobbered one a caller was expecting to remember.
I'm not aware of any instances of this being an actual problem, just
happened to notice that simpleonames() was different from other
similar routines.
For Unix, add internal vs external pager choice to #version.
Others always use internal pagination so don't need to see that.
Also remove obsolete "command completion" option. I don't know
when it became unconditional but that was long enough ago to be
absent from the git log and from second cvs log included in that.
And streamline the RNG seed stuff a bit.
Reported directly to devteam, player observed that objects on the
floor had their bless/curse state change when reading a blessed
scroll of remove curse while confused. Message feedback mentioned
a silver saber being dropped. I didn't attempt to view the ttyrec
playbacks; what I'm sure happened was that the saber was secondary
weapon for dual wielding and had been uncursed; the confused remove
curse effect cursed it, which in turn caused it to be dropped. The
saber's 'next object' pointer became the [previous] top of the pile
at that spot and further object traversal intended to process the
rest of hero's inventory ended up processing floor objects there
instead.
This bug has been present for over 20 years (since 3.3.0 came out
in late 1999, when dual wielding was introduced and cursing of the
secondary weapon forced it to be dropped since making it become
welded was deemed to be too complicated) and never been reported.
Most likely players keep secondary weapons blessed so the scroll
effect doesn't touch them and simple object traversal sticks with
inventory. Or items at the spot have unknown BUC state so having
them be affected wouldn't be particularly noticeable.
Get rid of the last reference to 'g.restoring' (which managed to
unintentionally survive the change to 'g.program_state.restoring').
Also have suppress_map_output() check 'g.program_state.saving' and
switch the couple of checks against that flag to use the function.
Eliminate a couple of compile warnings produced when DEF_PAGER is
defined: unixmain.c: g.catmore=DEF_PAGER; wintty.c: fd=open(...).
Override its use when DLB is also defined since an external pager
could access 'license' but not 'history', 'opthelp', &c when those
are in the dlb container file.
In the commented out value for DEF_PAGER, show a viable value for
the default configuration these days.
Reported directly to devteam: monster vs monster location swapping
didn't handle single-segment long worms properly. Multi-segment
worms are disallowed but a worm with no visible segments (which
actually has 1 segment at the head's location) are allowed and the
segment wasn't being moved with the core monster and could trigger
warnings if sanity checking is enabled. The next time that the
worm moved, it got itself back in synch.
I couldn't reproduce the warning but mdisplacem() clearly assumed
that a long worm reporting 0 segments didn't have any so wasn't
attempting to handle the hidden one.
for helm of opposite alignment.
Discovered and described by vultur-cadens.
The #adjust command can be used to split an object stack and if the
shop price of the two halves are different, the new stack will have
its obj->o_id modified to make the prices the same. That could be
used to tip off the player as to what the low bits of the next o_id
will be. Since no time passes, no intervening activity such as
random creation of a new monster can take place, so the player could
wish for something that depends on o_id with some degree of control.
Matters mainly for helms of opposite alignment intended to be used
by neutral characters since the player isn't supposed to be able to
control that. (Other items like T-shirt slogan text and candy bar
wrapper text had a similar issue but controlling those wouldn't have
had any tangible difference on play.)
The issue writeup suggested allowing the player to specify a helm's
alignment during a wish. That would defeat the purpose of having
o_id affect the helm's behavior in an arbitrary but repeatable way
so is rejected.
I implemented this fix before seeing a followup comment that suggests
using a more sophisticated decision than 'obj->o_id % N' for the
arbitrary effect. This just increments context.ident for the next
obj->o_id or mon->m_id by 1 or 2 instead of always by 1 and should
be adequate. It also has the side-effect that two consecutive wishes
for helm of opposite alignment won't necessary give one for each of
the two possible 'polarities', even with no intervening activity by
monsters, reinforcing the lack of player control.
Minor bonus fix: it moves the incrementing check for wrap-to-0 into
a single place instead of replicating that half a dozen times. Ones
that should have been there for shop billing and for objects loaded
from bones files were missing.
Fixes#596
by the Riders that only damage the hero, not other monsters.
Noticed and diagnosed by vultur-cadens. Attacks dishing out the
damage type AD_DETH (Death), AD_FAMN (Famine), AD_PEST (Pestilence),
and also AD_DISE (Demogorgon, Juiblex, Scorpius) did 0 damage if
inflicted against monsters. That made Death harmless to other
monsters. The others have additional attacks (in the two Rider's
cases, only if both instances of their special attack hit on the
same move so that the second one is converted into a stun attack)
and could manage to kill other monsters.
The uhitm case can't happen. I suspect that the mhitm case was
originally intentionally omitted as something which won't happen,
but that's just a guess.
Fixes#594
Be able to run 'make Guidebook{|.txt|.ps}' in the doc subdirectory
without requring a full install first.
makedefs is no longer one of the first things built when building
nethack, and even if it was, nobody should have to rebuild all of
nethack after 'make spotless' when they just want to create one
or more of the formatted Guidebooks. The 'roff versions (.ps and
also .txt) use makedefs as a filter by default, so wouldn't build
if it wasn't there.
Have Makefile.doc build makedefs when needed.
Clean up some dependencies in Makefile.utl.
Add a couple of comments to Makefile.src.
Reported and diagnosed by entrez:
recalc_mapseen() has been looking at all slots in u.urooms[] rather
than stopping at the first \0. Since the whole array doesn't get
zeroed out when the value changes, stale room indices might follow
that first \0 and any rooms those referred to would erroneously get
flagged as having been visited by overview updates. Wouldn't matter
for the level where the stale indices got set, since you'd have to
have been in those rooms for it to happen, but would matter once you
moved on to other levels.
The 5 glyphs are now unaligned_altar, chaotic_altar, neutral_altar,
lawful_altar, and high_altar. The latter is only mapped if you are
on astral or sanctum levels.
Reported by entrez as a github issue for evilhack, 'gcc -2' issued
warnings about droppables() possibly returning the address of a local
variable (&dummy). It is mistaken; that never gets returned because
of various checks being performed. But making 'dummy' be static adds
negligible cost and should shut it up (not verified but no doubt
about viability...).
sp_lev.c: In function ‘lspo_altar’:
sp_lev.c:3962:9: warning: declaration of ‘shrine’ shadows a global
declaration [-Wshadow]
3962 | int shrine;
| ^~~~~~
In file included from ../include/hack.h:217,
from sp_lev.c:14:
../include/display.h:959:5: note: shadowed
declaration is here
959 | shrine
| ^~~~~~
If a bones file is created, any object-liking monster next to
where hero died has a chance of grabbing objects from hero's
inventory.
This comes from xNetHack by copperwater <aosdict@gmail.com>.
The gas will expand from its chosen center point via breadth-first
search instead of hardcoding a diagonal shape. The search is performed
with a randomized list of directions, and has 50% chance of not spreading
to a space it otherwise would have spread to. This has the effect of fuzzing
the cloud edges in open areas, helping the clouds on, for instance,
the Plane of Fire not be big rhombuses.
Also some other code refactoring related to stinking clouds in read.c
This comes from xNetHack by copperwater <aosdict@gmail.com>