Juiblex could expel the hero over water, they'd get relocated to
a safe location, but Juiblex was still thinking they were at
the location it expelled them.
Adds a new boolean option, spot_monsters. If on, every time
the hero notices a monster which was out of sight before,
a message is given. Combine with accessiblemsg to get the
monster location:
(3north): You see a newt.
Breaks saves and bones.
Consistent with their mythological role of punishing those who had
violated societal taboos -- oathbreakers, hosts who attacked their
guests, etc -- erinyes scale with the cumulative amount of alignment
abuse the hero has committed over the course of the game. This is
tracked separately from the alignment record, and cannot be cleared by
the hero improving her favor with her god via "good deeds" as the normal
alignment record can. Erinyes will gain abilities, levels, and attacks
as the hero's alignment abuse worsens. They will also aggravate
monsters when near the hero.
Two variations:
IndexOk(idx, array) validate that idx is a valid index into the array
IndexOkT(idx, array) validate that idx is a valid index into the
array, excluding the final Terminator element
monmove.c: In function ‘postmov’:
monmove.c:1391:65: warning: suggest braces around empty body in an ‘if’ statement [-Wempty-body]
1391 | Soundeffect(se_door_unlock_and_open, 50);
| ^
monmove.c:1410:55: warning: suggest braces around empty body in an ‘if’ statement [-Wempty-body]
1410 | Soundeffect(se_door_open, 100);
| ^
monmove.c:1435:60: warning: suggest braces around empty body in an ‘if’ statement [-Wempty-body]
1435 | Soundeffect(se_door_crash_open, 50);
Remove a stale comment and update one or two others.
Remove several trailing spaces.
Change the data type of a couple of variables from schar to int and a
couple others from int to coordxy.
Redo a nested 'if' sequence to un-nest; results in a bloated diff due
to reducing indentation for a big chunk of code.
Change monster movement to use u_on_newpos() when swallowed hero's
location moves along with engulfer so that a clipped map will be kept
up to date.
Fuzzer encountered "m_detach: monster already detached?"
A monster hit a black pudding that split. The clone was
created on top of a rolling boulder trap, which triggered,
the boulder hit the original black pudding, and killed it.
The dead pudding then retaliated (as the code didn't check
if it was dead) and a passive attack of the other monster
tried to kill the already dead pudding.
I think one of these checks would be enough, but adding the
DEADMONSTER check just in case.
Issue reported by vultur-cadens: tame monsters capable of using items
would pick up cursed ones and even wear cursed armor.
The report cites commit 6c9700ab25 but
I don't see any reason why it would be the cause. However, I was able
to reproduce the misbehavior and this commit seems to fix it.
Fixes#1072
Reported by Umbire: if a statue of a hider-under was activated by
a statue trap, it would hide underneath its own statue. Also, the
hero saw a snake hide under unseen submerged kelp.
Both of those things were exposed by new "you see <monster> hide"
message rather than caused by it. It also led to the [re-]discovery
that an existing monster hiding under a statue that was a not-yet-
triggered trap prevented the trap from producing a monster.
This redoes yesterday's can't-hide-under-statue change: hiders can
hide under statues again, but they can't hide under anything at trap
locations. [Pits containing one or more objects are an exception,
although it seems silly that a hero is prevented from falling into
one by the presence of a tiny creepy-crawly hiding under a ring or
dart in there.] So, hider-underers won't be able to interfere with
statue traps by being present at the trap location. [Trappers and
lurkers-above probably need a similar restriction; I didn't look.
They avoid trap spots rather than get lured to such by objects.]
It also prevents newly created hider-underers from becoming hidden
as part of the their creation (except when that creation is part
of level creation) whether their creation uses up an object (statue
activation, egg hatching) or there are simply other items present.
That will prevent statue of a hider producing a monster that hides
under the activated statue (which was happening due to the sequence
create monster, transfer any statue contents to monster inventory,
destroy statue).
The can't-hide-under-statues code has been repurposed to prevent
hiding under gold pieces unless there are at least 10 (arbitrary
threshold) of those or they're in a pile with some other object(s).
Sea monsters hide in water regardless of the presence of objects.
Prevent other swimmers from hiding under objects at water locations.
Such creatures don't have gills and shouldn't be able to stay
submerged in hiding for an arbitrary length of time. [No exception
is made for non-breathers. The overlap between swimmers and hider-
underers is limited to small snakes, even though it is feasible for
a creature wearing an amulet of magical breathing to polymorph into
one. Heros don't spend enough time underwater to worry about snakes
hiding under kelp or thrown junk.]
Lastly, alter the "suddenly, you notice a <monster>" message if
monster-vs-monster activity causes one you've just seen going into
hiding comes back out again without any intervening messages. [I'm
not sure whether something similar is needed for the "Wait. There's
something there" message in the you-vs-monster case.]
Fixes#1062
Unlike ground clutter, statues are typically in pretty tight contact
with the ground; statue traps are sometimes proclaimed as "monsters
posing as statues".
This was caused by the mind flayer mind blasting polymorphed hero,
causing them to revert back to human form, and dropping into a pool,
doing an automatic teleport to save them from drowning.
After that the mind flayer tried to hit them in melee, but the hero
was far away.
Recalculate the nearby and inrage variables after mind blast.
Extracted from a larger pending commit. While trying to make sure
that bhitpos and notonhead are up to date when attacks are processed,
I noticed that covetous monster handling was buggy. For dist2(),
a value of 1 means adjacent in an orthogonal direction, so testing
for less than 2 unintentionally excluded diagonal adjacency.
Engraving in an empty doorway and then using locking magic to create
a door there resulted in an impossible warning: "engraving sanity:
illegal surface (23)" if the 'sanity_check' option was On (wizard
mode only). Engraving in an open doorway and then simply closing
the existing door produced the same effect.
Accept engravings at closed doors. Presumably hero will be using
Passes_walls to attempt that so treat closed doors same as open ones.
Update the engraving sanity check to deal with that.
Bonus fix: engraving sanity checking stopped after the first problem
instead of checking every engraving. Have it continue instead.
Not fixed: vault wall repair and temporary corridor removal does
not delete engravings and can trigger the illegal surface warning if
player engraves before the repairs. I didn't test shop wall repair
but it doesn't have any engr references so probably has the same bug.
... if the boulder is in a position they want to move to.
Shopkeepers, priests, and the quest leader can break one boulder
and then need to take several turns before being able to break
another. Riders can break a boulder every turn.
Previously when monster was interested to pick up an item,
the code went through the whole object chain, so going through
all the items on the level. This caused problems with some games,
for example where the player created thousands of meatballs
in separate stacks.
Changed the code so it now looks at the map locations inside
the search radius, and the stacks in those map locations,
skipping locations as early as possible.
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.
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.
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.
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() */
Issue reported by vultur-cadens: Elbereth used to be effective in
inhibiting monster movement when an object was present on the same
spot, but since 3.6.0 it isn't. It only functions that way when the
hero--or hero's displaced image--is present these days. So special
levels that have been using engraved Elbereth to try to protect
objects from monsters haven't been providing any useful protection.
This makes Elbereth that's engraved during level creation work like
it used to in 3.4.3 and earlier: when there's at least one object
on the engraving's spot, monsters who are affected by Elbereth will
be affected. [I'm fairly sure that that behavior started out
unintentionally, as a side-effect of an optimization to only check
for scroll of scare monster when there was at least one item present
which is a necessary condition for such a scroll.]
Old-style Elbereth includes Elbereth chosen as a random engraving
during level creation in addition to engravings specified in special
level definitions. Engravings by the player don't have the required
attribute and player-engraved Elbereth behaves in the 3.6 way.
This ought to be replaced by something more general. Perhaps a new
engraving type not usable by the player?
Fixes#900
A monster which has grabbed you could move away without becoming unstuck
if it hit the "move and shoot" or "helpless" conditions in the dochug
MMOVE_MOVED case (since those lead to early return or break), leaving
the hero stuck to a monster which is no longer adjacent. Put the
'grabber moved away -> become unstuck' stuff at the top of the block so
that it will always be evaluated if a grabber has moved.
I would have liked to move the whole "grabber checks" block up, but I
think it'd change behavior to have the u.uswallow attack come before the
early return for a helpless monster, so I split it up instead.
When a monster did something trying to get out of a boulder fort,
it usually meant teleporting or going down stairs or a hole.
The code didn't check for the action return value, and resulted
in a migrating monster being able to throw a potion at hero.
If a monster cannot move, for example because it's being
blocked off by boulders or walls, it will try to escape by some
method - such as a wand or scroll of teleportation.
The mind blast code was previously a part of dochug().
This commit pulls that code into its own function, and also
happens to eliminate a goto and label with no change in functionality.
This commit removes the gotos from set_apparxy, making it easier to read.
It also cuts out at 1-2 variable assignments on certain calls,
so technically it is an efficiency win as well.
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.]