Commit Graph

487 Commits

Author SHA1 Message Date
nhmall
7d22a2c7b9 uhandedness follow-up
boomerang trajectory
bones
2023-12-02 20:25:37 -05:00
nhmall
d7fef5f194 avoid another magic number
Some of the hardcoded +1 scattered about are likely
invlet_gold or invlet_overflow, but I didn't hunt those down.
2023-11-30 11:15:32 -05:00
nhmall
b2a94c8555 misbehavior when tethered aklys gets stuck in web
Reported by elunna after it was noticed on Hack'EM
Closes #1152
2023-11-25 20:47:35 -05:00
nhmall
04082a2033 Remove TEXTCOLOR build option 2023-11-22 16:01:58 -05:00
nhmall
76d328d86a gi.invalid_obj -> hands_obj 2023-11-11 19:49:38 -05:00
nhmall
f97ff175de bring a couple of comments up to date 2023-11-10 13:17:09 -05:00
nhmall
314a2a9489 use gi.invalid_obj instead of cg.zeroobj
cg.zeroobj was originally added (under its previous unprefixed name)
for providing a one-line way to zero out the fields of a struct obj.

    struct obj tempobj;
    tempobj = cg.zeroobj;

    initfn(struct obj *otmp)
    {
        if (otmp)
            *otmp = cg.zeroobj;
    }

More recently, the address of cg.zeroobj began to be used as a return
flag to indicate some things, but the 'const struct obj zeroobj' wasn't
an ideal fit for the purpose and required a number of casts, including
casting away const.

