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>.
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.
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.
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.
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.
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).
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.
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/
Changes to setuhpmax() a couple of days ago to deal with sanity_check
for "current hero health as monster better than maximum" ended up
triggering sanity_check about "current hero health better than maximum"
when gaining experience level(s) while polymorphed.
Prayer result of fix very low HP. Tricky because when poly'd,
prayer operates on both u.mhmax and regular u.uhpmax but setuhpmax()
only operates on one of the two depending on Upolyd.
The g? structs had a mix of variables that were written to
the savefile, and those that were not.
For better clarity and to distinguish those that end up in
the savefile, relocate some g? variables that get written
directly to the savefile into different structs.
This updates EDITLEVEL, although technically it probably
didn't need to, since savefile contents are not changing.
Details:
gb.bases -> svb.bases
gb.bbubbles -> svb.bbubbles
gb.branches -> svb.branches
gc.context -> svc.context
gd.disco -> svd.disco
gd.dndest -> svd.dndest
gd.doors -> svd.doors
gd.doors_alloc -> svd.doors_alloc
gd.dungeon_topology -> svd.dungeon_topology
gd.dungeons -> svd.dungeons
ge.exclusion_zones -> sve.exclusion_zones
gh.hackpid -> svh.hackpid
gi.inv_pos -> svi.inv_pos
gk.killer -> svk.killer
gl.lastseentyp -> svl.lastseentyp
gl.level -> svl.level
gl.level_info -> svl.level_info
gm.mapseenchn -> svm.mapseenchn
gm.moves -> svm.moves
gm.mvitals -> svm.mvitals
gn.n_dgns -> svn.n_dgns
gn.n_regions -> svn.n_regions
gn.nroom -> svn.nroom
go.oracle_cnt -> svo.oracle_cnt
gp.pl_character -> svp.pl_character
gp.pl_fruit -> svp.pl_fruit
gp.plname -> svp.plname
gp.program_state -> svp.program_state
gq.quest_status -> svq.quest_status
gr.rooms -> svr.rooms
gs.sp_levchn -> svs.sp_levchn
gs.spl_book -> svs.spl_book
gt.timer_id -> svt.timer_id
gt.tune -> svt.tune
gu.updest -> svu.updest
gx.xmax -> svx.xmax
gx.xmin -> svx.xmin
gy.ymax -> svy.ymax
gy.ymin -> svy.ymin
Related note:
There are some pointer variables that are heads of chains that were not
moved from 'g?' to 'sv?', because they are not actually written to the
savefile directly, but the objects/monst/trap/lightsource/timer in the
chains they point to are. That can be changed, if desired.
Examples: gi.invent, gm.migrating_objs, gb.billobjs, gm.migrating_mons,
gf.ftrap, gl.light_base, gt.timer_base
When you attempt #offer at a non-altar location, the feedback
"you are not standing on an altar" made it sound as if you wouldn't
be able to #offer if you were levitating. Since that is possible,
change the feedback.
In 3.6.2 parts of the wakeup code were merged together, and this
caused pets consider any noise made by the hero - such as hitting
iron bars or digging - as whistling for them to come to the hero.
Change it to only consider actual whistling and ringing a bell.
This will increase the potential challenge involved in donning a helm of
opposite alignment on Astral, now that it confuses the hero -- it means
that you must spend at least a turn after putting on the helm clearing
the confusion. Since putting on the helm of opposite alignment may also
summon erinyes, this hopefully turns it from a situation where it's a
no-brainer to immediately ditch them by #offering the Amulet of Yendor
into one where the player must actually stop and think a little bit.
After the drawbridge was destroyed, playing an instrument on the castle
level while knowing the tune continued to offer a chance to play it.
Then nothing interesting happened even if you were close enough to the
former bridge for it to have been useful prior to the destruction.
I think the hero could also be given the tune as a divine prayer boon
after bridge destruction but I didn't verify that. The player might
not know that the tune is no good anymore, but the hero's patron deity
should.
This is for completely destroying an altar with extra-powerful magical
digging -- the normal altar_wrath() punishment didn't seem sufficient
for such an outrage to me, so skip straight to slinging the lightning
bolts. Destroying an altar is unlikely to happen by accident (though
it's possible with poorly timed usage of a drum of earthquake).