A monster may be unhidden if it's caught in a trap, but maybe_unhide_at
was checking mtmp->mtrapped across the board, which wouldn't work for
the hero. Use u.utrap instead under those circumstances. Also refactor
a bit so it shouldn't need the repeated guards against mtmp being null.
maybe_unhide_at tried to handle both a monster and hero, but
hero being hidden is in u.uundetected flag, and the code was
only checking the monster mundetected field.
The code should probably be changed, either to change all uses
of the u.uundetected to gy.youmonster.mundetected, or perhaps
use a macro ... but these changes are all too big for me
to tackle for now.
sound_verbal(char *text, int32_t gender, int32_t tone, int32_t vol,
int32_t moreinfo);
-- NetHack will call this function when it wants to pass text of
spoken language by a character or creature within the game.
-- text is a transcript of what has been spoken.
-- gender indicates MALE or FEMALE sounding voice.
-- tone indicates the tone of the voice.
-- vol is the volume (1% - 100%) for the sound.
-- moreinfo is used to provide additional information to the soundlib.
-- there may be some accessibility uses for this function.
It may be useful for accessibility purposes too.
A preliminary implementation has been attempted for macsound to test
the interface on macOS. No tinkering of the voices has been done.
Use of the test implementation requires the following at build time with make.
WANT_SPEECH=1
That needs to be included on the make command line to enable the test code,
otherwise just the interface update is compiled in.
I don't know for certain when AVSpeechSynthesizer went into macOS, but older versions
likely don't support it, and would just leave off the WANT_SPEECH=1.
If built with WANT_SPEECH=1, the 'voices' NetHack option needs to be enabled.
It was a bit strange, when I first started up the test, to hear Asidonhopo,
the shopkeeper, talking to me as I entered his shop and interacted with him.
Add "walls of lava", basically lava which blocks vision and
require a bit more than just levitation or flight to move through.
No levels use this yet, as testing isn't thorough enough.
sanity_check feedback which occurred after using locking magic to
set off a bear trap at the location of a monster hiding under an
object.
Trivial bit: a recent change made stunning via knockback only occur
when not already stunned but was still adding the current stun time
to the new stun time even though current stun is now always zero.
Several formatting bits included.
While fuzzing, I saw a sanity checking error complaining about
a ceiling hider being on top of a pool; the rock piercer was
teleported on top of the pool while it was hiding in the ceiling.
Try to be a bit more consistent when a monster is hiding in ceiling,
and if it's valid for it to be on a pool.
Clean up some of the code for monster deciding what objects
to pick up, removing duplicate code. There should be no real
difference in behaviour, other than monsters now can pick up
one stack of items at a time; previously monster could pick up
gold, then a practical item, followed by a magical item all
in a single turn, although this very rarely mattered.
Not extensively tested.
Code originally from NetHack4.
My recent change to hit and wake monsters caused a recursive
ghod_hitsu -> wakeup -> dobuzz -> buzz -> ghod_hitsu loop.
Don't call the ghod_hitsu again if the priest is already angry.
Unless you kill the monster with one hit, it'll wake up
cranky and make noise - waking up other sleeping monsters.
This was a bit tricky with all the message sequencing; I tested
all the hit/throw/fire/zap combos I could think of, and it took
a while to get things looking right.
This code was in three different places; pet eating,
monster eating metal, and monster eating other objects.
Other than very minor changes (eg. rustproofing completely
protects pets from bad effects, rustproof items are no longer
giving apport, and monsters eating corpses are healed), it
should behave the same as before... But I haven't exhaustively
gone through every iteration.
Insert the calls to trigger a number of potential soundeffects
into the core.
If no additional soundlib support is integrated into the
build, then the Soundeffect macro (sndprocs.h) expands to nothing:
[#define Soundeffect(seid, vol)
]
If, however, at least one additional soundlib support is integrated
into the build, then the Soundeffect macro gets defined as this
in sndprocs.h:
[#define Soundeffect(seid, vol) \
do { \
if (!Deaf && soundprocs.sound_soundeffect \
&& ((soundprocs.sndcap & SNDCAP_SOUNDEFFECTS) != 0)) \
(*soundprocs.sound_soundeffect)(emptystr, (seid), (vol)); \
} while(0)
]
That macro definition checks for the hero not being Deaf; it checks
to ensure that the active soundlib interface has a non-null
sound_soundeffect() function pointer; and it checks to ensure
that the active soundlib interface has declared that it supports
soundeffects by setting the SNDCAP_SOUNDEFFECTS bit in its sndcap
entry. That just means that the interface routines are prepared to
accept and deal with the calls from the core, whether or not it
actually produces the desired soundeffect.
Replace FIRST_GEM and LAST_GEM with FIRST_REAL_GEM, LAST_REAL_GEM,
FIRST_GLASS_GEM, and LAST_GLASS_GEM and define those along with
objects[] rather than separately. Do the latter for FIRST_AMULET
and LAST_AMULET too. Also new FIRST_SPELL and LAST_SPELL used to
compute MAXSPELLS. (That value looks wrong to me, but this defines
it with the same value as before. If it gets fixed, EDITLEVEL will
need to be incremented.)
This started as just proof of concept that extra information could
be collected as objects[] gets initialized at compile time.
Reported by entrez: if a trap killed hero's steed and dismounting
was fatal for the hero (probably by falling onto the same trap),
impossible "dmonsfree: 1 removed doesn't match 0 pending" warning
occurred during game-over cleanup.
Move the dismount calls in mondead() and mongone() from before their
m_detach() call to the end of m_detach() itself. This led to a
cascade of problems and attempted fixes until finally zeroing in on
place_monster()'s sanity checks and dismount_steed()'s attempts to
work-around one of them.
This reverts the convoluted hack from four years ago in commit
be327d9822 and deals with the issue in
a simpler way. After that, the new dismount_steed() placement at
end of m_detach() works cleanly.
If you were on a level teleporter, the spoteffects() call after
the hero gets expelled could end up going to a new level and
freeing all the monst chains from the level you were originally
engulfed on.
#0 0xba0507 in free
#1 0x87feda in dealloc_monst src/mon.c:2369
#2 0x880a02 in dmonsfree src/mon.c:2194
#3 0x9a7aa2 in savelev_core src/save.c:507
#4 0x9a7a21 in savelev src/save.c:466
#5 0x71eb9d in goto_level src/do.c:1483
#6 0x71833f in deferred_goto src/do.c:1903
#7 0xa2533f in level_tele src/teleport.c:1117
#8 0xa2567b in level_tele_trap src/teleport.c:1198
#9 0xa5c007 in trapeffect_level_telep src/trap.c:1861
#10 0xa5f856 in trapeffect_selector src/trap.c:2497
#11 0xa47497 in dotrap src/trap.c:2586
#12 0x7d669b in spoteffects src/hack.c:2859
#13 0x89d495 in xkilled src/mon.c:3187
The latter parts of xkilled() after the spoteffects() call would
then attempt to dereference the free'd monst pointer.
Save a copy of the monst struct prior to spoteffects() if you were
expelled, then point at the reference copy afterwards.
Resolves#938
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
^ ^
Short for distu(mtmp->mx, mtmp->my) (i.e. the distance between the hero
and the specified monster), which is a very common use of distu(). The
idea is that this would be a convenient shorthand for it; I actually
thought it (or something very similar) existed already, but couldn't
find it when I tried to use it earlier. Based on the number of uses of
fully-spelled-out 'distu(mtmp->mx, mtmp->my)' replaced in this commit
I'm guessing I just imagined it.
Instead of using index() macro defined to strchr, use C99 strchr.
Instead of using rindex() macro defined to strrchr, use C99 strrchr.
If you want to try building on a platform that doesn't offer those
two functions, these are available:
define NOT_C99 /* to make some non-C99 code available */
define NEED_INDEX /* to define a macro for index() */
define NEED_RINDX /* to define a macro for rindex() */
Reported by Umbire:
|You kill SpaceMannSpiff! SpaceMannSpiff puts on a dwarvish cloak.
|SpaceMannSpiff puts on a dwarvish iron helm.
|The seemingly dead SpaceMannSpiff suddenly transforms and rises as
| a Vampire.
This was tough to reproduce but I finally managed it. The issue
text mentions that it was fixed by copperwater in xNetHack with
commit 8c4af50f0aa3e72522f3eb98df039ff25c2a1ea0 to the repository
for that variant. My attempt to cherry-pick that failed--I'm not
even sure whether it should have been expected to work--and some of
the code has been impinged upon by changes, so I ended up applying
the contents of that commit manually.
The commit changes how/when monsters put on new armor rather than
anything directly related to vampires. Circumstances similar to
the example above now yield:
|You kill SpaceMannSpiff!
|The seemingly dead SpaceMannSpiff suddenly transforms and rises as
| a Vampire.
on one turn, then on the next turn the revived vampire produces:
|SpaceMannSpiff puts on a dwarvish cloak.
My test case only had one item of interest; I assume that the second
item of armor gets worn on a subsequent turn rather than at the same
time as the first one.
Fixes#843
The pull request included some changes that were neither accidental nor
unintentional, so only a subset of the changes from pull request #869
submitted by klorpa were manually applied.
behaviour -> behavior
speach -> speech
knowlege -> knowledge
incrments -> increments
stethscope -> stethoscope
staiway -> stairway
arifact -> artifact
extracing -> extracting
The uses of "iff" were left alone.
Close#869
Apply the patch from entrez that makes pet gelatinous cubes who eat
containers engulf rather than digest the contents, like non-tame
g.cubes. Unlike the latter, tame ones will immediately drop the
stuff they just engulfed and might subsequently eat it all anyway.
Make sure u.uswallow is cleared when u.ustuck gets set to Null so
that they won't be out of sync with each other. Having u.uswallow
be non-zero does imply that u.ustuck is non-Null.
Running #panic while swallowed didn't produce any anomalies for me,
either before or after this change.
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 monster knockback could mess with the monster linked list while
the code was going through it for monster movements. (For example,
a monster knocked back another into a level teleport trap)
Add iter_mons_safe, which first grabs all the monster pointers in
the list into an array, and goes over that array instead of relying
on the "next monster" pointer. This is possible because dead monsters
are not removed from the linked list until after all the monsters
have moved.
Testing is very minimal, and I'm not sure the vault guard check
for migration is correct - it should probably check for more states?
Also the iterator could be improved by not continually allocating
and freeing the monster pointer array.
Reported by copperwater: if an engulfer swallowed a mounted hero,
odd things could happen if the hero dismounted. The steed would be
silently expelled and float-down flooreffects were attempted.
It turns out that if the engulfer is classified as an animal (so
purple worm, lurker above, trapper), the hero got "plucked from
<steed>'s saddle" and was forcibly dismounted prior to completing
the engulf operation, but non-animals (vortices, air elemental,
ocher jelly, Juiblex) swallowed the hero+steed intact. The most
straightforward fix to dismounting-while-engulfed issues is to change
engulfing to always pluck the hero from the saddle even when the
engulfer isn't an animal.
If there's no room on the level to place the former steed, it gets
killed off. I looked at changing that to put the steed into limbo,
waiting to migrate back to the current level if hero leaves and
subsequently returns, but that breaks movemon()'s assumption that
when monsters are in the process of moving, only the currently moving
one can be taken off the fmon list to be placed on migrating_mons.
[The recently added monster knockback code violates that assumption
too when knocking the victim into a level changer trap. It needs to
be fixed in one fashion or another.]
Pull request from entrez: don't allow an amorphous engulfer who
has swallowed the hero to move to a closed door location. If some
hypothetical amorphous holder existed, it could move to such a spot
while holding the hero adjacent.
Closes#821
An amorphous engulfer like a fog cloud could engulf the hero, then carry
him into a closed door. If it was killed or decided to spit out the
hero, he would be left occupying the same spot as a closed/locked door.
Make an amorphous monster unable to move into a door if currently
engulfing the hero.
Something more complicated could be done along the lines of allowing the
move if the hero is himself in an amorphous polyform, but that verges on
being a little too silly, maybe.
I also included fixes to a couple miscellaneous, unrelated formatting
issues that I noticed recently.
The bookkeeping for number of dead or removed monsters got out of sync
if a shopkeeper, temple priest, or vault guard on the migrating_mons
list who was scheduled to return to the current level got passed to
mongone() when #wizmakemap destroyed the current level in order to
replace it.
When getting rid of such monsters, first put them on fmon. Once 'gone'
they'll still be on that list and dmonsfree() will include them in the
tally of dead or removed monsters and hopefully the count will match
the number it thinks were pending.
This works for a vault guard; I didn't try for shopkeeper or priest.
Ditch pet(s) so that they won't kill stuff and open up map spots while
you're waiting for guard activity; enter vault away from the wall
nearest to 'civilization'; fill the level with lichens or other junk;
wait for guard to arrive; don't drop gold. After a short while the
guard will try to teleport next to you but with the level full will
end up in limbo instead (migrating, scheduled to come back to current
level). Then perform #wizmakemap. Prior to this patch, there will be
an impossible about N removed not matching N+1 pending; after it, no
such impossible.
place_monster() sanity check complained that a long worm was being
put at the same location as another monster. The long worm wasn't
on the map prior to that place attempt. This probably fixes it but
I don't a test case so am not sure.