Initially diagnosed in an xnethack fuzzer crash - unblock_point
shouldn't be called when a closed door becomes non-closed, because it's
possible that there's a gas cloud on the space which means it still
blocks vision. These always need to be recalc_block_point. A number of
them were fixed, but when I went through all the xnethack ones, I found
some that were unchanged from upstream NetHack. I reproduced the sanity
check impossibles usually by breathing gas at a door as an iron golem
and then opening or destroying the door to trigger the unblock_point
call.
The use of recalc_block_point in wizterrainwish was not triggering this
bug, but the previous code there basically duplicated
recalc_block_point.
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.
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
After a music.c change earlier today, an annoying compiler warning resulted.
.\music.c(721): warning C4295: 'notes': array is too small to include
a terminating null character
After the drawbridge was destroyed, playing an instrument on the castle
level while knowing the tune continued to offer a chance to play it.
Then nothing interesting happened even if you were close enough to the
former bridge for it to have been useful prior to the destruction.
I think the hero could also be given the tune as a divine prayer boon
after bridge destruction but I didn't verify that. The player might
not know that the tune is no good anymore, but the hero's patron deity
should.
Issue reported by mkuoppal: drum of earthquake triggers a sanity
check warning which the fuzzer escalated to panic.
Analysis by entrez. If a pit gets created next to a pool or moat or
lava, liquid might flow there and replace the pit. But the drum of
earthquake code assumed that the pit was still there. If there was a
monster there and it wasn't levitating or flying and it wasn't killed
by the liquid, it got marked as trapped even though the pit was gone.
'sanity_check' noticed.
With difficulty I was able to reproduce the impossible warning before
the fix, but the are a bunch of random factors at play. After the
fix I can't reproduce it again, but that's not a guarantee that it's
actually fixed. The analysis seems correct though and the fix is
based on dealing with that.
Closes#1170
Fumbling was apparently meant to make it harder to keep your footing
when an earthquake created a pit under you, requiring a 1/5 roll to
stay upright, but because it was added as an additional OR it actually
just gave the hero an additional (albeit unlikely) chance to retain her
footing. Make it actually have a negative impact on the hero's ability
to retain his footing rather than a minor boost.
This is for completely destroying an altar with extra-powerful magical
digging -- the normal altar_wrath() punishment didn't seem sufficient
for such an outrage to me, so skip straight to slinging the lightning
bolts. Destroying an altar is unlikely to happen by accident (though
it's possible with poorly timed usage of a drum of earthquake).
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.
I saw this included in PR966, but it has nothing to do with the
use-after-free that the pull request is meant to fix. It should
get applied independently of that pull request.
Use macOS AppKit framework routines for a first cut at a
macsound soundlib interface.
Requires WANT_MACSOUND=1 on build.
Nothing has been done to move the stock sounds into the resources
of a bundle, so after building, if you want to try the stock sounds
out:
cp sound/wav/*.wav ~/Library/Sounds
Because the NSSound macOS routines always do the search, supposedly
the following locations are searched in this order:
1. the application’s main bundle
2. ~/Library/Sounds
3. /Library/Sounds
4. /Network/Library/Sounds
5. /System/Library/Sounds
Although not specifically implemented as of yet, it may be pretty
close to being able to put soundeffects wav files (by se_ name)
into ~/Library/Sounds working for the SND_SOUNDEFFECTS_AUTOMAP feature.
Feedback is welcome. Contributions for improving it are even more
welcome.
The new soundlib supporting file is named
sound/macsound/macsound.m since it's got objective C in it.
Known bugs and glitches:
The Hero_playnotes on a set of 5 notes goes too fast, so there
needs to be a slight delay added between the note of a multi-note
play.
Expand windsound to support SNDCAP_USERSOUNDS (existing),
SNDCAP_SOUNDEFFECTS, SNDCAP_HEROMUSIC.
Place a small number of wav files into sound/wav that are considered
more specific to some game stuff in the core, namely: playing
instruments by the hero, squeaky board traps. The intention is that
those ones can be integrated into the game without requiring user
file deposits or mappings. Building soundlib support for sound/windsound
searches for them as resources, so they should "just work."
No actual instrument playback is available in the base sound/windsound
soundlib implementation (yet, at least), so it works around that in
a cheap way by using the note-variant wav sounds to concoct the
notes. There are better ways of doing music generation than that,
of course, but this will do in the early going. Any other soundlib
port (such as Qt) can probably easily do the exact same thing. If it
can play USER_SOUNDS, it can almost certainly use the
sound/wav/se_squeak*.wav and sound/wav/sound_*.wav files for
SND_HEROMUSIC and squeaky board sounds soundeffects.
A few of the se_ sounds in sound/wav are free yet licensed, and
there is a file called attributions.txt to meticulously attempt
to comply with the two license variations involved.
SND_SOUNDEFFECTS_AUTOMAP
If SND_SOUNDEFFECTS_AUTOMAP is defined, and a SND_LIB_ interface
that supports SNDPROC_SOUNDEFFECTS is also defined to integrate
soundlib support, the following gets added:
o If sounddir is defined (existing longstanding feature that's
been used with USER_SOUNDS for many, many years) any wav file
present in SOUNDDIR with a name that matches one of the defined
sound effects in include/sndproc.h will get played each time
that soundeffect comes up. So, just drop appropriate wav files
into sounddir and rename it to match.
No tedious config file entries are required to get soundeffects.
[ The only config file change required is to
set SOUNDDIR to point to your directory with the wav files.
Note: SOUNDDIR only works in your config file if NetHack
was built with USER_SOUNDS defined. ]
This is new so there may certainly be some bugs in here.
Please kindly report them if you encounter any.
For those that don't read C very well, these are the file names
that should trigger the SOUNDEFFECTS_AUTOMAP, assuming it is working.
se_faint_splashing.wav
se_crackling_of_hellfire.wav
se_heart_beat.wav
se_typing_noise.wav
se_hollow_sound.wav
se_rustling_paper.wav
se_crushing_sound.wav
se_splash.wav
se_chains_rattling_gears_turning.wav
se_smashing_and_crushing.wav
se_gears_turning_chains_rattling.wav
se_loud_splash.wav
se_lound_crash.wav
se_crashing_rock.wav
se_sizzling.wav
se_crashing_boulder.wav
se_boulder_drop.wav
se_item_tumble_downwards.wav
se_drain_noises.wav
se_ring_in_drain.wav
se_groans_and_moans.wav
se_scratching.wav
se_glass_shattering.wav
se_egg_cracking.wav
se_gushing_sound.wav
se_glass_crashing.wav
se_egg_splatting.wav
se_sinister_laughter.wav
se_blast.wav
se_stone_breaking.wav
se_stone_crumbling.wav
se_snakes_hissing.wav
se_loud_pop.wav
se_clanking_pipe.wav
se_sewer_song.wav
se_monster_behind_boulder.wav
se_wailing_of_the_banshee.wav
se_swoosh.wav
se_explosion.wav
se_crashing_sound.wav
se_someone_summoning.wav
se_rushing_wind_noise.wav
se_splat_from_engulf.wav
se_faint_sloshing.wav
se_crunching_sound.wav
se_slurping_sound.wav
se_masticating_sound.wav
se_distant_thunder.wav
se_applause.wav
se_shrill_whistle.wav
se_someone_yells.wav
se_door_unlock_and_open.wav
se_door_open.wav
se_door_crash_open.wav
se_dry_throat_rattle.wav
se_cough.wav
se_angry_snakes.wav
se_zap_then_explosion.wav
se_zap.wav
se_horn_being_played.wav
se_mon_chugging_potion.wav
se_bugle_playing_reveille.wav
se_crash_through_floor.wav
se_thump.wav
se_scream.wav
se_tumbler_click.wav
se_gear_turn.wav
se_divine_music.wav
se_thunderclap.wav
se_sad_wailing.wav
se_maniacal_laughter.wav
se_rumbling_of_earth.wav
se_clanging_sound.wav
se_mutter_imprecations.wav
se_mutter_incantation.wav
se_angry_voice.wav
se_sceptor_pounding.wav
se_courtly_conversation.wav
se_low_buzzing.wav
se_angry_drone.wav
se_bees.wav
se_someone_searching.wav
se_guards_footsteps.wav
se_faint_chime.wav
se_loud_click.wav
se_soft_click.wav
se_squeak.wav
se_squeak_C.wav
se_squeak_D_flat.wav
se_squeak_D.wav
se_squeak_E_flat.wav
se_squeak_E.wav
se_squeak_F.wav
se_squeak_F_sharp.wav
se_squeak_G.wav
se_squeak_G_sharp.wav
se_squeak_A.wav
se_squeak_B_flat.wav
se_squeak_B.wav
se_someone_bowling.wav
se_rumbling.wav
se_loud_crash.wav
se_deafening_roar_atmospheric.wav
se_low_hum.wav
se_laughter.wav
se_cockatrice_hiss.wav
se_chant.wav
se_cracking_sound.wav
se_ripping_sound.wav
se_thud.wav
se_clank.wav
se_crumbling_sound.wav
se_soft_crackling.wav
se_crackling.wav
se_sharp_crack.wav
se_wall_of_force.wav
se_alarm.wav
se_kick_door_it_shatters.wav
se_kick_door_it_crashes_open.wav
se_bubble_rising.wav
se_bolt_of_lightning.wav
se_board_squeak.wav
se_board_squeaks_loudly.wav
se_boing.wav
se_crashed_ceiling.wav
se_clash.wav
se_crash_door.wav
se_crash.wav
se_crash_throne_destroyed.wav
se_crash_something_broke.wav
se_kadoom_boulder_falls_in.wav
se_klunk_pipe.wav
se_kerplunk_boulder_gone.wav
se_klunk.wav
se_klick.wav
se_kaboom_door_explodes.wav
se_kaboom_boom_boom.wav
se_kaablamm_of_mine.wav
se_kaboom.wav
se_splat_egg.wav
se_destroy_web.wav
se_iron_ball_dragging_you.wav
se_iron_ball_hits_you.wav
se_lid_slams_open_falls_shut.wav
se_chain_shatters.wav
se_furious_bubbling.wav
se_air_crackles.wav
se_potion_crash_and_break.wav
se_hiss.wav
se_growl.wav
se_canine_bark.wav
se_canine_growl.wav
se_canine_whine.wav
se_canine_yip.wav
se_canine_howl.wav
se_feline_yowl.wav
se_feline_meow.wav
se_feline_purr.wav
se_feline_yip.wav
se_feline_mew.wav
se_roar.wav
se_snarl.wav
se_buzz.wav
se_squeek.wav
se_squawk.wav
se_squeal.wav
se_screech.wav
se_equine_neigh.wav
se_equine_whinny.wav
se_equine_whicker.wav
se_bovine_moo.wav
se_bovine_bellow.wav
se_wail.wav
se_groan.wav
se_grunt.wav
se_gurgle.wav
se_elephant_trumpet.wav
se_snake_rattle.wav
se_hallu_growl.wav
This needs further testing. At the moment only windsound has
this.
If nobody else looks into it for Qt, I'll take a look in a
few days.
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.
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.
Add macros to convert AD_foo, WAN_foo, and SPE_foo to relative values
for passing to BZ_U_foo and BZ_M_foo macros.
Change some return values in monster spellcasting function from
magic numbers to MM_MISS or MM_HIT.
Make buzzmu consider hero resistances - previously the
monster with innate zapping ray (Angels and Asmodeus) would
just keep doing that attack, but they will now just curse if
it saw the hero resist the attack.
The log message for commit 231bd75b7f
said that magic harp was changed to behave the same as scroll/spell
of taming, but the scroll and spell pacify an angry shopkeeper even
if the shk resists. Change magic harp to do likewise.
Issue reported by youkan700: for shopkeepers, taming via magic harp
behaved differently than taming via scroll or spell.
Make magic harp's taming be the same as [non-cursed] scroll of taming
and spell of charm monster: angry shopkeepers will be pacified (even
though they can't be tamed).
Also, add something I've been sitting on for ages: when taming magic
hits an already tame monster, give that monster a chance to become
tamer. Not significant for monsters that eat (unless being starved
for some reason) but matters for ones who don't eat. For tameness N
(which has a maximum of 20), if N is less than 10, have any taming
yield a 10-N out of 10 chance to increase the tameness by 1. So the
closer a pet is to becoming feral, the more likely for it to improve
tameness a little.
Closes#819
High altars and normal temple altars had identical altarmasks, so
there was no way to distinguish between the two based on the altarmask
alone. Instead, anywhere it was necessary to determine whether an altar
was a high altar included a check whether the hero was currently the
Astral Plane or Moloch's Sanctum, and assumed any temple altar was the
high altar.
Since there's an extra, unused bit in altarmask anyway, use it to
explicitly mark high altars -- the lua level files already distinguish
between normal temple altars and so-called 'sanctum' altars anyway, so
rather than throwing away this distinction when generating the level,
keep it in the altarmask and use it in place of various u.uz checks.
I think this would require incrementing EDITLEVEL again...
Move some code that was used to decide whether to call distant_name
or doname into distant_name so that the places which were doing that
don't need to anymore and fewer places can care about whether an
artifact is being found. There were two or three instances of
distant_name maybe being called, based on distance from hero, and
yesterday's artifact livelog change added two or three more and made
all of them override the distance limit for artifacts.
After that change to distant_name, make sure that conditional calls
to it become unconditional--just not displayed for the cases where
!flags.verbose had been excluding them. That way distant_name can
decide whether an item is up close and arrange for xname to find it
if it as an artifact.
Also, implement an old TODO. Wearing the Eyes of the Overworld
extends the distance that an item can be from the hero and still be
considered near anough to be seen "up close" when monsters pick it
up or drop it. The explicit cases were using distu(x,y) <= 5, the
distance of a knight's jump. Each quadrant around the hero is a 2x2
square with the diagonal corner chopped off. The replacement code in
distant_name calculates a value of 6, which is functionally equivalent
since the next value of interest beyond 5 is 8. Wearing the Eyes
(deduced by having Xray vision) extends that threshold an extra step
in addition to overriding blindness and seeing through walls: 15,
a 3x3 square in each quadrant, still with the far diagonal corner (16)
treated as out of range.
Use up the last available bit for achievements:
"You learned the tune to open and close the castle's drawbridge."
(More can still be added but xlogfile will need another field to
track a second set of 31 in order to keep its achievement bitmask(s)
within portable size.)
As achievements go, it's not very exciting, but players who normally
destroy the drawbridge have to choose whether to earn an achievement
first since once it's gone, there's no way to find out the tune
(either via prayer reward or successful Mastermind). I'm guessing
that most will probably decide to ignore this achievement since it
has no effect on the outcome of the game. However, that might not
be true for future tournament play.
There's no need to bump EDITLEVEL for this; room for recording one
additional achievement is already allocated.