Various places checking for whether a monster was on the map based on
mstate flags were inconsistent about which ones they checked (and then
place_monster() was additionally inconsistent with all of them about
which bits were cleared when placing a monster onto the map). I think
some places were also more convoluted than is now necessary because they
date back to mstate being an alias for mspare1, which it shared with
migflags before those became two separate dedicated fields (MSTATE_MASK
also dates back to this and is no longer used, so I removed it).
I tried to go through all the MON_foo mstate bits, understand when/why
they are set, and make the various functions I noticed more consistent
(with each other, and with my understanding of how the bits work) about
how they are treated. I don't know for a fact that I understood
everything right -- some diagnostic bits that aren't used for much of
anything, like MON_OBLITERATE, had me mystified until I read the 5ee78c5
commit message -- but this patch hasn't caused any new problems (sanity
check or otherwise) with the fuzzer in my testing so far. All the same,
it could probably use review by someone who has a good sense of what the
mstate bits mean.
I did this several months ago to avoid a sanity check warning (and
consequent fuzzer panic) when an engulfer expels the hero on a full
level. I was hoping to refine it but never went back; install it
now before forgetting about it entirely.
If a chameleon changes from wall-phazer to engulfer while in a spot
the hero can't move onto and engulfs him/her, expelling the hero
after the engulfer has taken the hero's spot might be forced to put
the hero on top of the engulfer or another monster when unable to
use the engulfer's former spot. Rather than try to figure out all
the possible ways this might happen and attempt to deal with each
of them, just prevent an engulf attack from succeeding if the hero
wouldn't be able to move to the engulfer's spot. (Does not prevent
an air elemental over water from engulfing the hero.)
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.
A tame nymph attacked another monster, stole an item and teleported
away, but dog_move() wasn't passed the information that the nymph
was done, and tried to move the nymph from the old location.
Same with a tame leprechaun.
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
Don't allow stick/wrap/engulf attacks directed at long worm tails
to succeed. Achieved by making sure that 'notonhead' is up do date
in a bunch of places and utilizing the fairly recent can't-{stick,wrap,
engulf}-unsolid-monsters code.
Should prevent a 'sanity_check' warning about being too far from
u.ustuck that would happen when holding the tail while the head was
not adjacent to the hero.
Also don't let pet ranged attacks from choosing a long worm's tail
as target. They'll still be able to target long worms provided that
the head is lined up and not shielded by tail segment(s).
When a monster being attacked was knocked back into a level
teleport trap, the attacker could still hit the defender.
If the second hit then killed the defender, this could result
in dmonsfree warning.
When monster attacked another monster, and the retaliation attack
knocked back the attacking monster, the variables holding the
attacking monster coordinates were out of sync and caused
"no monster to remove" warning.
Propagate back the knockback hit, so the current monster cannot
do anything further.
Prevent hug attacks (owlbear, python, pit fiend, several others),
attacks for wrap damage (eel and kraken, trapper and lurker above),
attacks for stick-to damage (mimic, lichen), and attacks for digestion
damage (purple worm) from succeeding against unsolid monsters (ghosts,
lights, vortices, most elementals).
Polymorph of an engulf or hold target into unsolid form has been
addressed by a couple of previous updates.
When a genetic engineer polymorphs someone it normally teleports away.
Also set mspec_used so that it can't polymorph someone [else] on its
next turn, it case of a no-teleport level or it happens to randomly
land adjacent to the target.
Be more consistent with the engulf attack feedback by creatures who
fold themselves around the victim (trapper, lurker above) rather than
swallow or directly engulf.
Replace an instance of a non-literal format string and the warnings
manipulation it needed with a literal one.
for <new form> portion of messages "<old form> turns into <new form>"
to avoid named vampires yielding "Dracula turns into Dracula".
Pull request from entrez: a couple of engulf messages used regular
monster naming for vamp-shifters transforming rather than dying.
3.6.1 had fixes for this for basic monster death but didn't handle
engulfing.
Fixes#978
Apply a similar fix as a791b4b and f441696 ended up settling on for
normal vampire transformation to the vampire transformation that happens
when their shapeshifted form is engulfed.
From a report 9 years ago, a pet pyrolisk was repeatedly gazing at a
grey-elf and nothing happened. It turned out that the elf was wearing
an elven cloak which was negating damage some of the time (most of the
time back then) but with no indication that that's what was happening.
This makes many types of damage that are negated by MC say so.
Probably other types of damage should do likewise.
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.
mhitm.c: In function 'hitmm':
mhitm.c:583:30: warning: '%s' directive writing between 8 and 9 bytes into a region of size between 0 and 255 [-Wformat-overflow=]
583 | Sprintf(buf, "%s %s", magr_name,
| ^~
In file included from ../include/config.h:671,
from ../include/hack.h:10,
from mhitm.c:6:
../include/global.h:279:24: note: 'sprintf' output between 10 and 266 bytes into a destination of size 256
279 | #define Sprintf (void) sprintf
mhitm.c:583:13: note: in expansion of macro 'Sprintf'
583 | Sprintf(buf, "%s %s", magr_name,
| ^~~~~~~
Issue reported by vultur-cadens: one of the checks for whether a
shade would be harmed by an attack was erroneously inside a block
of code that only executed when you could see the attack. Basic
physical damage wasn't affected but some monster (or poly'd hero)
damage types that shouldn't affect shades didn't when seen but did
when unseen.
Could also get "attack passes harmlessly through the shade" when
an unseen attack for physical damage hit and failed to deal damage.
fixes#907
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);').
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
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.
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.]
Fix a case where a monster knocked back another monster into a trap,
and the trap either killed or migrated the monster, then the actual
damage dealing later could try to detach the already detached monster.
If the monster migrated, the monster is gone before the damage from
the hit is dealt to the monster.
When a monster at least two sizes larger hits another one,
there's a chance the smaller defender will be knocked back.
This applies also to hero, attacking when polymorphed to
a large monster, or defending from a large monster.
Most of the monsters that can knock back are giants and dragons.
Idea and some of the code from EvilHack.
Reported direclty to devteam by a hardfought player:
|placing tame fire vortex <56,18> over itself at <56,18>, [...]
An old map fixup when an engulfer and its victim temporarily share
the same map location got impacted by changes made a month or two
back for removing dead or migrated monsters from the map. The old
fixup (for putting the engulfer back after removing the victim also
removed it) was no longer needed and using it resulted in a warning
from place_monster() about putting a monster on top of itself.
... unless there's some other form that would override the choice,
such as a worn dragon armor, lycanthropy, or vampirism.
The polymorph will be in effect for 10-24 turns.
One of the drivers of this change was that screen coordinates require a
type that can hold values greater than 127. Parameters to the window
port routines require a large type in order to be able to have values
a fair bit larger than COLNO and ROWNO passed to them, particularly for
their use to the right of the map window.
This splits the uses of xchar into 3 different situations, and adjusts
their type and size:
xchar
|
-----------------------
| | |
coordxy xint16 xint8
coordxy: Actual x or y coordinates for various things (moved to 16-bits).
xint16: Same data size as coordxy, but for non-coordinate use (16-bits).
xint8: There are only a few use cases initially, where it was very
plain to see that the variable could remain as 8-bits, rather
than be bumped to 16-bits. There are probably more such cases
that could be changed after additional review.
Note: This first changed all xchar variables to coordxy. Some were
reviewed and got changed to xint16 or xint8 when it became apparent that
their usage was not for coordinates.
This increments EDITLEVEL in patchlevel.h
can gain intrinsics by swallowing monsters whole
Pull request #792 from entrez: monsters can gain instrinsics now but
the case for an engulfer digesting a live monster was overlooked.
Add the same for non-pet monsters digesting other monsters, likely
under the influence of conflict but possibly counter-attacking a pet.
Closes#792
Switch to using a macro invocation Verbos(n, s) in place of the
flags.verbose checks.
Provide the mechanics for individual suppression of any of the
existing messages that were considered verbose.
Mechanics only - this code update does not provide any means of
setting the suppression bits.
iflags.verbose = 0
is still a master suppression of all the verbose messages.
iflags.verbose = 1
turns on the verbose messages only for those whose suppression
bit is 0 (not set).
Apparently this is a bug that's existed since mon-vs-mon displacement
was introduced in 2003 (in 89c785e): if a monster displaced a footrice,
having gloves on would make it vulnerable to being stoned, while having
bare hands would protect it. Switch it around so wearing gloves blocks
petrification, as it does under other circumstances.
Also add a message explaining why the displacing monster was stoned (if
the displacement attempt is visible to the hero), so the "Foo turns to
stone!" message has some context.
Gulping can move the trapped monster to another location, while
still being marked as trapped. I don't want to deal with this,
so just say purple worms can't swallow trapped monsters...
Special abilities conferred by wearing dragon armor was implemented in
a somewhat half-assed fashion; extend it to 3/4-assed. Abilities came
from wearing dragon armor but not from being poly'd into a dragon or
for monsters that were wearing dragon armor or actually were dragons.
This covers much of that.
There are umpteen calls of 'resists_foo(mon)' and some are now
'resists_foo(mon) || defended(mon, AD_FOO)' but the second part ought
to be incorporated into update_mon_intrinics() so that the extra
'|| defended()' doesn't have to be spread all over the place and the
ones being put in now could/should be removed.
While testing, I noticed that a monster wielding Fire Brand did not
resist being hit by a wand of fire. This fixes that and should also
fix various comparable situations for other artifacts. But so far it
has only been done for zapping (and any other actions which use the
zapping code). Folding defended() checks into update_mon_intrinsics()
matters more than that probably sounds.
When testing the urgent message for having weapon be snagged by a
bullwhip, in between the occasional weapon grabs (which mention
flicking the bullwhip) I saw lots of regular attacks that said
"<mon> swings his bullwhip." That is accurate but seems odd, so
change it to "<mon> lashes his bullwhip." Do same for the hero.
While working on that, I discovered that monsters using a polearm
for a ranged attack always showed "<mon> thrusts <a polearm>" even
for ones that aren't defined as piercing so should be swung rather
that thrust. And they're allowed to do that when adjacent where
there isn't enough room to thrust or swing a long polearm. Now it's
"<mon> bashes with <a polearm>" in that situation.
Teleporting a monster only updated the map. Give a message
so blind players can get the same information.
Making a monster invisible gives the same message, if you
cannot detect invisible.
Several other places where monsters teleported themselves
now also give the same message.
Reported directly to devteam: monster vs monster location swapping
didn't handle single-segment long worms properly. Multi-segment
worms are disallowed but a worm with no visible segments (which
actually has 1 segment at the head's location) are allowed and the
segment wasn't being moved with the core monster and could trigger
warnings if sanity checking is enabled. The next time that the
worm moved, it got itself back in synch.
I couldn't reproduce the warning but mdisplacem() clearly assumed
that a long worm reporting 0 segments didn't have any so wasn't
attempting to handle the hidden one.
It's redundant with g.moves, so there is no more need for it.
Way, way back, it looks like g.moves and g.monstermoves can and did
desync, where g.moves would track the amount of moves the player had
gotten (and would therefore increase faster if the player were hasted)
and g.monstermoves would track the amount of monster move cycles, aka
turns. But this has not been the case for a long time, and they both
increment together in the same location in allmain.c. There are no
longer any cases where they will not be the same value.
This is a save-breaking change because it changes struct
instance_globals, but I have not updated the editlevel in this commit.
From a 7 or 8 year old bug report from a beta tester. A pyrolisk's
gaze against a mimic didn't unhide that mimic. Somewhere along the
line that has been fixed, but some apparently redundant code inside
'if (visible)' should be done even when not visible.