Add a new boolean option showdamage, if on, outputs a message
like "[HP -2, 14 left]" - several variants have something similar,
but I chose the message based on how eSpeak said it, while keeping
it short.
Note: Original change is from xNetHack by copperwater <aosdict@gmail.com>,
but this commit comes from HACKEM-MUCHE by Erik Lunna, with
some minor code formatting.
From xNetHack commit a0a6103bea:
'The original goal: nerf item destruction using a method I initially
proposed for SpliceHack, in which the number of items subject to
damage from any single source is limited by the amount of damage the
effect caused. The intent was to be more fair all around and prevent
aggravating situations where, for instance, a chest shock trap zaps
you for 4 damage and immediately ten of your rings and wands blow up.
Problem 1: no easy way to limit the items destroyed without biasing
heavily towards the start of the invent chain. The old code was able
to get away without bias by just indiscriminately destroying
everything eligible with a 1/3 chance. Here, I had to introduce
reservoir sampling in a somewhat more complex form than I've applied
it elsewhere, since there are a pool of potential items.
Problem 2: destroy_item no longer worked remotely like destroy_mitem,
which still destroyed 1/3 of items indiscriminately. Commence the
process of squishing them into one function that handles both the
player and monsters. (Which required making a lot of adjustments to
destroy_one_item, now named maybe_destroy_item, on nits such as
messaging and when to negate damage. An annoying consequence of the
merge is that in the player case, their HP is deducted and they can
be killed directly, but for monsters they need to add up the
destruction damage and return it.)
Unifying destroy_item and destroy_mitem has some advantages: in
addition to the obvious code duplication removal, it ensures monsters
now take the same damage as players for destruction (previously they
took a piddly 1 damage per destroyed item). Now when you hit
something with Mjollnir and their coveted wand of death breaks apart
and explodes, you at least get the satisfaction of knowing they took
the standard amount of damage from it. Monsters also now get
symmetry with players in having extrinsic elemental resistance
protect them from item destruction, and damage negation from item
destruction if they were appropriately resistant.
Problem 3: a lot of callers didn't preserve the "amount of incoming
damage" that this refactor relies on. E.g. if the defender resisted
that element, the local dmg variable would be set to 0. So I had to
do some wrangling with callers to save that original damage
value. The rule of thumb is: all *incoming* damage counts. So that
includes the player's spellcasting bonus if applicable, but not
things like half damage, negation due to resistance, or extra damage
due to being vulnerable to cold/fire.
Then I figured, while I'm here let's get rid of all those silly cases
where destroy_items is called multiple times for various different
object classes, and cut the object class parameter out of it. This
has a few minor effects:
- Places where different object classes previously rolled
independently for destruction to happen at all now roll
once. (Which, by my calculation, generally means less incidences of
destruction - a fire attack now won't have three separate chances
to hit your scrolls, potions, and spellbooks. On the flip side, a
lucky roll will no longer save an entire object class in your
inventory.)
- Callers can no longer specify different probabilities for
destroying different object classes. The only place this was really
used was to call destroy_item with a slightly lower probability on
SPBOOK_CLASS. With the nerf in this commit, less of them ought to
be destroyed anyway.
- A very edge case of where explosion-vs-monster damage was totted up
differently for golems, which could result in differences of a hit
point here or there.
- All object classes being processed in one go means that less items
are destroyed than would be if they were still processed
independently. This is not really visible compared to the old
baseline of just destroying 33% of everything, but would be a
marked difference versus a copy of the game that still called
destroy_items separately for different object classes. To
compensate, I adjusted my planned damage-to-destruction-limit
scaling factor down from 8 to 5.
Not done: merging in ignite_items(), though that would probably be
really easy now.'
Notes from porting from xNetHack:
- It might be necessary to reexamine at all the conditional checks for
calling destroy_items. Because item destruction is much more
restrained and uses the actual damage from an effect, we might now
need to check 'if (!rn2(3))' and similar in all the places item
destruction occurs.
Adds a new boolean option, accessiblemsg. If on, some game messages
are prefixed with direction or location information, for example:
(west): The newt bites!
(northwest): You find a hidden door.
I added the info to the most common messages, but several are
still missing it.
vs lycanthropes
Issue reported by Umbire: when hero had Protection_from_shape_changers
extrinsic, a lycanthrope in human form that attacked could change into
critter form. It would change back to human on its next move.
Prevent werecreatures from transforming from human form to critter form
if hero has Protection_from_shape_changers. That attribute does not
prevent a human werecreature from summoning animal companions.
Fixes#1082
Issue reported by vultur-cadens: changing helm of brilliance to
crystal made it stop being classified as "hard helmet" so it gave
less protection against things falling onto the hero's head.
Change the is_metallic() tests used on helmets to new hard_helmet().
Unlike when thrown, crystal helmets don't break when objects fall
on them.
Fixes#1060
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).
I was working on this at the time 3.6.0 was released and set it aside
until later. Later has finally arrived. Redo the Blind, Blinded,
Blindfolded,&c macros to make more complete use of intrinsic property
handling. Blinded was being treated as a number which could be added
to or subtracted from; now that has to be done via TIMEOUT mask
because it has FROMOUTSIDE (OPTIONS:blind) and FROMFORM (poly'd into
!haseyes() form) bits included. Object definitions for blindfold and
towel now specify the BLINDED property; overriding blindness via the
Eyes of the Overworld is accomplished via props[BLINDED].blocked.
Code generated for the scores of Blind and !Blind tests throughout
the program should be smaller.
One bug that has been fixed is that putting on the Eyes of the
Overworld cured permanent blindness (from OPTIONS:blind). The
u.uroleplay.blind flag was cleared and stayed so after taking them
off. Putting the Eyes on still breaks blind-from-birth conduct but
now blindness will resume when they are removed.
This was untested at the time it was set aside and is only lightly
tested now. A large number of the changes here are just to switch
from Blinded to BlindedTimeout for current timed value and to call
set_itimeout() for setting a new value.
... 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.)
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.
The fix in commit 14d003c4ba prevented
current energy from ending up 1 point above maximum energy but it
didn't preserve the intent of splitting the drain with up to half
coming out of maximum and the remainder out of current. This restores
that intent but now only does so when maximum is more than the full
drain amount rather than when it is more than the up-to-half portion,
becoming less harsh when hero's max energy is very low. If current
is also very low then max energy will be reduced anyway, but by less.
Some unrelated formatting of invent.c has gotten mixed in.
Revises #1003
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.
Add "again" to the hit message when an attack sequence has
consecutive attacks of same type and they hit. A {bite, claw, claw}
sequence won't give that for first claw attack regardless of whether
the bite hits but will give it for the second claw attack when both
of the claw attacks hit.
The message sequence
|The fire ant bites! The fire ant bites! You're on fire!
or |The fire ant bites! The fire ant bites! You avoid harm.
when the first bite was for physical damage and the second was
for fire damage seemed a little confusing.
This changes that to be
|The fire ant bites! The fire ant bites again! You're on fire!
or |The fire ant bites! The fire ant bites again! You avoid harm.
It still isn't crystal clear that both bites are from a single
attack and that the second bite is for different type of damage
but I think it's an improvement.
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.
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.
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.
A number of C compiler suites have a math.h library that includes a yn()
function name that conflicts with NetHack's yn() macro:
"The y0(), y1(), and yn() functions are Bessel functions of the second kind,
for orders 0, 1, and n, respectively. The argument x must be positive. The
argument n should be greater than or equal to zero. If n is less than zero,
there will be a negative exponent in the result."
At one point, isaac64.h included math.h, although that has since been removed.
Some libraries used in NetHack (Qt for one) do include math.h and that required
build work-arounds to avoid the conflict.
Rename the NetHack macro from yn() to y_n() and avoid the math.h conflict
altogether, eliminating the need for that particular work-around.
tinklebear on IRC noticed that a hero paralyzed by a floating eye was
still "charmed" and capable of "removing her armor" as part of a nymph's
theft attack. The same thing was true of foocubus seduction: a
paralyzed hero was still able to respond to the questions about whether
particular pieces of armor should be removed (and also do whatever else
may be involved in a successful attack...).
I think paralysis should prevent both those things. Nymph theft will
still work, unless she needs the hero's active cooperation in removing a
bulky piece of armor. Foocubus attacks will be prevented entirely by
paralysis, making it interfere like unconsciousness already does.
Apply a similar constraint to hero vs monster seduction, as well.
Reported directly to devteam by entrez: a sleeping or unconscious
hero would still meet a monster's gaze attack even though those are
supposed to be eye-to-eye rather than just the monster looking at
you. Don't meet the gaze when Unaware, despite the fact that the
hero isn't blind and vision remains in operation.
Initially being Unaware also blocked Medusa's gaze being reflected
but I changed things so that that still affects her. It contradicts
the eye-to-eye aspect but is more consistent with her looking at a
blind hero who has reflection.
When a monster could first knock you back and then grab you,
the game would emit impossible, because the knockback moved you
but the distance variables used for the grab attack were not
updated.
Update the range variables within the loop iterating through
the monster attacks.
An engulfing monster can expel you onto a level teleporter or other
level-changing trap, in which case it may (under highly specific
circumstances[1]) no longer have been in memory by the time mtmp->mx/my
were accessed to see whether the "Brrooaa" message should be printed.
It also doesn't make much sense to print that message by the time you've
already fallen through a portal, trapdoor, etc, onto another level, so I
think moving it before the spoteffects() call kills two birds with one
stone.
[1] The highly specific circumstances: you must die due to illness or
some other timeout (or generally die on your own turn rather than the
monsters' turn, since this ensures the level change isn't deferred until
the end of the turn), while engulfed above a level teleporter [or maybe
another similar trap -- I tested with a level teleporter], and be
lifesaved, while positioned such that the engulfer can't follow you
through the levelport after expulsion (e.g. surrounded by other
monsters). It may happen under some other conditions too, but even if
so it's pretty rare and was tough to reproduce.
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.
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.
Suggested by aosdict: instead of describing all gulp attacks as
"engulfing you", say "The <monster> swallows you whole" for purple worms
(or any other animal engulfer) to visibly differentiate between
engulfing and swallowing.
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.]
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.
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.
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
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).