Adds two monsters originally from slash'em. I used the slash'em
tiles this time, also its code as a starting point but made various
revisions. Both the tiles could benefit from some touch-ups.
displacer beast: blue 'f'. Attempting a melee hit (ie, trying to
move to its spot) has a 50:50 chance for it to swap places with you.
Fairly tough monster to begin with, then half your ordinary attacks
effectively miss and if you try to face a mob by retreating to a
corridor or backing into a corner you can end up being drawn back
into the open. I added bargethrough capability, and also it won't
be fooled about hero's location by Displacement. [It only swaps
places during combat when contact is initiated by the hero, not
when attacked by another monster or when attacking.]
genetic engineer: green 'Q'. Its attack causes the target to be
polymorphed unless that target resists. Hero will almost always
have magic resistance by the time this monster is encountered, but
it can make conflict become risky by hitting and polymorphing other
monsters. Slash'em flagged it hell-only but I took that flag off;
I also took away its ability to teleport. Slash'em polymorphs the
hero if a genetic engineer corpse is eaten; that's included and I
introduced that for monsters too.
I added both of these to the list of candidates for monster spell
'summon nasties' and for post-Wizard harassment.
I also gave all the 'f's infravision. Probably only matters if the
hero polymorphs into a feline.
Displacer beast is originally from AD&D which depicts it as a six-
legged cougar with a pair of tentacles; it has Displacement rather
be able to affect an attacker's location. I think genetic engineer
is original to slash'em where it expands Q class but seems mainly to
be the base monster for Dr.Frankenstein (a unique monster with a
one-level side-branch lair in slash'em's incarnation of Gehennom).
Some instances of monsters eating nurse corpses or tins of nurse
caused blindness to be cured, others didn't. Always do that to
match the effect on the hero.
Also, fix a couple more obsolete references to green slime corpses.
Compiler complained about 'ptr' possibly being used unitinialized
in meatcorpse() and in this case it was right. meatcorpse() was
cloned from meatobj() but the necessary initialization was missing.
Purple worm would devore an entire stack of corpses in one bite.
Split one off and have it eat that instead.
I'm not sure whether attempting to revive a Rider corpse can force
a monster off the level to make room. If so, meatobj() and
meatcorpse() weren't prepared to handle that, nor was their caller.
It appears that the monster (either g.cube or purple worm) will
only eat as it moves so can't revive a Rider on a completely full
level since it won't be able to move in that situation. I fixed
the caller to be prepared to handle a result of 3 (no further
action allowed) instead of just dealing with 2 (died), but I didn't
fix either of the meatfoo() routines to return 3 since bumping the
eating monster off the level seems to not be possible.
Don't let purple worms eat lichen corpses, regardless of whether
they'll swallow live ones.
Shriekers only spawn purple worms when they're appropriate difficulty.
Non-tame Purple worms eat corpses off the ground.
Baby purple worms attack shriekers.
Hero polyed into baby purple worm is warned against shriekers.
Original changes by copperwater <aosdict@gmail.com>, added with some
formatting adjustments and consolidation.
Fix a warning about mixing || and && without parentheses where
parentheses were present but a closing one was misplaced.
Also, isok() was redundant in 'isok() && is_pool()' because is_pool()
starts off with its own isok() check.
Report complained about multiple Archons causing his character to
be swarmed by monsters on the Plane of Fire. I don't think that
the behavior has changed significantly from how it worked in 3.4.3.
Nobody can summon an Archon directly because they're excluded from
the nasties[] list. But whenever summoning picks a genocided
'nasty', the result gets replaced by random monster of appropriate
difficulty for the level (which could be an Archon for a high level
character in the endgame). [Note that that won't pick an Archon
in Gehennom or at arch-lich outside of there because the random
monster creation honors the only-in-hell and never-in-hell flags;
picking from the nasties[] list doesn't.]
This prevents that for any creature (except arch-lich or the Wizard)
casting the summon nasties spell. If a replacement creature is a
spellcaster it now has to have lower difficulty than the summoner.
If not, it will be discarded even though its difficulty is classified
as appropriate. So to summon an Archon, the summoner has to have
higher difficulty than an Archon; arch-lich and the Wizard are the
only ones meeting that criterium. When summoner is an arch-lich,
it can't summon another arch-lich (since that wouldn't have lower
difficulty than the summoner) and can summon (via replacement for
genocided type, and only if outside of Gehennom) at most one Archon.
When summoner is the Wizard, he could summon an arch-lich (when in
Gehennom; demoted to master lich elsewhere--see below) or an Archon
(outside Gehennom only), but at most one per summoning.
For post-Wizard harassment, which effectively has infinite
difficulty level, it could still happen. However, each instance of
harassment is only allowed to create at most one Archon or arch-lich
now, so chain summoning should be lessoned. Also if it tries to
pick an arch-lich when outside of Gehennom it will switch to master
lich instead (which won't be allowed to summon an Archon or an arch-
lich or even another master lich).
(The monmove.c bit is unrelated, just some comment formatting that
I had laying around that got mixed in.)
Instead of forgetting maps and objects, make amnesia forget skills.
Forgetting maps and objects could be circumvented with taking notes,
or by using an external tool to remember the forgotten levels.
Forgetting skills allows the player to optionally go down another
skill path, if they trained the wrong weapon in the early game.
Amnesia still forgets spells.
As a replacement for the deja vu messages when entering a forgotten
level, those messages will now indicate a ghost with your own name
existing on the level, given only when the level is entered for
the first time.
These changes based on fiqhack, with some adjustments.
Setting or clearing u.ustuck now requires that context.botl be set,
so make a new routine to take care of both instead of manipulating
that pointer directly.
Several conditions result in stale data on the status line when
starting or stopping because things which didn't used to affect it
haven't been setting context.botl to force an update. This wasn't
systematic; there are bound to be lots more.
The "you have a sad feeling for a moment" message was only given when
one monster kills another (and the latter is an unseen pet). Give it
for drowning too. There are probably a bunch of other circumstances
which warrant it as well but I've settled for handling minliquid().
Instead of an assortment of bits, assign numeric indices to the
potential achievements and keep an array of those in the order they
were attained. So disclosure might show the same subset occurring
differently in different games depending on the player's actions.
The encoded field in xlogfile doesn't care about that and remains
the same.
Modifies 'struct u', so EDITLEVEL has been incremented and existing
save files are invalidated.
Some new code was using 3.4.3 era formatting (operators at end of
first half of a continued line rather than at start of second half).
Also a few cases of 'g.' prefix making lines be too wide. I imagine
there will be a lot more of these over time.
With 3.7+ aspirations of improving savefile interoperability between 32-bit
and 64-bit builds, as well as between platforms, it is better to not have
the underlying struct/array content be conditional.
This splits off some of the MAIL code into MAIL_STRUCTURES code. In theory,
since MAIL_STRUCTURES is unconditionally included, the macro could
just go away and leave that code unconditional, but this commit doesn't
go that far.
Reported directly to devteam rather than via the web contact form:
throwing wielded aklys while swallowed would hit the engulfer and
return to the hero's hand but leave a stale 'thrownobj' pointer if
the monster survived. Under usual circumstances, throwing anything
else or throwing the aklys again when not engulfed would clear that
pointer, putting things back to normal. However, killing any engulfer
with the same weapon would try to add it to engulfer's inventory to
be dropped as it died. If the killing blow was via melee rather than
another throw, the object in question would still be in hero's
inventory instead of free, hence panic.
The initial returning-aklys implementation shared Mjollnir's code
which doesn't have this issue. This reverts from having attached
aklys always returning successfully when thrown while swallowed to
Mjollnir's 99% chance of return and 99% to be caught when it does
come back. (That was already the case if the engulfer was killed by
the throw, where hero wasn't swallowed anymore after the damage was
inflicted.)
causes "dmonsfree: N removed doesn't match N+1 pending" warning. The
sandestin monster definition flags it as MR_STONE, immune to being
turned to stone. If the hero hit it with a cockatrice while it was
shape-changed into something which isn't MR_STONE, it had its mon->mhp
set to 0, so died, and its form (mon->data) was set back to sandestin.
known_hitum() decided that it didn't turn to stone because of MR_STONE
for that form so proceeded to kill it off due to lack of hit points,
causing it to die twice.
I started to change that so that it didn't kill off the critter a
second time, bit it really shouldn't be able to kill it by stoning in
the first place. So sandestin how shares some vampire code to revert
to innate form and not turn to stone when "killed" by stoning. It
only yields the normal visible polymorph message: "the <foo> turns
into a <bar>" without any attempt to explain why. Once in sandestin
form, smacking it with a cockatrice corpse doesn't do anything special
(due to MR_STONE now being unambiguously in effect). It will soon
shape-change to some other form and then become subject to being
forced back to innate shape by stoning again.
For wizard mode 'monpolycontrol', the getlin() answer buffer for the
"Change <monster> @ <x,y> into what kind of monster?" prompt is also
used to format the coordinate portion of that prompt, so when
EDIT_GETLIN is enabled getlin() was inadvertently given "<x,y>" to use
as default response. Clear it after the prompt is formatted instead
of via an initializer. Also, shorten the prompt on the first try:
"Change <monster> @ <x,y> into what?", expanding to the old prompt if
retry is needed.
This also allows specifying 'chameleon' when <monster> is a chameleon,
'doppelganger' when it's a doppelganger, and 'sandestin' when it's one
of those (but not 'doppelganger' when it's a chameleon or sandestin,
and so forth), instead of blanket refusal to accept any non-vampire
shapechanger as the choice.
Hero polymorphed into a vampire or v.lord can use #monster to switch
to vampire bat or fog cloud [or wolf for lord] but it was a one shot
polymorph. Remember when current form is a shape-shifted vampire and
allow #monster in shifted form to pick another shifted form or the
vampire form.
Genocide of the alternate shape forces back to base vampire. Genocide
of base vampire does too, then reverts to human (or dwarf, &c) as
vampires go away. Being killed while shafe-shifted reverts all the
way to human rather than to vampire. [Just realized: interaction
with Unchanging wasn't taken into consideration so hasn't been tested.]
Since 'youmonst' isn't saved and restored, I had to add a field to 'u'
to hold youmonst.cham during save/restore.
Tested with 3.6.2+ and seemed to be working (except saving while
shape-shifted restored as ordinary bat/cloud/wolf because new u.mcham
wasn't there to hold youmonst.cham yet). Builds with 3.7.0- but not
execution tested yet (I didn't want to clobber my current playground).
While testing, I noticed that I could completely fill the Water level
with air elementals.
Hero can't fly or levitate or water walk into/onto water locations
on Water level without drowning/crawling out the water, and monsters
shouldn't have been able to but could, then they were hit by drowning
since minliquid used different criteria than movement. But goodpos(),
used for teleport destination and new monster creation among other
things, consided water locations acceptable on that level for
non-aquadic creatures with Fly/Lev/Wwalk ability.
It explains why so many dragons and other 'nasty' monsters have been
ending up on the vanquished monsters list when hero uses level
teleport to go directly there from level 1. They've either been
getting created in water and then they drown when it's their turn to
move or moving into it to approach the hero and drowning (not sure
whether that case is immediate or on next move). There's no message
since hero doesn't see it, and air elementals didn't drown since thy
don't breathe.
Even though a goodpos failure in mnearto() would return 0 to
the caller and trigger proper overcrowding handling for mtmp,
the 'othermon' would be left with its mx,my set to 0,0 under
that circumstance and then trigger a mon_sanity_check()
failure and accompanying impossible() message a short while
afterwards.
This also includes the addition of some flags that proved useful
for troubleshooting the mystery sanity_check failure and helping
to understand some of the code paths the struct monst data had
been through. They are only used for inspection when issues are
reported or when debugging, they don't presently control the
code flow. Their setting and use is done in an overloaded way
that should not intrude on the existing use of mspare1 for
MIGR_LEFTOVERS. mon->mstate is just a pseudonym for mon->mspare1
and does not alter save file content.
This doesn't solve the <0,0> problem but it does prevent mnexto()
from using uninitialized coordinates if enexto() fails. It also adds
several debugging messages.
enexto() was ignoring map row #0 (unlike column #0, row #0 contains
valid map locations). Fixing that doesn't matter for Plane of Water
though since that row is stone there--that's probably a bug. It was
also repeatedly re-testing the top+1 and bottom rows and left and
right columns after they had already failed to be acceptable. It
still does some of that, but less.
When cloning a monster, clear the clone trapped and hiding states.
When splitting a monster (eg. a black pudding), the clone could
be placed on a trap, so do mintrap.
When removing a monster from the map, clear the trapped state.
|The seemingly dead vampire bat rises as a vampire.
was overriding hallucination when describing both old and new forms.
In 3.6.0 it only overrode the dying shape (explicitly so, presumeably
because the feature was brand new) and honored hallucination for the
revived shape. The 3.6.1 fix to prevent non-hallucinating: 'The
seemingly dead Foo rises as Foo.' for a named vampire unintentionally
overrode hallucination for the revived shape.
Change it to honor hallucination for both before and after monsters
|The seemingly dead grid bug rises as a microscopic space fleet.