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.
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
"human", "dwarf", "elf", "gnome", and "orc" are all flagged M2_NOPOLY;
so is "giant". But dwarf and gnome are ordinary monsters and should
be eligible to be polymorph targets, so take the no-poly flag off of
them. The others are used for corpses and not intended to be distinct
monsters. But they are reasonable polymorph targets, so if player
with control picks any of them, choose a substitute. The exception is
human, which already has special poly-self handling.
A very small number of relatively recent macro definitions got
placed in mondata.h, even though they had a 'struct monst *'
argument, rather than a 'struct permonst *' argument.
Relocate the macros with the 'struct monst *' argument to
include/monst.h, leaving the macros with the 'struct permonst *'
argument in include/mondata.h.
This is useful for debugging and it allows the index
to be used directly instead of calculated in a
monsndx() function, which has been removed.
I left monsndx() in as a simple short-hand macro for the value
and didn't change the use cases, the reasoning being that this:
monsndx(mon->data)
is arguably a little easier on the eyes than:
mon->data->pmidx
LOW_PM, NON_PM, SPECIAL_PM have been included in the 'enum monnums'
now, instead of as individual macro definitions.
I chose to add the pmidx field as an instance of the enum declaration,
because that has very advantageous results in some debuggers, where it is
then shown as:
pmidx PM_GRAND_MASTER (349) monnums
instead of the less-informative:
pmidx 349 int
Adding the element count to the extern declaration for mons from:
'extern struct permonst *mons[];'
to the more specific declaration to that in src/monst.c:
'extern struct permonst *mons[NUMMONS + 1];'
then allows navigation through the mons array in one of the debuggers.
... unless explicitly specified to generate at a specific point or
within a specific area. But if they are permitted to generate anywhere
on the level, and it contains water, they always end up in the water. I
noticed this when trying to explicitly specify ghouls to generate
anywhere on a level with a minimal amount of water.
This was due to the definition of "amphibious" being conflated with
"breathless", such that all breathless monsters counted as amphibious.
There are plenty of breathless monsters in the game that decidedly don't
normally inhabit water, such as undead, but they would pass the
amphibious() check in pm_to_humidity and thus the game decides that they
must generate in wet terrain if there is any available.
This fix takes the approach of changing amphibious() so that it no
longer checks the M1_BREATHLESS flag and only considers M1_AMPHIBIOUS,
then updating the places where amphibious() and Amphibious are used
accordingly. I also added a new macro cant_drown() which wraps up
swimming, amphibiousness, and breathlessness because these three things
are frequently checked together in the context of whether something
should drown.
Places where amphibious() or Amphibious did NOT have an extra
breathless() or Breathless check added on, and thus where behavior has
been changed:
- The pm_to_humidity function (to fix the bug).
- Player vs water in goodpos; it didn't seem like being polymorphed into
a breathless non-amphibious monster should make it fair game to
randomly teleport into water even though it's technically safe.
- Awarding extra experience when killing an eel. (So the hero will get
the extra experience if they are polymorphed into a breathless
non-amphibious monster and don't have magical breathing. Very much an
edge case.)
The consolidation of global variables from scattered source
files into decl.c and declared in decl.h was begun in 3.7.0.
Their placement in common files was done for centralized
initialization and potential re-initialization during a
"play again" scenario.
It wasn't really necessary for all of them to be housed in a
single huge structure to meet the "play again" requirement,
and the single huge structure has been a little unwieldy when
it comes to maintenance.
Following this commit, instead of one single extremely large structure
named 'g' to house all of the relocated global variables, they
are distributed into several ga through gz.
To make things easy for the developer, each variable is placed
into the struct corresponding to the starting letter of the variable.
That way, no lookup is required in order to know which struct houses
a particular variable, it is a simple match to the starting letter
for all the centralized global variables.
A global variable named 'amulets', would be found in ga.
ga.amulets
^ ^
A global varable named 'move', would be found in gm.
gm.moves
^ ^
A global variable named 'val_for_n_or_more' would be found in gv.
gv.val_for_n_or_more
^ ^
A global variable named 'youmonst' would be found in gy.
gy.youmonst
^ ^
Reverts 690e072, which changed the various is_foo macros from this:
| #define is_elf(ptr) ((((ptr)->mflags2 & M2_ELF) != 0L)
to this:
| #define is_elf(ptr) ((((ptr)->mflags2 & M2_ELF) != 0L) \
| || ((ptr) == g.youmonst.data && \
| !Upolyd && Race_if(PM_ELF)))
This is a problem because g.youmonst.data is not unique to the hero:
the '(ptr) == g.youmonst.data' test will also be true of all player
monsters of the same role. For this reason, any of those player
monsters will be treated as sharing the hero's race, producing strange
results. For example, if the player is an elven ranger, any ranger
player monster generated will be considered 'elven' too (so will get a
to-hit bonus when attacking orcs, etc) -- but only while the hero is
unpolymorphed.
There are already other ways of checking the hero's race in addition to
her current polyform, most notably the maybe_polyd() macro. maybe_polyd
or something similar is already used in nearly all the cases where the
hero's race is being evaluated, meaning Race_if gets used instead when
the hero is in her natural form. So I think the check of the hero's
race in is_foo had very little effect except for the unintended
side-effects on player monsters.
In reviewing all the uses of is_{elf,dwarf,gnome,orc,human}, I noticed
only one case that relied on the hero-race-checking behavior. That has
been changed in this commit to use maybe_polyd (there's another 'raw'
is_human(g.youmonst.data) a few lines down, but it doesn't need
maybe_polyd since it already distinguishes between 'hero in nonhuman
polyform' vs 'nonpolyd or human polyform'). same_race(mondata.c) is
another case where &g.youmonst.data can be passed to is_foo, but
everywhere that calls it for the hero also calls your_race() or
same_race(&mons[Race_switch]) to handle the racial case.
Use verbiage for mon vs mon and hero (mostly hero) engulf attacks that
matches recent changes to monster vs hero engulf attacks more closely
(e.g. "swallows whole" instead of "engulfs" for purple worm, other
changes in b07fe59...). Also ensure non-AD_DGST engulf attacks
(e.g. from revamped trapper or lurker above polyforms) aren't treated as
"eating" (or as involving "debris").
Also change the enfolds and digests macros so they produce booleans
rather than attack pointers (I got a compiler warning about casting
struct attack * to boolean when I did 'boolean b = digests(ptr);').
Change trappers and lurkers above to remove digestion damage. They
fold themselves around rather than swallow the victim. There were
are lot of places that assumed that an engulfer which is an animal
would swallow and digest the victim. In hindsight, it might have
been simpler to take the M1_ANIMAL flag off of trappers and lurkers
above.
This adds a new digests() predicate for creatures with AT_ENGL+AD_DGST
(purple worm) and also enfolds() for AT_ENGL+AD_WRAP (both 't'-class
critters).
There are several minor fixes mixed in with this. I didn't record
them as I went along but the two I remember are
1) if poly'd into a holder and holding on to a monster, the '<' and
'>' commands refursed to work; release the held creature first
and then treat those commands as normal;
2) throwing a non-weapon while engulfed by an ochre jelly reported
"the <item> vanishes into the ochre jelly's /currents/".
This needs a lot more testing. I found and fixed multiple minor
details before my own testing burned out.
The grounded() macro wasn't fully handling is_clinger(). Not sure
what impact this fix will have.
Add ability to cling to the ceiling to enlightenment feedback. If
it gets fixed up to a state where it is useable while polymorphed,
some or all of it should be moved to non-magic ^X feedback.
In the name of accessibility: Prevent moving into dangerous liquids.
Now with themed rooms, water and lava are more common, and it's
unreasonable to expect blind players to check every step for those.
With paranoid:swim, just prevent normal walking into those liquids,
unless you prefix the movement with 'm', or if the liquid would not
harm you.
Doesn't completely prevent an accidental dunking - for example
if the hero is impaired or couldn't see the liquid.
This comes from xNetHack by copperwater <aosdict@gmail.com>
with some changes to the code.
Implement the suggestion that falling rock traps and rolling boulder
traps be harmless to xorns. I've extended that to all missiles made
of stone (rocks, gems, boulders, a handful of other things that will
only matter if poly'd hero throws in '<' direction or is hit by stuff
scattered by an explosion).
I excluded ghosts because they would become even harder to kill and
the missile handling would need extra checks to test for blessed objs.
Add two new monsters and two new objects:
gold dragon
baby gold dragon
gold dragon scale mail
set of gold dragon scales
A couple of variants seem to have added these already, but this came
off my ancient list of monsters to add and was done from scratch.
It's a clone of silver dragon, but instead of having reflection and
breathing cold, a gold dragon emits light and breathes fire; because
of the latter it can be seen with infravision like a red dragon.
Adult gold dragons are lawful as in the AD&D Monster Manual rather
than chaotic as the wiki pages show for the variant versions.
Worn gold dragon scales operate similar to wielded Sunsword: when
blessed, radius is 3 (same as a lamp), if uncursed, radius is 2, and
if cursed, radius is 1 (but functions as 2 when worn by the hero,
otherwise there would be no tangible effect). Gold dragon scale mail
gets an extra +1, making blessed gold DSM have a bigger radius than
lamps. Embedded scales have radius 1 regardless of BUC state; light
for that case comes from the gold dragon monster form the hero is in.
When not worn, gold scales and scale-mail don't emit any light.
The tiles use a mix of yellow (for gold) and red. The two object
tiles seem reasonable variations of the corresponding silver dragon
ones. The two monster tiles definitely need work since the silver
ones were mostly cyan and changing that to red did not produce very
good result; subsequent attempt at a mixture was haphazard at best.
This tries to fix the problem of the extra message when a tame
golem is completely destroyed (paper or straw golem burned, iron
golem rusted, wood or leather golem rotted) being issued at odd
times. I basically punted on the visibility aspect since the
original logic was strange: you had to be able to see both the
attacker's and defender's spots and at least one of those two
monsters. Now mon-attacks-mon visibility requires that you be
able to see one of the two and if you don't see both, the unseen
one will be referred to as "it". The "may the iron golem rust
in peace" message is independent of that and may be displayed
after "you have a sad feeling", but now that's intentional and
will refer to an unseen pet by name or monster type, not "it".
This needs a lot of testing and hasn't attempted to address
issue #402: only some attacks that should compeletely destroy
a golem actually do so. (So a hit by fire elemental against a
paper golem does, but passive fire counterattack when a paper
golem hits a fire elemental doesn't, nor does a wand of fire
or being hit by Firebrand.)
Fixes#401
Reject arrows and darts as candidates for wielding two weapons at
once.
Make the check for being able to two-weapon when polymorphed be more
robust. Instead of just testing whether the monster form's second
atttack is a weapon attack and then assuming that the first one is
too, test the first three to validate that at least two of those are
AT_WEAP. The existing code works but seemed fragile.
Those tests were only checking the permonst mflags2 field
so anytime those tests were used in the code, they came
up false for things like an elven ranger. An elven ranger
should return true for an is_elf() test, as an example.
That happens because the profession monsters in monst.c
are defined with M2_HUMAN.
This augments those is_*() race tests to also check for a
matching player race as well.
Generally, fish should lay their eggs in the water and
not on land, but the game was only allowing the opposite.
Eels are catadromous and lay their eggs in the Sargasso Sea,
not in the dungeon.
Fixes#177
The monst struct has 'mintrinsics' field which attempts to handle
both mon->data->mresists and extrinsics supplied by worn armor, but
polymorph/shape-change was clobbering the extrinsics side of things.
Potentially fixing that by changing newcham() to use set_mon_data(...,1)
instead of (...,0) solved that but exposed two other bugs. Intrinsics
from the old form carried over to the new form along with extrinsics
from worn armor, and update_mon_intrinsics() for armor being destroyed
or dropped only worked as intended if the armor->owornmask was cleared
beforehand--some places were clearing it after, so extrinsics from worn
gear could persist even after that gear was gone.
So, fixing the set_mon_data() call in newcham() was a no go. This
fixes update_mon_intrinsics() and adopts the suggested code from
github pull request #177 to have mon->mintrinsics only handle worn
gear instead of trying to overload innate intrinsics with that. This
is a superset of that; the flag argument to set_mon_data() is gone
and mon->mintrinsics has been renamed mon->mextrinsics. (The routine
update_mon_intrinsics() ought to be renamed too, but I didn't do that.)
I did much of this quite some time ago, as prequisite for a different
bug report about monsters vs shades, then set it aside. It ended up
being more complicated than I anticipated.
When deciding whether various non-weapon attacks might hit a shade,
hmonas() was not checking for blessed or silver armor that should have
been applicable. It did check boots when kicking, but not gloves or
rings (when no gloves) when touching, or outermost of cloak/suit/shirt
when hugging, or helmet when head-butting. (The last one is actually
moot because nothing with a head-butt attack is able to wear a helm.)
The problem was more general than just whether attacks might hit and
hurt shades. Various undead and/or demons are also affected by blessed
and/or silver attack but weren't for non-weapon attacks by poly'd hero.
At least two unrelated bugs are fixed: a rope golem's AT_HUGS attack
gives feedback about choking but was fully effective against monsters
which fail the can_be_strangled() test. And it was possible to hug a
long worm's tail, rendering the entire worm immobile.
The report also suggested that all artifacts be able to hit shades for
full effect, but by the time shades are encountered everyone has an
artifact so that would nullify a shade's most interesting ability.
TODO: monster against hero and monster against other monster need to
have similar changes.
Take a first step towards making the mons[] array be readonly.
The only other place that updates it is when changing succubus and
incubus AD_SSEX attacks to AD_SEDU ones and that can be handled
via existing getmattk(), but so far has proven to be trickier than
anticipated.
The original report complained that gremlins seemed impervious to
Sunsword's light yet a flash from a camera caused them to cry out in pain
despite "The long sword named Sunsword begins to shine brilliantly!"
This commit does two things:
1. A dmg bonus is applied against gremlins using a lit Sunsword.
2. Gremlins will generally avoid the light emitted by Sunsword.
There's a few minor flavor bits thrown in also.
It is understood that this effectively makes Sunsword provide
"gremlin-proofing", but the gremlin myth and Sunsword's characteristic
feature pretty much demand it.
bug 42
When a monster killed a paper golem with a fire attack, the player was
told that the golem "burns completely" yet it might still leave some
blank scrolls as 'corpse'. The fix for that was one-line, but several
other death-by-fire situations which didn't report "burns completely"
were also leaving scrolls: fireball spell or scroll of fire or other
fire explosions (if any), also wand of fire. Fire trap and poly'd
hero with fire attack were already suppressing 'corpse'.
It seems to me that the reaction to "you feel dead inside" when you're
polymorphed into an undead creature at the time would be "so what else
is new?". Vary the "dead" when current form is something which gets
reported as "destroyed" rather than "killed" when killed. That happens
for things flagged as non-living. Now undead "feel condemned inside"
and golems "feel empty inside". Neither of those are ideal but they're
more interesting than "feel dead inside".
After becoming dead inside, give a reminder about that during
enlightenment and if you restore a saved game in that condition. It
was the latter that set this in motion: I wanted to confirm that
restoring with u.uhp == -1 didn't give "you aren't healthy enough to
survive restoration" when polymorphed. (It doesn't; the game resumes
and you'll die if/when you rehumanize.)
My old monkey patch was a bit more extensive than Pasi's, although
it didn't originally include letting apes be tameable with bananas.
No sense in throwing it away:
1) Make monkeys and apes be omnivores instead of carnivores.
2) Make bananas be preferred food for herbivore/omnivore subset
of Y-class, so excludes carniverous ape, owlbear, and yeti.
[Sasquatch remain omnivorous but aren't tameable with bananas.]
3) While updating befriend_with_food(), make horses be affected
only by food they might eat, not by meat and corpses and tins.
So they'll be somewhat harder to cope with for characters not
strong enough to kill them. [Dogs and cats are unchanged.]
Not included (not even implemented...):
0) Allow archeologists to choose monkey for starting pet.
[The one in "Raiders of the Lost Ark" didn't actually belong
to Indiana Jones but spent a lot of time accompanying him.]
Bananas are quite rare, and none of the simians are very strong,
so this won't be in any way overpowered - just a bit of flavor.
I think this was originally Derek's change from Sporkhack, but
similar ones have been done in the community multiple times
in the past.
From Boudewijn:
> y a light (tame yellow light called Snertkat) [seen: normal vision,
> infravision]
> Snertkat deliberately jumps onto a polymorph trap!
So make yellow and black lights floaters.
I did my best to exempt some of the bigger aligned blocks from the reformatting
using the /* clang-format off */ and /* clang-format on */ tags. Probably some
that shouldn't have been formatted were anyway; if you encounter them, please
fix.
The clang-format tags were left in on the basis that it's much easier to prune
those out later than to put them back in, and it means that, modulo my custom
version of clang-format, I should be able to run clang-format on the source tree
again without changing anything, now that Pat has fixed the VA_DECL issues.