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
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.
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.
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.
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.
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.
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.]
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.
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 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
For 'pauper', most roles start without any skills. However, strong
fighter types were starting with basic bare-handed combat/marital arts,
giving them a big advantage. Knights were starting with basic riding,
which is probably useless now that they have to acquire a saddle to
use it (see below). Take those initial skills away, producing an even
playing field.
Non-paupers get their initial skills without needing to spend any skill
credits (aka slots) on those. Give paupers 2 starting credits that
they'll be able to use once they acquire suitable weapons or spells
and train them up, so that once they're reasonably developed they won't
lag in skills compared to non-paupers.
Pauper knights were still starting with a saddled pony. Suppress the
saddle when the knight is a pauper.
Wizards start knowing a lot of spellbooks. Pauper wizards shouldn't.
Give most roles advance knowledge of one low level spell or other item.
It won't benefit them unless/until they find the corresponding item.
The previous is_shapeshifter() test meant that pets were still perfectly
willing to eat genetic engineer corpses and be polymorphed. Use
polyfood() instead.
Fixes#1183.
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
Pull request by mkuoppal: some objects which use the corpsenm field
to access the mons[] array can have a corpsenm value of NON_PM (-1)
and weren't avoiding array access in those cases.
In addition to a fixes entry for it, this makes some revisions to the
commited code, handling a few of the cases differently.
Closes#1175
TIN or EGG can have the corpsenm not assigned into a
proper monster and instead have an value of -1.
Take this into account in assigning the monster (fptr)
pointer to only point into >= LOW_PM monste entries.
Some players of 3.6 recently noticed that sometimes, mimics in shops
seemed to have moved around even before the player had entered the shop
or done anything to uncloak them. I found that this was because
finish_meating was being called for all non-eating monsters when
restoring a level (monsters that weren't eating anything would have
meating == 0 so always pass the 'imv > meating' check). This would
uncloak mimics -- but not all the time, because the 'mappearance != 0'
test meant mimics disguised as strange objects weren't uncloaked. I
think that was meant to be an additional check to confirm the monster
really did have a disguise, but in reality it meant that M_AP_OBJECT
"strange object", M_AP_MONSTER "giant ant", etc disguises wouldn't be
removed by finish_meating.
As it turns out, this was mostly fixed by coincidence in 221e4a7, which
fixed the "exclude actual mimics" check in finish_meating. So at this
point in 3.7 it's largely a latent bug, but it still had the potential
to improperly uncloak non-mimics who can disguise themselves (like the
Wizard of Yendor, maybe?), and could cause other problems if
finish_meating were updated to have additional effects, or if some
monster types were made to disguise themselves as a strange object when
eating a mimic.
When a pet ate a mimic corpse and started mimicking a monster,
and the user then used #wizmakemap, the sanity checker complained
about "non-mimic posing as foo" - the code was clearing the
pet eating counter, but didn't clear the appearance.
Simplify suppression of highlighting for menu header lines during end
of game disclosure. Didn't actually affect as many things as I was
expecting.
Plus a bit left out of the optfn_dogname() parsing commit.
From a reddit thread: a tame mind flayer ate Medusa's corpse and was
turned to stone. Pets won't eat cockatrice corpses unless stoning
resistant but would eat Medusa's corpse if merely poison resistant.
Mind flayers aren't normally poison resistant but could be if wearing
green dragon scales/mail.
Augment the touch_petrifies() test when classifying food for pets.
Player reported that a cat which was asleep in a treasure zoo would
catch and eat thrown food, becoming tamed in the process, without
waking up.
Temporary sleep was handled (via checking for !mon->mcanmove) but
indefinite sleep wasn't. Make a monster hit by taming effect (either
magic or thrown food) wake up from indefinite sleep. For temporary
sleep, the remaining duration is cut in half. The timeout isn't
dropped all the way to 0 because a monster with mon->mfrozen set
isn't necessarily asleep. That timeout is shared by timed sleep,
timed paralysis, and busy time when donning armor.