Provide a better fitting variable (gi.invalid_obj) and eliminate a
number of casts.
2023-11-10 11:07:49 -05:00
Pasi Kallinen
2bb7239867 Fix iron bars dissolving turning floor into doorway 2023-11-04 13:41:12 +02:00
Pasi Kallinen
738c2239c9 Lightning has a small chance of melting iron bars 2023-11-04 13:04:27 +02:00
nhmall
6cbefc7c2d Revert "granular verbose message suppression mechanics"
This reverts commit be76727265.
2023-10-29 20:39:07 -04:00
PatR
04d6789c98 report ice's thaw state
Classify nearby ice as "solid" (no melt timer), "sturdy" (more than
1000 turns left), "steady" (101 to 1000 turns left), "unsteady" (51
to 100 turns left), "thin" (15 to 50 turns left), or "slushy" (1 to
14 turns left, matching walking on ice with the Warning attribute).
[I'm not thrilled with "steady" and particularly "unsteady".]

I was originally going to do this just for probing downward, but ended
up also doing it for look-here and getpos's autodescribe.  It nearly
got out of hand and touched more files than anticipated.

'mention_decor' ought to treat moving from ice firmer than thin to
thin or slushy, from thin to slushy, from slushy to any other, and
from thin to firmer as if moving onto different terrain but I haven't
attempted to tackle that.

The melt timer could work more like a candle's burn timer, triggering
at intermediate stages and resetting itself, so that ice which changes
to a weaker state under the hero could be reported to the player.  But
this doesn't implement that.
2023-10-25 13:26:03 -07:00
PatR
a31be02350 more wand of probing enhancements
Recent changes to wand of probing cause it to map unseen terrain when
zapped into the dark (or while blind) and also to reveal tin contents
if the beam hits such.  Extend that to discover secret doors, secret
corridors, and traps whether the spot can be seen or not, and also to
reveal egg contents.  Previously, secret corridors were converted into
regular corridors when they couldn't be seen but left as is if their
spot could be seen; now they're found and converted either way.

Issue the kaboom sound effect if zapping a magical trap with wand or
spell of cancellation causes it to go "Kaboom!"

A couple of chunks of code has been moved out of zap_updown() and
bhit() into new routine zap_map().
2023-10-19 00:28:19 -07:00
Pasi Kallinen
f83a57c5bf Tweak wand of make invisible and potion of invisibility
Wand of make invisible doesn't make you permanently invisible,
just for a short duration.  Potion of invisibility makes you
invisible for much longer period, or if blessed, has a small
chance of giving permanent invisibility.

This makes the wand actually useful, and improves the spell
too.
2023-10-03 21:04:15 +03:00
Pasi Kallinen
412a996da8 Wand of probing reveals tin contents 2023-10-01 14:49:00 +03:00
PatR
e2203ef9a1 stone-to-flesh of wielded rocks
Wielding one or more rocks while other rocks were quivered and casting
stone-to-flesh at self wasn't subject to the "null obj after quiver
merge" panic but it did result in a combined stack of meatballs that
was neither wielded nor quivered.  Keep inventory items that turn into
meat and are wielded/alt-wep/quivered separate and still wielded/&c.
2023-09-19 14:43:17 -07:00
Pasi Kallinen
44a649b0b4 Wand of probing reveals map locations in ray path 2023-09-18 19:20:01 +03:00
Michael Meyer
8fa53d6ac5 Adjust seenres when observed attack succeeds
If a monster sees an elemental attack succeed from some other creature
or environmental danger, it will be willing to try those attacks again.
2023-08-29 12:05:48 +03:00
PatR
e9c58c2fe4 breaking crystal armor
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.
2023-06-14 15:54:04 -07:00
PatR
450f060132 github issue #1060 - crystal helmet
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
2023-06-14 06:13:11 -07:00
PatR
62eacb11c4 bhit()'s use of bhitpos
bhit() updates the value of bhitpos as a zap or missile progresses
and it also maintains a copy in local x,y.  But it was using the
latter inconsistently and also used x,y to fetch drawbridge location,
which might retain the same value or might change one of the two
values by +1 or -1.  Use different local variables for drawbridege
handling and use x,y instead of bhitpos throughout bhit().

Also, zapping the spot in front of a closed bridge with striking or
force bolt would destroy the bridge even though the zap was just
passing over moat.  Don't destroy the bridge in that situation.
Zapping striking/force bolt across the span of an open bridege still
destroys the bridge.
2023-06-07 15:58:47 -07:00
PatR
3f4634211f fix #K3918 - statue-to-boulder polymorph in water
'sanity_check' complains if it finds a boulder in water or lava.
Polymorphig a statue usually produces another statue but might
produce a boulder.  If done it water, keeping the boulder intact
would trigger the sanity warning.  Break it into rocks if object
polymorph produces a boulder at water or lava location.
2023-05-08 02:07:31 -07:00
Pasi Kallinen
d036116dee Fix hiding under nonexistent obj
Zapping a wand of teleportation at a location with object and
a monster hiding under it, but with the level full of monsters,
the monster would stay hidden even when the object was moved.
2023-05-06 20:13:10 +03:00
PatR
180042434e more engraving sanity feedback
Delete engravings made in a breach of a shop's wall or of a vault's
wall or in the guard's temporary corridor when the wall is repaired
or the corridor removed.  If 'sanity_check' was On, those would
trigger impossible warning "engraving sanity: illegal surface (x)"
where x was the terrain type code for solid rock or relevant walls.

Adding del_engr_at() calls to the shop code was straightforward.
The vault code is very complicated and I'm not sure that all the
calls I added were actually necessary.
2023-05-04 06:02:23 -07:00
PatR
654b7d41b2 fix #K3907 - reviving buried corpse
Burying an olog-hai corpse with a boulder resulted in a panic when
its time to revive occurred.  I was able to reproduce this once but
failed with "you feel less hassled" several times (using same save
file for multiple tests) so I'm not quite sure what was happening.

A buried corpse was allowed to revive if it was for a zombie.  This
fix extends that to auto-revivers (trolls and Riders).  The corpse
keeps its revive_mon timer rather than changing that to zombify_mon.

If/when revival of a buried troll or Rider happens while in view, it
will "claw itself out of the ground" like zombies do.
2023-05-02 18:02:02 -07:00
nhmall
826ce951e7 get rid of NetHack macro conflict with curses routine delay_output() 2023-04-21 08:25:53 -04:00
copperwater
dad1c3f8b7 Fix: breathless monsters always generate in water in special levels
... 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.)
2023-04-16 09:57:41 +03:00
copperwater
95972d7147 Wand of speed gives player temporary speed and potion gives intrinsic
This aims to fix the issue in which there are way more wands of speed
monster in the game than the player has any use for. Usually, one is
enough for the whole game (unless a player has a lot of pets they want
to speed up) but since they're an item monsters can generate with, it's
typical to see eight to twelve of them, useless for anything besides
polypiling into other wands since they just grant an intrinsic that's
usually obtained early in the game.

