Commit Graph

182 Commits

Author SHA1 Message Date
Alex Smith
4d043a6f9c Monsters require experience using wands before they can hit with them
It takes time for an early-game monster to acclimatize itself to the
power of an attack wand: in much the same way as a nervous human may
quite possibly miss with their first attempt to use a gun in combat,
an early-game monster will always miss on its first use of an attack
wand (but from then on will understand how they work and get over
their nerves, and will hit as normal).

This is a balance change based on observed results from tournaments:
guarding against deaths to early-game attack wands requires an
unusually cautious playstyle which isn't much fun (and might not
always be possible even for the best players), so it is quite common
for them to be the cause of random deaths that it wasn't worth trying
to avoid. Although trying to dodge a monster who found an attack wand
is fun, you only actually get that fun if something makes you aware
of the danger: the monster missing with the wand is a clear way to
demonstrate the danger and let the player know that now is the right
time to take precautions.

This change could theoretically have broken saves, but probably
doesn't due to there having been a spare bit in struct monst.  Just
in case, it is being pushed together with other save-breaking changes
to avoid the need for multiple bumps to EDITLEVEL.
2026-03-19 03:55:10 +00:00
PatR
aae7778d66 github issue #1472 - elven monster bow bonus
Issue reported by Tomsod: monster elves were intended to get a small
bonus to to-hit and damage when shooting arrows with bows, but the
check for that tested the arrows for skill P_BOW which never matches.
It should be -P_BOW.

[Pretty minor: +1 to-hit for any bow, another +1 to-hit if elven bow;
+1 damage for elven arrow; against hero and against other monsters.]

