Put everything through a single function that can handle all the
complicated parts of using the correct proposition for different terrain
types, and will not just call things "solid ground" indiscriminately.
This got complicated but I'm not sure if it's possible to do it much
simpler while still using the distinct names for each type of terrain
(unless you are OK with the sentences sounding sort of wonky).
If throwing an item while levitating sent the hero hurtling into a wall
of water, the item would land in the water due to water_damage_chain's
use of bhitpos. Restore the previous value when it is finished to avoid
interfering with the use of bhitpos further up the call stack.
"object lost" panic occurred when hero's worn amulet of magical
breathing was stolen. This prevents drown() -> emergency_disrobe()
from dropping an item while in the midst of it being stolen, avoiding
the possibility of it no longer being in inventory when the theft
completes. There may be variations other than drowning that lead to
unwear -> drop-or-destroy that are still vulnerable, and this fix can
potentially cause items to vanish from hangup save files.
It also has a side-effect of not being able to drop levitation boots
to lighten encumbrance enough to crawl out of water if the drowning
occurs while they are being taken off, not just when being stolen,
even though they should be easily droppable in such circumstance. The
hero will just need to drop other things instead.
While testing something else, I noticed rolling boulders
just ignored walls and trees; in normal play this isn't
a problem - but should probably make boulders handle other
terrain too. Lava and water is already handled correctly.
b_trapped was treating 0 as a null value for its bodypart parameter, but
0 is actually the value of ARM, so b_trapped(..., ARM) would be treated
as intending no A_CON abuse. Add NO_PART = -1 to the bodypart_types
enum, and use that instead of 0 as the "no body part" value in
b_trapped, so that ARM can be passed to it without any ambiguity.
aosdict identified this issue in xNetHack and handled it differently; he
added NO_PART with a value of 0, incremented the existing bodypart_types
values, and padded the body part arrays so the actual body parts would
start at index 1. I think using NO_PART = -1 is simpler, but that's an
alternative approach that can be used instead -- it is advantageous in
that it automatically fixes any other places where 0 is assumed to be a
non-body-part value that I may have overlooked.
Fairly old pull request from copperwater: add new paranoid_confirm
setting 'trap'.
The old commit suffered from bit rot and merging needed too much
fixing up despite there not being many bands of change in the commit's
diffs. I ultimately redid it from scratch, although the two biggest
chunks of code started with copy+paste of the pull request's commit.
It operates like paranoid:pray. Setting paranoid:trap adds a new
"Really step into <trap>?" y/n prompt when attempting to move
into/onto a known trap, even if an object covers it on the map.
Setting both 'paranoid:Confirm trap' turns that into a yes/no prompt.
(Adding 'Confirm' affects other paranoid confirmations; in addition
to requiring yes<return> rather than just y to accept, it also forces
no<return> to reject.)
However, moving into a known trap that is considered to be harmless
behaves as if no trap was present. Some of the trap classification
might be out of date; several types of traps have undergone changes
since implementation of the original pull request, notably anti-magic
field. When the hero is hallucinating, all known traps are considered
harmful since the map no longer reliably describes them.
Preceding a movement command with the 'm' prefix also behaves as if
no trap was present, bypassing confirmation for that move, similar to
how paranoid:swim currently behaves. Being stunned or confused also
behaves as if no trap was present, taking priority over hallucination.
This updates the documentation.
Supersedes #259Closes#259
Sitting on a squeaky board wasn't triggering it even after the
handler for that type of trap allowed VIASITTING to override Flying.
The check_in_air() test for floor traps didn't have the same override,
so the squeaky board handler didn't get called.
This fixes that, which led to inconsistency with some other trap
types, and additional fixes for pits and bear traps. There might be
others that still behave oddly. For example, if flying over a hole,
using #sit yields
|You land. There's a gaping hole under you! You don't fall in.
I think that's a message phrasing issue rather than a falling trap
issue; if you want to go down, use '>' instead of #sit. On the other
hand, you do now fall into pit traps for #sit while flying over them.
If the hero deliberately sits on the floor while flying over a squeaky
board, then either they're trying to squeak it on purpose or they haven't
noticed it. Either way, sitting should trigger it.
Especially powerful magic is meant to be able to destroy altars
(breaking a wand of digging or using a drum of earthquake), but it was
being blocked by a check added to maketrap() in a7f6460 designed to
prevent wizard-mode trap wishing from overwriting stairs. The check was
refined in 6a3d82c to add an exception for digging up graves, but
continued to prevent the destruction of other types of
previously-destructible terrain.
Since this block was a side effect of an attempt to add some guard rails
to wizmode terrain wishes, and the code to explicitly permit the
destruction of other furniture with especially powerful magic is still
present, it doesn't seem like it was actually intended. Open up terrain
destruction by digging magic a bit more by excluding only
non-destructible terrain, not all furniture other than graves, from
being overwritten by pits and holes.
Also, use AM_SANCTUM to more precisely identify non-destructible high
altars in dig_check() rather than checking whether the hero is on the
Astral or Sanctum levels.
Pull request from NulCGT: make statues created for statue traps be
5 to 10 points higher in difficulty than the default would be.
5 to 10 points of difficulty higher is already used for figurines.
The pull request chose the same amount but I've reduced it to 3 to 6.
Partly so that they won't be the same, partly so that they won't be
too hard when activated, and partly so that the creature won't be
quite as obvious a give away that the statue is a trap.
Closes#1009
If the priest quest artifact Mitre of Holiness (a helm of brilliance,
so crystal helmet) is acquired via the quest (rather than by wishing)
make it start out tempered (aka crackproof|erodeproof).
Issue reported by loggersviii: dipping a container into an uncursed
potion of water mentions water getting into the container. That
happens even when that type of potion hasn't been discovered yet.
Make POT_WATER become discovered if this occurs. Doesn't apply when
hallucinating where a random liquid is mentioned instead of water.
Fixes#1061
Instead of a 5% chance for crystal plate mail or crystal helmet to
break each time it's subjected to breakage, switch to a 10% chance
but the damage is treated as erosion rather than break/don't-break.
'crystal foo' will need to go through four stages of damage before
breaking: cracked crystal foo, very cracked crystal foo, thoroughly
cracked crystal foo, then gone. Crackproof handling is included,
described as tempered crystal foo.
It mostly still applies to throwing and kicking the item. Having
some hits trigger damage might be worthwhile but isn't implemented.
Object creation within lua code probably needs to be updated, and
when the Mitre of Holiness is created in the priest/priestess quest
it should start out as tempered (erodeproof). Perhaps it ought to
be erodeproof regardless of where/how it's created.
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
The teleport to safety routine would try to find a viable spot 400
times, the first 200 rejecting trap locations and the last 200
accepting such. While testing various escape from lava variations
recently, I noticed that I could fail to reach safety even when there
was an open spot immediately adjacent to me. I added a BandAid(tm)
to make another 400 tries if the first attempt failed, but that was
clumsy and still didn't guarantee picking a viable spot.
This adds a new routine, collect_coords(), which will gather a list
of coordinates for the entire map. safe_teleds() goes through them
one by one until either finding a spot or exhausting the possibilies,
without randomly trying and retrying the same spot multiple times and
without missing other potential spots, also without just scanning the
map from left to right and top to bottom or similar.
Various other things which retry over and over, and especially the
ones which make a bunch of random attempts and then fallback to trying
every spot on the map, could be switched over to this, at least for
the falling back phase. Right now collect_coords() is local to
teleport.c but that could be easily changed.
The try-at-random method is much quicker when there are lots of
available spots but the gather-shuffled-candidates method is
guaranteed to succeed if success is possible. The way safe_teleds()
is presently using it collects the list with all locations within
2 steps first, then those within 3 to 4, then 5 to 6, and so on out,
randomized within each block of ranges. So the destination will be
within one step of being as close to the starting spot as possible
but not always immediately adjacent when that happens to be available.
Replace a couple of hardcoded "back on solid ground" messages with
something more versatile.
Also, make life-saving handling for failed rescue from drowning
similar to that of failed rescue immolation by lava. If there are
any cases where more than two tries is needed, they elude me. The
new code doesn't confer temporary water walking if emergency teleport
fails; perhaps it should.
Change dying in lava to attempt life-saving at most twice. The first
time might be via amulet or via declining to die. The second time
can only be via declining to die after failing to teleport to safety
the first time, but could happen in explore mode as well as in wizard
mode, either interactive or fuzzer.
If the hero dies twice without ending the game, confer temporary fire
resistance and water walking so that other actions can be attempted.
After 5 turns without getting away from the lava or doing something
to acquire those capabilities, the hero will be subject to falling in
again.
Then the whole cycle might repeat, even many times, but the fuzzer
will eventually choose ^V or #wizmakemap and escape. Players in
explore mode will either figure out a way to get out of it or
eventually have to give up but can try as many times as they like,
not that much different than being cornered by a deadly monster.
When being burned up by lava, die 20 times before giving up the
attempt at life-saving (was unlimited). Giving up leads to the hero
standing on lava rather than dying. Normally moveloop() dunks the
hero again on next turn but fuzzer life-saving now has a chance to
confer temporary fire resistance. So hero might have an opportunity
to level teleport or use ranged attacks that free up spots so have
somewhere available to teleport to safety if/when dunked again.
The recent code to give up on trying to resurrect the dying hero
after 15 deaths on the same move is extended to 20. They apply to
each of the 20 lava resurrect attempts but still doesn't guarantee
that the hero will eventually get free before done() gives up.
Knocking a monster into lava triggering impossible warnig "deleting
worn object" for a wooden shield. The monster had to be wearning
flammable armor and be fire resistant to survive instead dying and
dropping inventory, and it couldn't be a creature that can survive at
lava locations like a salamander and the armor needed to already be
thoroughly burnt.
It wasn't hard to figure out what needed to be fixed, but it was very
hard to reproduce the situation in order to verify that fix. The
report was for monster vs monster knockback but I jousted with a lance
instead. I still don't understand why burning up a worn wooden shield
triggered the warning but burning up a worn orcish cloak did not.
A recent change to prevent creating webs at water locations also
deliberately prevented them at air locations but had the unintended
side-effect of preventing creation of magic portals on the Planes
or Air and Water. Those two levels always place the portal in air.
Explicitly allow air if the trap being created is a magic portal.
Reported by copperwater, it was possible for a web to be created
at a water or lava location. It would not be displayed even after
being discovered; showing the terrain superseded showing the trap.
But it functioned normally and could trap the hero. Webs pull the
victim to the floor so hero would drown or burn up on next move
even if the spot had been reached while floating or flying.
A monster spider couldn't survive at a water or lava location, but
a poly'd hero could. Creating a web via #monster or wizard mode
wish could result in it affecting some unsuspecting player via bones.
Disallow creation of webs at water, lava, and air or cloud spots.
(They're already disallowed at furniture spots.)
Fixes#1017
... 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.)
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
Issue reported by vultur-cadens: when trap effect of an anti-magic
field reduced maximum energy, the result might end up with current
energy being one point higher than new maximum.
Fixes#1003
If standing at the location of a closed door while having Passes_walls
ability, attemping to open that door with 'o' reported "you don't find
anything here to loot".
Using 'o' to loot for direction ./>/< is intentional but it ignored
the possibility of there also being a closed door present. When the
latter applies, only switch from open to loot for direction '>' so
that '.' (and '<') will open the door. Doesn't matter for creatures
that can ooze under a closed door--trying to use 'o' gets rejected for
them because they lack hands.
Also allow hero in Passes_walls form to use 'c' when at open door spot.
(For creatures that ooze under doors, 'c' is rejected just like 'o'.)
Unrelated: fix a typo in a recently added comment.
Fix the duplicate feedback given when landing on one or more items
after teleporting out of lava.
This also avoids "you find yourself back on solid water" if you are
able to survive at a water location and safe_teleds() puts you on one.
Report was for spell-casting monster using the destroy armor spell on
hero's levitation boots while hero was floating over lava. The boots
became unworn but still in inventory, hero dropped into lava, the
boots happened to be an inventory item which got burned up, then the
call stack unwound back to the destroy armor routine which tried to
finish by deleting them but they were already gone by then. Could
also happen for black dragon breath, hero reading scroll of destroy
armor, or overenchanting the boots with scroll of enchant armor, so
not so unlikely that nobody would be expected to notice.
Initially I couldn't reproduce the object lost panic. It only happens
if the memory used by the boots is cleared or clobbered during first
time it's freed, otherwise second free doesn't notice any problem.
The 'wornarm_destroyed()' portion of this commit is sufficient to fix
this. The other bits are things I tried before figuring out how to
reproduce it, plus zeroing out any object passed to dealloc_obj().
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.
When attached iron ball was in a pit (or a pool) with a monster,
and your levitation ended, you were put on top of the monster.
Add a sanity check for hero over monster.
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.
When running #wizkill, if hero was swallowed and you killed the
engulfer and that dropped hero onto a level teleporter, the targetting
loop for selecting the next monster to kill kept going after changing
to another level. Terminate #wizkill if killing something sends you
to a different level.
Not fixed, and an old bug, or variation of one: the cursor got
positioned at the coordinates of your spot on the prior level even
though the part of the new level where you actually arrived was
displayed.
The hack.c and trap.c bits are just reformatting.
Pull request #936 took away the destruction of potions of acid ("acid
and water don't mix") if they survived water_damage(). Restore that
by forcing them to not survive. Exception: if they're greased and
pass the 50:50 chance of retaining the grease, they aren't destroyed.
This was totally silent, which -- at least for me -- has led to quite a
few cases of believing my bag or cloak is still greased when it actually
wore off the last time I took a dip. I think telling the player that
the grease has worn off would be helpful, and is consistent with other
types of water damage.
The message is printed even if you are blind, since that seems to be
true of all the other messages in water_damage(). I am not sure if that
makes complete sense (especially for ones like a scroll fading -- some
like water getting into a bag could be sensed by touch) but I didn't
change anything there.
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() */
Use the level passed in instead of the hero's location when checking
the dungeon branch.
It probably doesn't make any difference, but use the argument that's
already being provided.
This should prevent anyone from exploiting falling into the sanctum (by
taking advantage of a trap door in a bones file from a differently
laid-out dungeon, as described in the previous commit) to bypass the
invocation, in addition to falling past the actual end of the dungeon.