By making the wand effect (on the player) temporary very fast speed, it
becomes desirable to keep the wand around again. By adding a lightweight
source of very fast speed, it also helps diversify character builds away
from always relying on speed boots. And importantly, it becomes an item
the player can use situationally early in the game to enhance their odds
in a fight or run away from danger.

Meanwhile, the speed-intrinsic-granting has been moved to the potion,
and the potion is still superior to the wand in the duration of very
fast speed it grants (100 + d10 turns for an uncursed potion or 160 +
d10 turns for a blessed one, versus 50 + d25 turns for the wand).

Since monsters don't have a concept of very fast speed, this doesn't
change the wand or potion effects on them. They will still gain
permanent intrinsic speed by either method.
2023-04-16 09:33:56 +03:00
PatR
a93ee09d1a fix github issue #1011 - monster imagination
Issue reported by AndrioCelos:  if hero was killed by a wand zapped
by a monster, the cause of death was "killed by <wand damage>
imagined by <the monster>" instead of intended "killed by <wand
damage> zapped by <the monster>".  Report mentioned that the monster
was unseen but that wasn't relevant.

This worked when monster wand zaps were briefly changed to use
-9..-0 but got broken when that was reverted to using -39..-30 (due
to -0 being the same as 0 so making zapper of wands of magic missile
ambiguous).

Fixes #1011
2023-04-15 12:34:27 -07:00
copperwater
886394c9c7 Give a message when a monster is zapped with make invisible
Every other case I'm aware of where a visible monster that the hero can
see turns invisible has some associated message, but not when zapping it
with the wand of make invisible and lacking see invisible. This adds a
message "[monster] vanishes!" in this case, which is the same as the
most common message one gets for zapping a monster with teleportation,
by design to keep the wand's identity ambiguous.

Technically, prior to this commit, there was a leak of information if
one zapped a wand that made a monster disappear: the identity of the
want could be determined immediately by the presence or absence of a
vanishing message. Practically, though, it was easy to check if there
was a new invisible monster in the same spot or not.

Also, zapping teleportation at a monster and having it land somewhere
visible to the hero now has attention explicitly drawn to it with a
message (in prior versions it didn't, so it could potentially be a
different monster appearing by some other means), so I additionally made
the wand of teleportation automatically identify itself when the hero
sees the monster appear as a result of their zap.
2023-04-01 10:00:57 +03:00
PatR
82fc66da0e wands zapped by monsters
Reverse part of commit 4021a63bcf
"wand/spell/breath killer reason" so that wand of magic missile
zapped by a monster isn't ambiguous about who is responsible.
2023-03-31 10:59:48 -07:00
nhmall
de79240dea some comment spelling fixes 2023-03-16 22:27:01 -04:00
PatR
4021a63bcf wand/spell/breath killer reason
Extend "killed by the touch of death inflicted by <monster>" to buzz().
"Killed by a bolt of cold" becomes "killed by a bolt of cold zapped by
<monster>" or "killed by a blast of cold" becomes "killed by a blast
of cold exhaled by <monster>" and so forth.

More work than expected; the zap code isn't passed enough context.