Fixes #1472
2025-12-23 23:28:58 -08:00
Alex Smith
8c29b20010 Accurately track which items have been discovered, even if not #named
This fixes a couple of bugs: a long-standing bug in which writing a
scroll by label could fail even if you've already seen a scroll with
that label (due to the game not tracking whether or not you've seen a
scroll if it doesn't have a name); and a somewhat newer bug in which
spellbooks auto-identified by Wizard knowledge were marked as having
been encountered (rather than as known but not encountered).

Breaks save file compatibility, but not bones files.
2025-11-25 22:42:38 +00:00
nhmall
fe357a6f04 avoid telling hero they missed a monster they aren't aware of
Resolves #1441
2025-09-04 22:13:07 -04:00
nhmall
7e44aad627 address a static analyzer complaint
mthrowu.c: In function ‘hit_bars’:
  mthrowu.c:1419:25: warning: ‘maybe_unused’ attribute ignored [-Wattributes]
  1419 |            static enum sound_effect_entries se[] SOUNDLIBONLY = {
  mthrowu.c:1419:46: warning: unused variable ‘se’ [-Wunused-variable]
  1419 |            static enum sound_effect_entries se[] SOUNDLIBONLY = {
2025-08-31 10:30:23 -04:00
PatR
afc34c9873 mon throw-and-return cleanup
Fix up a few comments in the monster throwing code.  And change a
couple 'if (!Blind)' checks to use 'if (canseemon(magr))' instead
of that the player won't be told about a returning aklys hitting an
invisible monster ("it") on the arm.
2025-04-05 09:50:38 -07:00
PatR
500af49dd3 fix issue #1407 - mon throw-and-return crash
Issue reported by k21971:  a gnome throwing a wielded aklys at the
hero was killed when failing to catch its return.  Bookkeeping for
dead monsters got messed up, then a crash occurred.

This fixes things.  Instead of a comment stating that the thrower
might be dead, kill it off.
2025-04-05 09:44:02 -07:00
nhmall
e8c401acb3 follow-up: numeric values in comment fix 2025-03-19 13:38:12 -04:00
nhmall
a943c4c10b replace some weight-related magic numbers
adds a header file include/nhconst.h  (I'm open to a better name)
2025-03-19 13:29:58 -04:00
nhmall
d53698baea whitespace at end-of-line 2025-03-16 15:39:22 -04:00
nhmall
0bdf9830e6 throw-and-return weapons used by monsters
Resolves #1338
2025-03-16 15:37:49 -04:00
PatR
df06fc36f2 analyzer lint for m[o-u]*.c
The changes to muse.c are more extensive that most.  The many new
panic calls could be simplified by assigning a dummy object for the
trap cases.
2025-01-20 00:58:06 -08:00
Pasi Kallinen
7fa38eb7b3 Use pline_mon instead of pline_xy
Forgot about this function. Whoops.
2024-12-14 15:20:17 +02:00
nhmall
0792e5fe9e expand implicit fallthrough detection to non-gcc compilers
gcc has recognized various "magic comments" for white-listing
occurrences of implicit fallthrough in switch statements for
a long time:

    The range and shape of "falls through" comments accepted are
    contingent upon the level of the warning. (The default level is =3.)

    -Wimplicit-fallthrough=0 disables the warning altogether.
    -Wimplicit-fallthrough=1 treats any kind of comment as a "falls through" comment.
    -Wimplicit-fallthrough=2 essentially accepts any comment that contains something
     that matches (case insensitively) "falls?[ \t-]*thr(ough|u)" regular expression.
    -Wimplicit-fallthrough=3 case sensitively matches a wide range of regular
     expressions, listed in the GCC manual. E.g., all of these are accepted:
        /* Falls through. */
        /* fall-thru */
        /* Else falls through. */
        /* FALLTHRU */
        /* ... falls through ... */
       etc.
    -Wimplicit-fallthrough=4 also, case sensitively matches a range of regular
     expressions but is much more strict than level =3.
    -Wimplicit-fallthrough=5 doesn't recognize any comments.

Plenty of other compilers did not recognize the gcc comment convention,
and up until now the compiler warning for detecting unintended
fallthrough had to be suppressed on other compilers. That's because the code
in NetHack has been relying on the gcc approach, and only the gcc approach.

The C23 standard introduces an attribute [[fallthrough]] for the
functionality, when implicit fallthrough warnings have been enabled.

Several popular compilers already support that, or a very similar attribute
style approach, today, even ahead of their C23 support:

       C compiler                       whitelist approach
       ---------------------------   -------------------------------------
       C23 conforming compilers         [[fallthrough]]

       clang versions supporting
       standards prior to
       C23                              __attribute__((__fallthrough__))

       Microsoft Visual Studio
       since VS 2022 17.4.
       The warning C5262 controls
       whether the implict
       fallthrough is detected and
       warned about with
       /std:clatest.                    [[fallthrough]]

This adds support to NetHack for the attribute approach by inserting a
macro FALLTHROUGH to the existing cases that require white-listing, so
other compilers can analyze things too.

The definition of the FALLTHROUGH macro is controlled in include/tradstdc.h.

The gcc comment approach has also been left in place at this time.
2024-11-30 14:16:27 -05:00
nhmall
1dbba0f63b rename IS_ROCK() macro to IS_OBSTRUCTED()
It has included trees since they were added, so give it a
more fitting name.
2024-11-09 11:12:42 -05:00
PatR
cc1066b1ff some reformatting (3 of 4) 2024-09-05 15:55:24 -07:00
nhmall
6c0ae092c6 distinguish global variables that get written to savefile
The g? structs had a mix of variables that were written to
the savefile, and those that were not.

For better clarity and to distinguish those that end up in
the savefile, relocate some g? variables that get written
directly to the savefile into different structs.

This updates EDITLEVEL, although technically it probably
didn't need to, since savefile contents are not changing.

Details:

    gb.bases            -> svb.bases
    gb.bbubbles         -> svb.bbubbles
    gb.branches         -> svb.branches
    gc.context          -> svc.context
    gd.disco            -> svd.disco
    gd.dndest           -> svd.dndest
    gd.doors            -> svd.doors
    gd.doors_alloc      -> svd.doors_alloc
    gd.dungeon_topology -> svd.dungeon_topology
    gd.dungeons         -> svd.dungeons
    ge.exclusion_zones  -> sve.exclusion_zones
    gh.hackpid          -> svh.hackpid
    gi.inv_pos          -> svi.inv_pos
    gk.killer           -> svk.killer
    gl.lastseentyp      -> svl.lastseentyp
    gl.level            -> svl.level
    gl.level_info       -> svl.level_info
    gm.mapseenchn       -> svm.mapseenchn
    gm.moves            -> svm.moves
    gm.mvitals          -> svm.mvitals
    gn.n_dgns           -> svn.n_dgns
    gn.n_regions        -> svn.n_regions
    gn.nroom            -> svn.nroom
    go.oracle_cnt       -> svo.oracle_cnt
    gp.pl_character     -> svp.pl_character
    gp.pl_fruit         -> svp.pl_fruit
    gp.plname           -> svp.plname
    gp.program_state    -> svp.program_state
    gq.quest_status     -> svq.quest_status
    gr.rooms            -> svr.rooms
    gs.sp_levchn        -> svs.sp_levchn
    gs.spl_book         -> svs.spl_book
    gt.timer_id         -> svt.timer_id
    gt.tune             -> svt.tune
    gu.updest           -> svu.updest
    gx.xmax             -> svx.xmax
    gx.xmin             -> svx.xmin
    gy.ymax             -> svy.ymax
    gy.ymin             -> svy.ymin

Related note:
There are some pointer variables that are heads of chains that were not
moved from 'g?' to 'sv?', because they are not actually written to the
savefile directly, but the objects/monst/trap/lightsource/timer in the
chains they point to are. That can be changed, if desired.
Examples: gi.invent, gm.migrating_objs, gb.billobjs, gm.migrating_mons,
          gf.ftrap, gl.light_base, gt.timer_base
2024-07-13 14:57:50 -04:00
Pasi Kallinen
a0256ef5cb Fix monster vs monster breath attacks
The same routine handles both monster-vs-hero and monster-vs-monster
breath attacks, but the code was considering what the attacker knows
about hero's resistances even when attacking another monster.

Also unconditionally prevent the monster breath attack if the monster
knows about hero reflecting - previously the monster could use the breath
if it were just far enough from hero, even when hero had reflection.
2024-04-27 21:18:58 +03:00
nhkeni
54c3dd35ac Merge branch 'keni-staticfn' into NetHack-3.7 2024-03-16 09:38:21 -04:00
nhmall
79648c6ce2 some variables not referenced in another translation unit made static
Also adds some cross-refence comments for some variables that are
referenced in another translation unit.
2024-03-15 16:00:14 -04:00
nhkeni
9c0ed8ae63 NOSTATICFN for src/* 2024-03-14 17:41:51 -04:00
nhmall
0a985459f0 make style consistent for function ptr arguments 2024-02-19 17:21:04 -05:00
nhmall
688ac6ffbe remove register from variable declarations 2024-02-19 16:30:07 -05:00
Pasi Kallinen
7fa74d0199 Add LAVAWALL to linedup blocking 2024-01-23 18:37:16 +02:00
Pasi Kallinen
7353bf0e69 Message location for monster throwing or shooting at hero 2024-01-04 11:43:13 +02:00
Pasi Kallinen
dc8d9d6cd0 Accessibility: Add location info to messages
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.
2024-01-02 18:59:25 +02:00
nhmall
c5a5b55c15 nonnull for some static functions during recent analysis 2023-12-16 10:51:59 -05:00
nhmall
70dcab833d remove obj guard from stone_missile(obj) macro
Checking the callers:
toss_up() would have segfaulted prior to use of stone_missile() if obj were NULL.
thitu() now has a guard prior to use of stone_missile()
ohitmon() would have crashed from earlier dereference otmp->dknown if it were NULL,
   otmp arg is declared nonnull
thitm() now has a guard prior to use of stone_missile().
hmon_hitmon_do_hit() null obj takes a different code path than the code path
    using stone_missile(); comment asserting that added
2023-12-16 07:58:44 -05:00
Pasi Kallinen
5dc94f3d83 Macro for picking random entry from array 2023-12-05 10:06:27 +02:00
nhmall
a7242760f7 consistent cast syntax 2023-11-13 19:28:19 -05:00
Michael Meyer
d42564bacd Better align drop_throw with hero ammo breakage
There is a comment above the function indicating that it should be
aligned with hero ammo breakage, but this wasn't the case.  One big
difference is that any monster-thrown or -shot object would be deleted
unconditionally if it hit another monster trapped in a pit.  I don't
know why that was in there, but it's not present in hero ammo breakage
chances, and it meant that a monster could sling the Mines luckstone at
the hero, hit a monster in the pit, and permanently lock the hero out of
getting the luckstone (as just happened to a player during the current
tournament).  This pulls the hero breakage rules out into their own
function and uses that for monster breakage as well, to make sure they
are aligned.  I also refactored drop_throw a bit to reduce the number of
separate variables tracking whether the object was deleted (was create,
objgone, and retvalu), and changed its (and ohitmon's) type to boolean.
2023-11-11 01:17:54 -08:00
nhmall
6cbefc7c2d Revert "granular verbose message suppression mechanics"
This reverts commit be76727265.
2023-10-29 20:39:07 -04: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
1c94bdac89 blindness overhaul
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.
2023-04-27 14:53:28 -07:00
nhmall
826ce951e7 get rid of NetHack macro conflict with curses routine delay_output() 2023-04-21 08:25:53 -04:00
Pasi Kallinen
ce1f4788cd Angering monsters hit by items from exploding bag of holding 2023-04-08 18:25:45 +03:00
nhmall
5f69dc6228 make attack result macros more distinguishable from makemon macros
Use the MM_ prefix only for the makemon macros, and change these five as follows:

 MM_MISS 0x0     -> M_ATTK_MISS      /* aggressor missed */
 MM_HIT 0x1      -> M_ATTK_HIT       /* aggressor hit defender */
 MM_DEF_DIED 0x2 -> M_ATTK_DEF_DIED  /* defender died */
 MM_AGR_DIED 0x4 -> M_ATTK_AGR_DIED  /* aggressor died */
 MM_AGR_DONE 0x8 -> M_ATTK_AGR_DONE  /* aggressor is done with their turn */

include/hack.h:#define NO_MM_FLAGS     0x000000L /* use this rather than plain 0 */
include/hack.h:#define MM_NOWAIT       0x000002L /* don't set STRAT_WAITMASK flags */
include/hack.h:#define MM_NOCOUNTBIRTH 0x000004L /* don't increment born count (for revival) */
include/hack.h:#define MM_IGNOREWATER  0x000008L /* ignore water when positioning */
include/hack.h:#define MM_ADJACENTOK   0x000010L /* acceptable to use adjacent coordinates */
include/hack.h:#define MM_ANGRY        0x000020L /* monster is created angry */
include/hack.h:#define MM_NONAME       0x000040L /* monster is not christened */
include/hack.h:#define MM_EGD          0x000100L /* add egd structure */
include/hack.h:#define MM_EPRI         0x000200L /* add epri structure */
include/hack.h:#define MM_ESHK         0x000400L /* add eshk structure */
include/hack.h:#define MM_EMIN         0x000800L /* add emin structure */
include/hack.h:#define MM_EDOG         0x001000L /* add edog structure */
include/hack.h:#define MM_ASLEEP       0x002000L /* monsters should be generated asleep */
include/hack.h:#define MM_NOGRP        0x004000L /* suppress creation of monster groups */
include/hack.h:#define MM_NOTAIL       0x008000L /* if a long worm, don't give it a tail */
include/hack.h:#define MM_MALE         0x010000L /* male variation */
include/hack.h:#define MM_FEMALE       0x020000L /* female variation */
include/hack.h:#define MM_NOMSG        0x040000L /* no appear message */

include/hack.h:#define MM_NOEXCLAM     0x400000L /* more sedate "<mon> appears." mesg for ^G */
include/hack.h:#define MM_IGNORELAVA   0x800000L /* ignore lava when positioning */
2023-03-19 12:19:34 -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
PatR
c212ee44c0 breaking iron bars with weapon
For strength over 18, A_CURR(A_STR) can return up to 125, giving
the chance to break bars by hitting them with a warhammer a 50:50
chance.  Switch to acurrstr() which returns at most 25.

Allow heavy iron balls (wielded or thrown, regardless of whether
they're chained to hero) to have a chance to break bars too.  They
are slightly more complicated because they don't use obj->spe like
a weapon but are otherwise straightfoward.
2023-02-03 17:01:49 -08:00
nhmall
4575d564c7 do warning suppression for soundlib vars another way
Instead of introducing a bunch of preprocessor #ifdef blocks,
this approach is less-intrusive.
2023-01-28 14:55:54 -05:00
nhmall
44677e82ab warning fix if no added soundlib compiled in 2023-01-28 09:09:39 -05:00
nhmall
5b3a8b5774 more Soundeffects tinkering 2023-01-27 23:41:54 -05:00
Pasi Kallinen
f61e1e8e23 Tiny chance of breaking iron bars with war hammer
Also add some different sounds to hitting the iron bars,
and make it noisy.
2023-01-26 18:21:53 +02: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
PatR
ddd358aa03 miscellaneous objects[] macros
Replace FIRST_GEM and LAST_GEM with FIRST_REAL_GEM, LAST_REAL_GEM,
FIRST_GLASS_GEM, and LAST_GLASS_GEM and define those along with
objects[] rather than separately.  Do the latter for FIRST_AMULET
and LAST_AMULET too.  Also new FIRST_SPELL and LAST_SPELL used to
compute MAXSPELLS.  (That value looks wrong to me, but this defines
it with the same value as before.  If it gets fixed, EDITLEVEL will
need to be incremented.)

This started as just proof of concept that extra information could
be collected as objects[] gets initialized at compile time.
2022-12-28 01:50:24 -08:00
nhmall
8120b74051 realign macro continuation after g to g? expansion 2022-12-07 11:24:17 -05: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
Michael Meyer
619781dbb8 Add 'mdistu' macro
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.
2022-11-18 23:42:47 -08:00
Michael Meyer
a0dfc94bbe Fix: "a dry rattle comes from its throat"
"A dry rattle comes from its throat" would be printed whenever a
canceled monster tried to spit at you or another monster while not in
the hero's line of sight.  That seemed weird to me: you can't see the
monster and don't know what it is, but you can tell the sound is
definitely coming from "its throat".

Change the message if the monster isn't visible, and make sure it's
printed it only if the monster is nearby (within reasonable hearing
range for a "dry rattle").
2022-11-18 23:42:47 -08:00
PatR
13fb141ddd more steadfast
Make changes similar to the suggested patch from entrez:  support
for 'youmonst' as the monster passed to m_carrying().  This doesn't
change carrying(otyp) to call m_carrying(&g.youmonst,otyp) though.

Also, treat being on the Plane of Air or in an air bubble on the
Plane of Water similar to flying or levitating:  wielded Giantslayer
(or carried loadstone) doesn't prevent knockback there.
2022-10-26 01:13:01 -07:00