BZ_M_WAND() was producing the wrong value for wands zapped by monsters.
2023-03-07 02:47:42 -08:00
Pasi Kallinen
ce0d25129e Spell of knock can knock back small monsters
Differentiates the spell from the wand of opening,
and the spell name sounds like it should do that ...
2023-03-04 19:07:34 +02:00
Pasi Kallinen
4ce149b2cf Lava walls vs cold rays
Lava walls have a small chance of turning into solid stone wall
when hit by a cold ray. Before this they turned to floor, which
was obviously wrong.
2023-02-22 11:55:24 +02:00
Pasi Kallinen
91dc1ea834 Fix angering monsters with non-hero magic rays
Can't use gc.context.mon_moving to check if the ray was caused
by hero, as some rays can happen while hero is moving in response
to their action - for example a bolt of lightning from a god.
dobuzz() uses positive type values for magic rays caused by hero,
so just use that.
2023-02-15 11:28:09 +02:00
Michael Meyer
7f0bbc28a8 Don't anger target via mon vs mon zap/breath
dobuzz() was modified in 677b32c2a7 to anger the target, but beams
handled by dobuzz() don't necessarily originate from the hero.  Monsters
can zap wands that hit other monsters, dragons can use their breath
attacks, etc.  Those things were causing the targets to become angry at
the hero.
2023-02-14 09:22:56 +02:00
Pasi Kallinen
7401b44fa1 Walls of lava
Add "walls of lava", basically lava which blocks vision and
require a bit more than just levitation or flight to move through.

No levels use this yet, as testing isn't thorough enough.
2023-02-06 19:23:42 +02:00
nhmall
5b3a8b5774 more Soundeffects tinkering 2023-01-27 23:41:54 -05:00
Pasi Kallinen
52c98b300d Wrong order of params 2023-01-21 17:15:38 +02:00
Pasi Kallinen
677b32c2a7 Hit and wake sleeping monster makes it growl
Unless you kill the monster with one hit, it'll wake up
cranky and make noise - waking up other sleeping monsters.

This was a bit tricky with all the message sequencing; I tested
all the hit/throw/fire/zap combos I could think of, and it took
a while to get things looking right.
2023-01-21 16:52:23 +02:00
nhmall
28cd188259 resolve 5 analyzer warnings if no SND_LIB_* define 2023-01-20 17:32:55 -05:00
nhmall
07d3b22b4e whitespace cleanup on files just modified 2023-01-20 14:28:33 -05:00
nhmall
8bbe9282aa add soundeffects hooks to core
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.
2023-01-20 14:20:08 -05:00
Pasi Kallinen
242a7ba796 Boomerang hitting a sink is noisy 2023-01-19 19:59:19 +02:00
PatR
d20f670710 striking/force-bolt comment
I'm not sure whether newsym_force() is still necessary, but add a
comment about why it's being used in bhito().
2023-01-02 14:25:23 -08:00
PatR
608490ad98 more zapping while hiding-under
The previous commit had the up/down test backward.  Also the commit
log text described the old behavior incorrectly:  zapping down while
hiding-under skipped the top item but zapping up hit the whole pile.

Still not adequately tested.
2022-12-10 18:00:44 -08:00
PatR
0e1f1c653b fix #K3802 - sanity_check: boulder not on top
This should fix the problem of polymorphing or stone-to-fleshing a
pile of multiple boulders and having some underneath ones which get
changed resist and not get changed, producing a pile with one or more
non-boulders above one or more boulders.  If that situation arises,
re-stack the pile so that boulders are moved to the top.

This also revises zapping up or down while hiding under something
(if that is even possible; the types of creatures which can hide
under things can't zap wands or cast spells; maybe there are some
exceptions?).  Zapping up used to hit only the top item, but zapping
down hit the whole stack.  Now up still hits only the top, but down
skips the top and hits the rest.

Caveat: not adquately tested.
2022-12-10 17:48:55 -08:00
Patric Mueller
68f9ef8aa6 Followup to "suicide by wand", remove redundant an() 2022-12-07 15:54:30 +01:00
PatR
f52a8e48b1 suicide by wand
If hero zaps self with a wand and the result is fatal, report the
death as "zapped himself with <a wand of sometype>" rather than just
"zapped himself with a wand".
2022-12-06 11:41:24 -08:00
nhmall
02a48aa8cf split g into multiple structures
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
     ^ ^
2022-11-29 21:53:21 -05:00