diff --git a/.gitignore b/.gitignore index f5a18b7e3..5615fe332 100644 --- a/.gitignore +++ b/.gitignore @@ -96,3 +96,4 @@ bundle/* util/*.lib util/*.exp submodules/CHKSUMS.tmp +sys/amiga/regex/ diff --git a/Cross-compiling b/Cross-compiling index 38998a937..285e824b5 100644 --- a/Cross-compiling +++ b/Cross-compiling @@ -369,10 +369,11 @@ Using the cross-compiler, build the following targets: src/dlb.c, src/do.c, src/do_name.c, src/do_wear.c, src/dog.c, src/dogmove.c, src/dokick.c, src/dothrow.c, src/drawing.c, src/dungeon.c, - src/eat.c, src/end.c, src/engrave.c, src/exper.c, - src/explode.c, src/extralev.c, src/files.c, - src/fountain.c, src/getpos.c, src/glyphs.c, - src/hack.c, src/hacklib.c, src/insight.c, + src/earlyarg.c src/eat.c, src/end.c, src/engrave.c, + src/exper.c, src/explode.c, src/extralev.c, + src/files.c, src/fountain.c, src/getpos.c, + src/glyphs.c, src/hack.c, src/hacklib.c, + src/iactions.c, src/insight.c, src/invent.c, src/isaac64.c, src/light.c, src/lock.c, src/mail.c, src/makemon.c, src/mcastu.c, src/mdlib.c, src/mhitm.c, src/mhitu.c, src/minion.c, @@ -413,7 +414,7 @@ Using the cross-compiler, build the following targets: b) Lua (mandatory in 3.7) - lib/lua-5.4.6/src + lib/lua-5.4.8/src from sources: lua.c, lapi.c, lauxlib.c, lbaselib.c, lcode.c, lcorolib.c, lctype.c, ldblib.c, ldebug.c, @@ -528,11 +529,11 @@ Cross-compiler pre-built binary downloads: Disclaimer: This is a minimal recipe, just to help someone else get started if they have a desire to get a full cross-compile of NetHack going for the Amiga. - See CAVEATS below. Cross-compiler used: bebbo's amiga-gcc -Cross-compiler url: https://github.com/bebbo/amiga-gcc - +Cross-compiler url: https://github.com/AmigaPorts/m68k-amigaos-gcc +Author of 3.7 support: Ingo Paschke + To our knowledge, a pre-built copy of the cross-compiler isn't available, so you will likely have to obtain the cross-compiler sources via git and build it on your system. @@ -542,86 +543,84 @@ Cross-compiler url: https://github.com/bebbo/amiga-gcc sudo apt install make wget git gcc g++ lhasa libgmp-dev \ libmpfr-dev libmpc-dev flex bison gettext texinfo ncurses-dev \ - autoconf rsync + autoconf rsync libreadline-dev The build prerequisite packages for macOS via homebrew are documented but not tested by us any of us to date. brew install bash wget make lhasa gmp mpfr libmpc flex gettext \ - texinfo gcc make autoconf + gnu-sed texinfo gcc@12 make autoconf bison - After installing the prerequite packages and the cross-compiler - it was a straightforward build: + After installing the prerequisite packages and the cross-compiler + the build is straightforward: - git clone https://github.com/bebbo/amiga-gcc.git + Create the /opt/amiga directory for holding the compiler and tools. + sudo mkdir /opt/amiga + sudo chgrp users /opt/amiga + sudo chmod 775 /opt/amiga + sudo usermod -a -G users username + (you may have to log off and back on) + + Build the cross-compiler. + git clone https://github.com/AmigaPorts/m68k-amigaos-gcc.git amiga-gcc cd amiga-gcc make update - [Note that you may have to take ownership of the files in the bebbo - repo via chown before successfully carrying out the next steps] + The compiler pieces are installed in /opt/amiga by default, and the + NetHack Makefiles expect them there. - make clean - make clean-prefix - date; make all -j3 >&b.log; date + If you prefer, you can alter the prefix before you build if you want. + The instructions for doing so were spelled out at the time of this writing at: - The compiler pieces are installed in /opt/amiga by default. If you prefer, - you can alter the prefix before you build if you want. The instructions - for doing so were spelled out at the time of this writing at: + https://github.com/AmigaPorts/m68k-amigaos-gcc + Makefile adjustments will be needed, however. - https://github.com/bebbo/amiga-gcc + On your linux host: - On your linux host: - - cd sys/unix ; sh setup.sh hints/linux.370 ; cd ../.. + # Clone NetHack 3.7 source + cd NetHack + sys/unix/setup.sh sys/unix/hints/linux.370 make fetch-lua - - On your macOS host: - - cd sys/unix ; sh setup.sh hints/macOS.370 ; cd ../.. - make fetch-lua - - The Amiga cross-compile can then be carried out by specifying - CROSS_TO_AMIGA=1 on the make command line: - + make CROSS_TO_AMIGA=1 fetch-regex make CROSS_TO_AMIGA=1 all make CROSS_TO_AMIGA=1 package - You can explicitly include tty and curses support if desired, otherwise - you'll end up with a tty-only cross-compile build. The SDL1 pdcurses - support has not been tested. + The distribution ZIP is created at targets/amiga/NH370AMI.ZIP. - make WANT_WIN_TTY=1 WANT_WIN_CURSES=1 CROSS_TO_AMIGA=1 all + On your macOS host, the Amiga cross-compile can be carried out by specifying + CROSS_TO_AMIGA=1 on the make command line: + + # Clone NetHack 3.7 source + cd NetHack + sys/unix/setup.sh sys/unix/hints/macOS.370 + make fetch-lua + make CROSS_TO_AMIGA=1 fetch-regex + make CROSS_TO_AMIGA=1 all + make CROSS_TO_AMIGA=1 package Result: The "make package" target will bundle the (hopefully) necessary - components to run NetHack on msdos into a folder: + components to run NetHack on an Amiga into a folder: targets/amiga/pkg and then it zips the contents of that folder into: - targets/amiga/nh370ami.zip + targets/amiga/NH370AMI.ZIP - Also note that building the amiga targets using the make command - above, does not preclude you from building local linux or macOS - targets as well. Just drop the CROSS_TO_AMIGA=1 from the make - command line. + Display Modes + ------------- + Two display modes are available, selected in nethack.cnf: - The cross-compiler hints additions are enclosed inside ifdef sections - and won't interfere with the non-cross-compile build in that case. + Text mode (AMII): + OPTIONS=symset:AmigaFont - CAVEATS: The original NetHack Amiga build steps included the source for - some utilities that were built and executed on the amiga: - txt2iff and xpm2iff - as part of the NetHack build procedure on amiga. - Those did not compile out-of-the-box on the linux host. They - will either have to be: - - ported to build and run on the linux or macOS cross-compile - host - or + Tile mode (AMIV): + OPTIONS=windowtype:amiv - - their functionality will have to be rolled into amiga NetHack - itself and executed on the target Amiga the first time the game - is run, perhaps. + Tile mode auto-selects tiles32.iff (32 colors, 5 bitplanes) when the + screen supports 32+ colors, otherwise tiles16.iff (16 colors, 4 planes). + + For further details about playing the game, and setting up your NetHack + configuration, please see the following file: + sys/amiga/README.amiga - If you make headway, or are successful getting a working copy of - NetHack going on the amiga, drop us a note at devteam@nethack.org. +--------------------------------+ | B6. Case sample: Web Assembly | @@ -646,7 +645,7 @@ Cross-compiler url: https://emscripten.org/docs/getting_started/downloads.html For macOS, you will need to install Xcode, git, cmake, Python 3.5 or new (at time of this writing). - After installing the prerequite packages above, obtain the cross-compiler + After installing the prerequisite packages above, obtain the cross-compiler via git and build it from the directory of your choice using steps similar to these: diff --git a/DEVEL/Developer.txt b/DEVEL/Developer.txt index 37fd69c59..2622ffbd6 100644 --- a/DEVEL/Developer.txt +++ b/DEVEL/Developer.txt @@ -74,7 +74,7 @@ A. If you have never set up git on this machine before: git config --global credential.helper osxkeychain Linux: (This will vary by distribution.) - cd /usr/share/doc/git/contrib/credentail/libsecret + cd /usr/share/doc/git/contrib/credential/libsecret sudo apt-get install libglib-2.0-dev libsecret-1-dev sudo make git config --global credential.helper `pwd`/git-credential-libsecret diff --git a/DEVEL/hooksdir/nhsub b/DEVEL/hooksdir/nhsub index 7aa183b65..b5b90704b 100644 --- a/DEVEL/hooksdir/nhsub +++ b/DEVEL/hooksdir/nhsub @@ -397,7 +397,7 @@ sub cmdparse { } if($opt{cmd} eq 'commit' && $opt{a} && $#in == -1){ # "git commit -a" does multiple things; we only care - # about modigied files. + # about modified files. #XXX this assumes $RS is set properly for Windows - need to check that my @x = split(/$::RS/,`git ls-files -m`); chomp(@x); @@ -661,7 +661,7 @@ is actually a directory, the program recurses into that directory tree. Not all files found are re-written; only those with the attribute NHSUBST (for inline substitutions) or NH_DATESUB (for template substitution) are considered; finally those with no substitution -variables or no changes due to subtitution variables are not +variables or no changes due to substitution variables are not re-written. Unless changed by the options, files that have not changed are not affected. @@ -711,7 +711,7 @@ or the changes will be overwritten with the current date.) =back -=head1 SUBSTITUTION VALRIABLES +=head1 SUBSTITUTION VARIABLES =over diff --git a/DEVEL/nhgitset.pl b/DEVEL/nhgitset.pl index b20b5c57c..2c4977895 100755 --- a/DEVEL/nhgitset.pl +++ b/DEVEL/nhgitset.pl @@ -43,7 +43,7 @@ BEGIN { # Set $is_sourcerepo while we're at it - same logic. { # Special case for running nhgitset against a different repo. - # Must preceed the normal case! + # Must precede the normal case! # NB: we use $DEVhooksdir later in the program $DEVhooksdir = ($0 =~ m!^(.*)$DS!)[0]; chomp($DEVhooksdir); diff --git a/Files b/Files index e4f4e096b..a91ef523d 100644 --- a/Files +++ b/Files @@ -47,21 +47,21 @@ Val-strt.lua Wiz-fila.lua Wiz-filb.lua Wiz-goal.lua Wiz-loca.lua Wiz-strt.lua air.lua asmodeus.lua astral.lua baalz.lua bigrm-1.lua bigrm-2.lua bigrm-3.lua bigrm-4.lua bigrm-5.lua bigrm-6.lua bigrm-7.lua bigrm-8.lua bigrm-9.lua bigrm-10.lua -bigrm-11.lua bigrm-12.lua bogusmon.txt castle.lua cmdhelp -data.base dungeon.lua earth.lua engrave.txt epitaph.txt -fakewiz1.lua fakewiz2.lua fire.lua hellfill.lua help -hh history juiblex.lua keyhelp knox.lua -license luahelper medusa-1.lua medusa-2.lua medusa-3.lua -medusa-4.lua minefill.lua minend-1.lua minend-2.lua minend-3.lua -minetn-1.lua minetn-2.lua minetn-3.lua minetn-4.lua minetn-5.lua -minetn-6.lua minetn-7.lua nhcore.lua nhlib.lua opthelp -optmenu oracle.lua oracles.txt orcus.lua quest.lua -rumors.fal rumors.tru sanctum.lua soko1-1.lua soko1-2.lua -soko2-1.lua soko2-2.lua soko3-1.lua soko3-2.lua soko4-1.lua -soko4-2.lua symbols themerms.lua tower1.lua tower2.lua -tower3.lua tribute tut-1.lua tut-2.lua usagehlp -valley.lua water.lua wizard1.lua wizard2.lua wizard3.lua -wizhelp +bigrm-11.lua bigrm-12.lua bigrm-13.lua bogusmon.txt castle.lua +cmdhelp data.base dungeon.lua earth.lua engrave.txt +epitaph.txt fakewiz1.lua fakewiz2.lua fire.lua hellfill.lua +help hh history juiblex.lua keyhelp +knox.lua license luahelper medusa-1.lua medusa-2.lua +medusa-3.lua medusa-4.lua minefill.lua minend-1.lua minend-2.lua +minend-3.lua minetn-1.lua minetn-2.lua minetn-3.lua minetn-4.lua +minetn-5.lua minetn-6.lua minetn-7.lua nhcore.lua nhlib.lua +opthelp optmenu oracle.lua oracles.txt orcus.lua +quest.lua rumors.fal rumors.tru sanctum.lua soko1-1.lua +soko1-2.lua soko2-1.lua soko2-2.lua soko3-1.lua soko3-2.lua +soko4-1.lua soko4-2.lua symbols themerms.lua tower1.lua +tower2.lua tower3.lua tribute tut-1.lua tut-2.lua +usagehlp valley.lua water.lua wizard1.lua wizard2.lua +wizard3.lua wizhelp doc: (files for all versions) @@ -87,23 +87,23 @@ include: tile2x11.h winX.h xwindow.h xwindowp.h (files for all versions) -align.h artifact.h artilist.h attrib.h botl.h -color.h config.h config1.h context.h coord.h -cstd.h decl.h defsym.h dgn_file.h display.h -dlb.h dungeon.h engrave.h extern.h flag.h -fnamesiz.h func_tab.h global.h hack.h hacklib.h -integer.h isaac64.h lint.h mail.h mextra.h -mfndpos.h micro.h mkroom.h monattk.h mondata.h -monflag.h monst.h monsters.h nhmd4.h nhregex.h -obj.h objclass.h objects.h optlist.h patchlevel.h -pcconf.h permonst.h prop.h quest.h rect.h -region.h rm.h savefile.h seffects.h selvar.h -sfmacros.h sfprocs.h skills.h sndprocs.h sp_lev.h -spell.h stairs.h sym.h sys.h tcap.h -tileset.h timeout.h tradstdc.h trap.h unixconf.h -vision.h vmsconf.h warnings.h weight.h winami.h -wincurs.h windconf.h winprocs.h wintype.h you.h -youprop.h +align.h amiconf.h artifact.h artilist.h attrib.h +botl.h color.h config.h config1.h context.h +coord.h cstd.h decl.h defsym.h dgn_file.h +display.h dlb.h dungeon.h engrave.h extern.h +flag.h fnamesiz.h func_tab.h global.h hack.h +hacklib.h integer.h isaac64.h lint.h mail.h +mcastu.h mextra.h mfndpos.h micro.h mkroom.h +monattk.h mondata.h monflag.h monst.h monsters.h +nhmd4.h nhregex.h obj.h objclass.h objects.h +optlist.h patchlevel.h pcconf.h permonst.h prop.h +quest.h rect.h region.h rm.h savefile.h +seffects.h selvar.h sfmacros.h sfprocs.h skills.h +sndprocs.h sp_lev.h spell.h stairs.h sym.h +sys.h tcap.h tileset.h timeout.h tradstdc.h +trap.h unixconf.h vision.h vmsconf.h warnings.h +weight.h winami.h wincurs.h windconf.h winprocs.h +wintype.h you.h youprop.h (file for tty versions) wintty.h @@ -124,8 +124,8 @@ bitmfile.h gem_rsc.h load_img.h wingem.h winGnome.h (files for all versions) -amiconf.h beconf.h def_os2.h os2conf.h system.h tosconf.h -trampoli.h wceconf.h +beconf.h def_os2.h os2conf.h system.h tosconf.h trampoli.h +wceconf.h (files for various Macintosh versions) mac-carbon.h mac-qt.h mac-term.h macconf.h macpopup.h @@ -134,12 +134,7 @@ mactty.h macwin.h mttypriv.h outdated/sys/amiga: (files for Amiga versions - untested for 3.7) Build.ami Install.ami Makefile.agc Makefile.ami NetHack.cnf -amidos.c amidos.p amifont.uu amifont8.uu amigst.c -amii.hlp amimenu.c amirip.c amistack.c amitty.c -amiwind.c amiwind.p clipwin.c colorwin.c grave16.xpm -ifchange mkdmake txt2iff.c winamenu.c winami.c -winami.p winchar.c windefs.h winext.h winfuncs.c -winkey.c winproto.h winreq.c winstr.c xpm2iff.c +ifchange mkdmake txt2iff.c xpm2iff.c outdated/sys/atari: (files for Atari version - untested for 3.7) @@ -296,29 +291,40 @@ allmain.c alloc.c apply.c artifact.c attrib.c ball.c bones.c botl.c calendar.c cfgfiles.c cmd.c coloratt.c date.c dbridge.c decl.c detect.c dig.c display.c dlb.c do.c do_name.c do_wear.c dog.c dogmove.c -dokick.c dothrow.c drawing.c dungeon.c eat.c end.c -engrave.c exper.c explode.c extralev.c files.c fountain.c -getpos.c glyphs.c hack.c hacklib.c insight.c invent.c -isaac64.c light.c lock.c mail.c makemon.c mcastu.c -mdlib.c mhitm.c mhitu.c minion.c mklev.c mkmap.c -mkmaze.c mkobj.c mkroom.c mon.c mondata.c monmove.c -monst.c mplayer.c mthrowu.c muse.c music.c nhlobj.c -nhlsel.c nhlua.c nhmd4.c o_init.c objects.c objnam.c -options.c pager.c pickup.c pline.c polyself.c potion.c -pray.c priest.c quest.c questpgr.c read.c rect.c -region.c report.c restore.c rip.c rnd.c role.c -rumors.c save.c selvar.c sfbase.c sfstruct.c shk.c -shknam.c sit.c sounds.c sp_lev.c spell.c stairs.c -steal.c steed.c strutil.c symbols.c sys.c teleport.c -timeout.c topten.c track.c trap.c u_init.c uhitm.c -utf8map.c vault.c version.c vision.c weapon.c were.c -wield.c windows.c wizard.c wizcmds.c worm.c worn.c -write.c zap.c +dokick.c dothrow.c drawing.c dungeon.c earlyarg.c eat.c +end.c engrave.c exper.c explode.c extralev.c files.c +fountain.c getpos.c glyphs.c hack.c hacklib.c iactions.c +insight.c invent.c isaac64.c light.c lock.c mail.c +makemon.c mcastu.c mdlib.c mhitm.c mhitu.c minion.c +mklev.c mkmap.c mkmaze.c mkobj.c mkroom.c mon.c +mondata.c monmove.c monst.c mplayer.c mthrowu.c muse.c +music.c nhlobj.c nhlsel.c nhlua.c nhmd4.c o_init.c +objects.c objnam.c options.c pager.c pickup.c pline.c +polyself.c potion.c pray.c priest.c quest.c questpgr.c +read.c rect.c region.c report.c restore.c rip.c +rnd.c role.c rumors.c save.c selvar.c sfbase.c +sfstruct.c shk.c shknam.c sit.c sounds.c sp_lev.c +spell.c stairs.c steal.c steed.c strutil.c symbols.c +sys.c teleport.c timeout.c topten.c track.c trap.c +u_init.c uhitm.c utf8map.c vault.c version.c vision.c +weapon.c were.c wield.c windows.c wizard.c wizcmds.c +worm.c worn.c write.c zap.c submodules: (files in top directory) CHKSUMS lua pdcurses pdcursesmod +sys/amiga: +(files for Amiga versions) +README.amiga amidos.c amidos.p amifont.uu +amifont8.uu amigst.c amii.hlp amimenu.c +amirip.c amistack.c amitty.c amiwind.c +amiwind.p bmp2iff_host.c clipwin.c colorwin.c +grave16.xpm nethack.cnf winamenu.c winami.c +winami.p winchar.c windefs.h winext.h +winfuncs.c winkey.c winproto.h winreq.c +winstr.c xpm2iff_host.c + sys/libnh: (files in top directory) README.md libnhmain.c sysconf @@ -418,6 +424,7 @@ sys/unix/hints/include: compiler.370 cross-post.370 cross-pre1.370 cross-pre2.370 gbdates-post.370 gbdates-pre.370 misc.370 multisnd-post.370 multisnd1-pre.370 multisnd2-pre.370 multiw-1.370 multiw-2.370 +response.370 sys/vms: (files for VMS version) @@ -430,7 +437,7 @@ vmsmain.c vmsmisc.c vmssetup.com vmstty.c vmsunix.c sys/windows: -(files for Windows 7/8.x/10/11 version) +(files for Windows 10/11 version) GNUmakefile GNUmakefile.depend Install.windows Makefile.nmake build-msys2.txt build-nmake.txt build-vs.txt console.rc consoletty.c @@ -441,7 +448,7 @@ win10.c win10.h win32api.h windmain.c windsys.c winos.h sys/windows/vs: -(files for Visual Studio 2019 or 2022 Community Edition builds) +(files for Visual Studio Community builds) NetHack.sln NetHackPackage.appxmanifest NetHackPackage.wapproj NetHackProperties.props Package.StoreAssociation.xml ScreenShot.PNG @@ -453,11 +460,11 @@ dirs.props dll.props files.props sfctool.sln sys/windows/vs/FetchPrereq: -(files for Visual Studio 2019 or 2022 Community Edition builds) +(files for Visual Studio Community builds) fetchprereq.nmake fetchprereq.vcxproj sys/windows/vs/Images: -(files for Visual Studio 2019 or 2022 Community Edition builds) +(files for Visual Studio Community builds) BadgeLogo.scale-100.png BadgeLogo.scale-125.png BadgeLogo.scale-150.png @@ -512,71 +519,75 @@ Wide310x150Logo.scale-200.png Wide310x150Logo.scale-400.png sys/windows/vs/NetHack: -(files for Visual Studio 2019 or 2022 Community Edition builds) +(files for Visual Studio Community builds) NetHack.vcxproj afternethack.proj sys/windows/vs/NetHackW: -(file for Visual Studio 2019 or 2022 Community Edition builds) +(file for Visual Studio Community builds) NetHackW.vcxproj sys/windows/vs/PDCurses: -(file for Visual Studio 2019 or 2022 Community Edition builds) +(file for Visual Studio Community builds) PDCurses.vcxproj sys/windows/vs/PDCursesGui: -(file for Visual Studio 2019 or 2022 Community Edition builds) +(file for Visual Studio Community builds) pdcursesgui.vcxproj sys/windows/vs/dlb: -(files for Visual Studio 2019 or 2022 Community Edition builds) +(files for Visual Studio Community builds) afterdlb.proj dlb.vcxproj sys/windows/vs/fetchctags: -(files for Visual Studio 2019 or 2022 Community Edition builds) +(files for Visual Studio Community builds) fetchctags.nmake fetchctags.vcxproj sys/windows/vs/hacklib: -(file for Visual Studio 2019 or 2022 Community Edition builds) +(file for Visual Studio Community builds) hacklib.vcxproj sys/windows/vs/lualib: -(file for Visual Studio 2019 or 2022 Community Edition builds) +(file for Visual Studio Community builds) lualib.vcxproj sys/windows/vs/makedefs: -(files for Visual Studio 2019 or 2022 Community Edition builds) +(files for Visual Studio Community builds) aftermakedefs.proj makedefs.vcxproj +sys/windows/vs/nhlua_h: +(file for Visual Studio Community builds) +nhlua_h.vcxproj + sys/windows/vs/package: -(files for Visual Studio 2019 or 2022 Community Edition builds) +(files for Visual Studio Community builds) package.nmake package.vcxproj sys/windows/vs/recover: -(files for Visual Studio 2019 or 2022 Community Edition builds) +(files for Visual Studio Community builds) afterrecover.proj recover.vcxproj sys/windows/vs/sfctool: -(file for Visual Studio 2019 or 2022 Community Edition builds) +(file for Visual Studio Community builds) sfctool.vcxproj sys/windows/vs/sftags: -(files for Visual Studio 2019 or 2022 Community Edition builds) +(files for Visual Studio Community builds) aftersftags.proj sftags.vcxproj sys/windows/vs/tile2bmp: -(files for Visual Studio 2019 or 2022 Community Edition builds) +(files for Visual Studio Community builds) aftertile2bmp.proj tile2bmp.vcxproj sys/windows/vs/tilemap: -(files for Visual Studio 2019 or 2022 Community Edition builds) +(files for Visual Studio Community builds) aftertilemap.proj tilemap.vcxproj sys/windows/vs/tiles: -(file for Visual Studio 2019 or 2022 Community Edition builds) +(file for Visual Studio Community builds) tiles.vcxproj sys/windows/vs/uudecode: -(files for Visual Studio 2019 or 2022 Community Edition builds) +(files for Visual Studio Community builds) afteruudecode.proj uudecode.vcxproj test: @@ -631,8 +642,8 @@ win/share: (files for versions using optional tiles) bmptiles.c gifread.c giftiles.c monsters.txt nhicns.uu nhsplash.xpm objects.txt other.txt ppmwrite.c renumtiles.pl -safeproc.c thintile.c tile.doc tile.h tile2bmp.c -tilemap.c tileset.c tiletext.c tiletxt.c +thintile.c tile.doc tile.h tile2bmp.c tilemap.c +tileset.c tiletext.c tiletxt.c win/shim: (file in top directory) diff --git a/Porting b/Porting index 1abba0d93..45aa1bfc5 100644 --- a/Porting +++ b/Porting @@ -201,7 +201,7 @@ need to be included in the packaging of the game. 4.3. Lua Compile and link into a library, or obtain a prebuilt Lua library for -your platform. Place the Lua source into lib/lua-5.4.6 (or other folder +your platform. Place the Lua source into lib/lua-5.4.8 (or other folder representing an appropriate Lua version); place the compiled Lua library into lib. diff --git a/README b/README index 9a4923d03..e6bf764f8 100644 --- a/README +++ b/README @@ -110,8 +110,8 @@ Please read items (1), (2) and (3) BEFORE doing anything with your new code. Intel Pentium or better running Linux, BSDI Intel Pentium or better running Windows 10 or 11 - Intel-based, or Apple M1, M2, M3 Macs running - macOS 10.11 (El Capitan) to macOS 14 (Sonoma) + Intel-based, or Apple M1, M2, M3, M4, M5 Macs running + macOS 10.11 (El Capitan) to macOS 26 (Tahoe) (follow the instructions in sys/unix/NewInstall.unx) Intel 80386 or greater running MS-DOS with DPMI built via djgpp compiler (native or Linux-hosted cross-compiler) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 9da64ea50..7c7eea340 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -24,16 +24,21 @@ strategy: imageName: 'macOS-15' toolchainName: clang buildTargetName: all - windows-visualstudio: + windows-x64-vstudio: imageName: 'windows-latest' toolchainName: vs buildTargetName: all + windows-arm64-vstudio: + imageName: 'windows-latest' + toolchainName: vs + buildTargetName: all + buildPlatform: 'ARM64' # Targeting Snapdragon windows-mingw: imageName: 'windows-2025' toolchainName: mingw buildTargetName: all - linux_noble_cross_msdos: - imageName: 'ubuntu-24.04' + linux_latest_cross_msdos: + imageName: 'ubuntu-latest' toolchainName: cross buildTargetName: msdos linux_noble_docs: @@ -129,7 +134,7 @@ steps: export LUASRC=../submodules/lua export ADD_CURSES=Y export PDCURSES_TOP=../submodules/pdcursesmod - export LUA_VERSION=5.4.6 + export LUA_VERSION=5.4.8 # # 64-bit #export CURLSRC=https://github.com/brechtsanders/winlibs_mingw/releases/download/14.2.0posix-19.1.1-12.0.0-ucrt-r2/winlibs-x86_64-posix-seh-gcc-14.2.0-llvm-19.1.1-mingw-w64ucrt-12.0.0-r2.zip @@ -227,6 +232,7 @@ steps: - bash: | sudo apt -qq -y install libfl2 export GCCVER=gcc1220 + export IN_CI=SKIP_FONTS_IN_CI=1 cd sys/unix sh setup.sh hints/linux.370 cd ../.. @@ -234,13 +240,13 @@ steps: sys/msdos/fetch-cross-compiler.sh retVal=$? if [ $retVal -eq 0 ]; then - make LUA_VERSION=5.4.6 WANT_WIN_TTY=1 WANT_WIN_CURSES=1 CROSS_TO_MSDOS=1 package + make LUA_VERSION=5.4.8 WANT_WIN_TTY=1 WANT_WIN_CURSES=1 CROSS_TO_MSDOS=1 $IN_CI package fi condition: and(eq( variables['Agent.OS'], 'Linux' ), eq( variables.toolchain, 'cross')) workingDirectory: $(Agent.BuildDirectory)/$(netHackPath) displayName: 'Building MSDOS build' - bash: | - sudo apt-get install texlive + sudo apt install texlive texlive-latex-extra make Guidebook make Guidebook.txt make Guidebook.pdf diff --git a/dat/Rog-strt.lua b/dat/Rog-strt.lua index 55bf11969..1e4700d49 100644 --- a/dat/Rog-strt.lua +++ b/dat/Rog-strt.lua @@ -157,10 +157,10 @@ des.monster({ id = "leprechaun", x=74, y=04, peaceful=0 }) des.monster({ id = "leprechaun", x=25, y=19, peaceful=0 }) des.monster({ id = "water nymph", x=25, y=18, peaceful=0 }) -- Wandering the streets. -for i=1,4 + math.random(1 - 1,1*3) do +for i = 1, math.random(4,7) do des.monster({ id = "water nymph", coord = streets:rndcoord(1), peaceful=0 }) des.monster({ id = "leprechaun", coord = streets:rndcoord(1), peaceful=0 }) end -for i=1,7 + math.random(1 - 1,1*3) do +for i = 1, math.random(7,10) do des.monster({ id = "chameleon", coord = streets:rndcoord(1), peaceful=0 }) end diff --git a/dat/astral.lua b/dat/astral.lua index f0ba8951e..6d6f0808b 100644 --- a/dat/astral.lua +++ b/dat/astral.lua @@ -35,7 +35,7 @@ des.map([[ -- chance to alter above map and turn the wings of the bottom-center into -- a pair of big (5x15) rooms -for i=1,2 do +for i = 1, 2 do -- 3.6.[01]: 75% chance that both sides opened up, 25% that neither did; -- 3.6.2: 60% twice == 36% chance that both sides open up, 24% left side -- only, 24% right side only, 16% that neither side opens up @@ -58,7 +58,7 @@ for i=1,2 do des.terrain(41,18, ".") end -- extra monsters; was [6 + 3d4] when both wings were opened up at once - for i=1,3 + math.random(2 - 1,2*3) do + for j = 1, math.random(4,9) do des.monster({ id="Angel", coord = hall:rndcoord(1), align="noalign", peaceful=0 }) if percent(50) then des.monster({ coord = hall:rndcoord(1), peaceful=0 }) diff --git a/dat/bigrm-1.lua b/dat/bigrm-1.lua index a4003aa4b..2c734a5ac 100644 --- a/dat/bigrm-1.lua +++ b/dat/bigrm-1.lua @@ -28,10 +28,10 @@ des.map([[ ]]); -if percent(75) then +if percent(80) then local terrains = { "-", "F", "L", "T", "C" }; local tidx = math.random(1, #terrains); - local choice = math.random(0, 4); + local choice = math.random(0, 5); if choice == 0 then -- one horizontal line des.terrain(selection.line(10,8, 65,8), terrains[tidx]); @@ -48,6 +48,14 @@ if percent(75) then des.terrain(selection.rect(4,4, 70,13), terrains[tidx]); local sel = selection.line(25,4, 50,4) | selection.line(25,13, 50,13); des.terrain(sel, '.'); + elseif choice == 4 then + -- snake + des.terrain(selection.fillrect(5,5, 69, 12), terrains[tidx]); + for i = 0, 7 do + local x = 6 + i*8; + local y = 5 + (i%2); + des.terrain(selection.fillrect(x, y, x+6, y+6), '.'); + end else -- nothing end diff --git a/dat/bigrm-13.lua b/dat/bigrm-13.lua new file mode 100644 index 000000000..ff834a666 --- /dev/null +++ b/dat/bigrm-13.lua @@ -0,0 +1,82 @@ +-- NetHack bigroom bigrm-13.lua $NHDT-Date: 1652196024 2022/05/10 15:20:24 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.0 $ +-- Copyright (c) 2026 by Pasi Kallinen +-- NetHack may be freely redistributed. See license for details. +-- +-- Pillars + +des.level_init({ style = "solidfill", fg = " " }); +des.level_flags("mazelevel", "noflip"); + +des.map([[ +--------------------------------------------------------------------------- +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +--------------------------------------------------------------------------- +]]); + +local pillar = [[ +--- +| | +---]]; + +filters = { + -- 1: all pillars + function(x, y) return true; end, + -- 2: 3 vertical lines + function(x, y) return (x%2 == 1); end, + -- 3: checkerboard + function(x, y) return (((x+y)%2) == 0); end, + -- 4: center row + function(x, y) return (y%2 == 1); end, + -- 5: top and bottom rows + function(x, y) return (y%2 == 0); end, + -- 6: random 50% + function(x, y) return (math.random(0,1) == 0); end, + -- 7: corners and center + function(x, y) return ((x/3)%2 == y%2); end, + -- 8: slanted + function(x, y) return ((x+1)//3 == y); end, +}; + +idx = math.random(1, #filters); + +for y = 0,2 do + for x = 0,6 do + if (filters[idx](x, y)) then + des.map({ coord = {12 + x*9, 4 + y*5}, map = pillar, contents=function() end }); + end + end +end + +des.region(selection.area(00,00,75,18), "lit"); +des.wallify(); +des.non_diggable(); + +des.stair("up"); +des.stair("down"); + +for i = 1,15 do + des.object(); +end +for i = 1,6 do + des.trap(); +end +for i = 1,28 do + des.monster(); +end + diff --git a/dat/data.base b/dat/data.base index ed42fca25..d43307f33 100644 --- a/dat/data.base +++ b/dat/data.base @@ -3025,7 +3025,7 @@ knight And was a bugbear in men's eyes; But had the fortune in his age To live a fool and die a sage. - [ Don Quixote of La Mancha, by Miquel de Cervantes Saavedra ] + [ Don Quixote of La Mancha, by Miguel de Cervantes Saavedra ] ~kobold ??m* *kobold* The race of kobolds are reputed to be an artificial creation @@ -3144,7 +3144,7 @@ lance his lance into shivers, carrying him and his horse after it, and finally tumbled him a good way off from it on the field in evil plight. - [ Don Quixote of La Mancha, by Miquel de Cervantes Saavedra ] + [ Don Quixote of La Mancha, by Miguel de Cervantes Saavedra ] land mine Your heart is intact, your brain is not badly damaged, but the rest of your injuries are comparable to stepping on a land mine. You'd diff --git a/dat/dungeon.lua b/dat/dungeon.lua index eb6223d66..d90eb9214 100644 --- a/dat/dungeon.lua +++ b/dat/dungeon.lua @@ -70,7 +70,7 @@ dungeon = { base = 10, range = 3, chance = 40, - nlevels = 12 + nlevels = 13 }, { name = "medusa", diff --git a/dat/hellfill.lua b/dat/hellfill.lua index 00175ffd3..3a9b4ed9f 100644 --- a/dat/hellfill.lua +++ b/dat/hellfill.lua @@ -413,14 +413,16 @@ hells = { -- 7: open cavern, "mines" with more space function () + -- walls are either stone or lava + local wter = percent(50) and " " or "L"; des.level_init({ style = "solidfill", fg = " ", lit = 0 }); des.level_flags("mazelevel", "noflip"); - des.level_init({ style="mines", fg=".", smoothed=true ,joined=true, lit=0 }); + des.level_init({ style="mines", fg=".", bg = wter, smoothed=true ,joined=true, lit=0 }); local sel = selection.match("."):grow(); - des.terrain({ selection = sel, typ = "." }); + des.terrain({ selection = sel, typ = ".", lit = 0 }); local border = selection.rect(0,0, 78, 20); - des.terrain({ selection = border, typ = " " }); + des.terrain({ selection = border, typ = wter, lit = 0 }); des.wallify(); end, diff --git a/dat/hh b/dat/hh index 951e91460..847ca02b7 100644 --- a/dat/hh +++ b/dat/hh @@ -34,8 +34,8 @@ O options set options / what-is tell what a map symbol represents \ known display list of what's been discovered | perminv interact with persistent inventory window instead of hero+map -v version display version number -V history display game history +v chronicle display a list of important events +V version display version number ^A again redo the previous command ^R redraw redraw the screen ^P prevmsg repeat previous message (consecutive ^P's repeat earlier ones) diff --git a/dat/minetn-1.lua b/dat/minetn-1.lua index 590817bf4..ef10d70b7 100644 --- a/dat/minetn-1.lua +++ b/dat/minetn-1.lua @@ -88,7 +88,7 @@ des.object({ id = "corpse", montype="watchman" }) des.object({ id = "corpse", montype="watch captain" }) -- Rubble! -for i=1,9 + math.random(2 - 1,2*5) do +for i = 1, math.random(10,19) do if percent(90) then des.object("boulder") end @@ -118,7 +118,7 @@ des.object({ id = "wand of magic missile", coord = place[5], buc="uncursed", spe local inside = selection.floodfill(18,8) local near_temple = selection.area(17,8, 23,14) & inside -for i=1,5 + math.random(1 - 1,1*10) do +for i = 1, math.random(5,15) do if percent(50) then des.monster({ id = "orc-captain", coord = inside:rndcoord(1), peaceful=0 }); else @@ -130,12 +130,14 @@ for i=1,5 + math.random(1 - 1,1*10) do end end -- shamans can be hanging out in/near the temple -for i=1,math.random(2 - 1,2*3) do - des.monster({ id = "orc shaman", coord = near_temple:rndcoord(0), peaceful=0 }); +-- one of the shamans is higher level +for i = 1, math.random(1,6) do + des.monster({ id = "orc shaman", coord = near_temple:rndcoord(0), peaceful=0, + m_lev_adj = (i == 1) and 3 or 0 }); end -- these are not such a big deal -- to run into outside the bars -for i=1,9 + math.random(2 - 1,2*5) do +for i = 1, math.random(10,19) do if percent(90) then des.monster({ id = "hill orc", peaceful = 0 }) else diff --git a/dat/minetn-6.lua b/dat/minetn-6.lua index a31d9fcd8..2827d1947 100644 --- a/dat/minetn-6.lua +++ b/dat/minetn-6.lua @@ -86,6 +86,14 @@ des.monster("gnome lord") des.monster("dwarf") des.monster("dwarf") des.monster("dwarf") +des.monster({ id = "dwarf", peaceful = 1 }) +des.monster({ id = "dwarf", peaceful = 1 }) +des.monster({ id = "gnome", peaceful = 1 }) +des.monster({ id = "gnome", peaceful = 1 }) +des.monster({ id = "hobbit", peaceful = 1 }) +des.monster({ id = "goblin", peaceful = 1 }) +des.monster({ id = "kobold", peaceful = 1 }) +des.monster({ id = "dog", peaceful = 1 }) des.monster({ id = "watchman", peaceful = 1 }) des.monster({ id = "watchman", peaceful = 1 }) des.monster({ id = "watchman", peaceful = 1 }) diff --git a/dat/opthelp b/dat/opthelp index d36e5228b..68eb53215 100644 --- a/dat/opthelp +++ b/dat/opthelp @@ -48,14 +48,17 @@ nudist start your character without armor [False] null allow nulls to be sent to your terminal [True] try turning this option off (forcing NetHack to use its own delay code) if moving objects seem to teleport across rooms +pauper start your character with no possessions [False] perm_invent keep inventory in a permanent window [False] pickup_stolen override pickup_types for stolen objects [True] pickup_thrown override pickup_types for thrown objects [True] +price_quotes show remembered price quotes for unIDed items [False] pushweapon when wielding a new weapon, put your previously [False] wielded weapon into the secondary weapon slot quick_farsight usually skip the chance to browse the map when [False] randomly triggered clairvoyance takes place rawio allow the use of raw I/O [False] +reroll allow rerolling of starting inventory [False] rest_on_space count the space bar as a rest character [False] safe_pet prevent you from (knowingly) attacking your pet(s) [True] safe_wait require use of 'm' prefix before '.' or 's' to [True] diff --git a/dat/symbols b/dat/symbols index af6c04ade..f2bc486fa 100644 --- a/dat/symbols +++ b/dat/symbols @@ -895,4 +895,82 @@ start: Enhanced1 G_trwall_mines: U+251C/113-126-142 finish +start: AmigaFont + Description: Amiga hack.font line-drawing and effect characters + # Dungeon features + S_stone: \x20 + S_vwall: \xc0 + S_hwall: \xc1 + S_tlcorn: \xc2 + S_trcorn: \xc3 + S_blcorn: \xc4 + S_brcorn: \xc5 + S_crwall: \xc6 + S_tuwall: \xd8 + S_tdwall: \xd6 + S_tlwall: \xd7 + S_trwall: \xd5 + S_ndoor: \xd9 + S_vodoor: \x91 + S_hodoor: \x92 + S_vcdoor: \x93 + S_hcdoor: \x94 + S_bars: '#' + S_tree: '#' + S_room: '.' + S_darkroom: \x20 + S_corr: \xe5 + S_litcorr: \xe5 + S_upstair: '<' + S_dnstair: '>' + S_upladder: '<' + S_dnladder: '>' + S_altar: '_' + S_grave: \x5c + S_throne: '#' + S_sink: '{' + S_fountain: '}' + S_pool: '*' + S_ice: '}' + S_lava: '*' + S_vodbridge: '*' + S_hodbridge: '#' + S_vcdbridge: '#' + S_hcdbridge: '.' + S_air: '#' + S_cloud: '}' + # Traps: all default to '^' except web + S_web: '"' + # Effects + S_vbeam: \xf1 + S_hbeam: \xf0 + S_lslant: \xf2 + S_rslant: \xf3 + S_digbeam: '*' + S_flashbeam: '!' + S_boomleft: '{' + S_boomright: '}' + S_ss1: '@' + S_ss2: '&' + S_ss3: '*' + S_ss4: '#' + S_sw_tl: \xf4 + S_sw_tc: \xf5 + S_sw_tr: \xf6 + S_sw_ml: \xf7 + S_sw_mr: \xef + S_sw_bl: \xf8 + S_sw_bc: \xf9 + S_sw_br: \xfa + S_explode1: \xe6 + S_explode2: \xea + S_explode3: \xe7 + S_explode4: \xec + S_explode5: \xd4 + S_explode6: \xed + S_explode7: \xe8 + S_explode8: \xeb + S_explode9: \xe9 +finish + # symbols EOF diff --git a/dat/wizhelp b/dat/wizhelp index 88e1b3417..1945214b0 100644 --- a/dat/wizhelp +++ b/dat/wizhelp @@ -30,6 +30,7 @@ Debug-Mode Quick Reference: #wizloadlua == load and execute a lua script #wizmakemap == recreate the current dungeon level #wizmondiff == [Opt] check for discrepancies in monster difficulty ratings +#wizobjprobs == [Opt] list actual probabilities of item generation #wizrumorcheck == validate rumor indexing; also show first, second, and last random engravings, epitaphs, and hallucinatory monsters #wizseenv == show map locations' seen vectors diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index dae3b6713..aec4cbd6f 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -1,4 +1,4 @@ -.\" $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.598 $ $NHDT-Date: 1745139202 2025/04/20 00:53:22 $ +.\" $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.614 $ $NHDT-Date: 1770881338 2026/02/11 23:28:58 $ .\" .\" This is an excerpt from the 'roff' man page from the 'groff' package. .\"+-- @@ -31,8 +31,21 @@ .lt 70n .\} . -.so tmac.nh \" extra macros which aren't in tmac.n -.if !\n(nH .so doc/tmac.nh +.\" Load extra macros that "tmac.n" doesn't define. +.de So +.ie \n(.g&(\n(.x=1)&(\n(.y>22) .soquiet \\$1 +.el .so \\$1 +.. +. +.So tmac.nh +.if !\n(nH So doc/tmac.nh +. +.if !\n(nH \{\ +.tm fatal error: cannot locate "tmac.nh" macro package +.ab +.\} +. +.rm So . .\" building Guidebook.txt doesn't have CR font available; groff 1.23 issues .\" a warning each time any font can't be loaded; earlier versions silently @@ -47,7 +60,7 @@ .ds f0 \*(vr .ds f1 \" empty .\"DO NOT REMOVE NH_DATESUB .ds f2 Date(%B %-d, %Y) -.ds f2 May 1, 2025 +.ds f2 March 25, 2026 . .\" A note on some special characters: .\" \(lq = left double quote @@ -131,7 +144,8 @@ will vary with your background and training: .pg \fIArcheologists\fP understand dungeons pretty well; this enables them to move quickly and sneak up on the local nasties. They start equipped -with the tools for a proper scientific expedition. +with the tools for a proper scientific expedition, and are able to read +ancient languages. .pg \fIBarbarians\fP are warriors out of the hinterland, hardened to battle. They begin their quests with naught but uncommon strength, a trusty hauberk, @@ -1233,6 +1247,9 @@ Autocompletes. Default key is \(oqM-A\(cq, and also \(oq\(haN\(cq if .op number_pad is on. +.lp "" +Preceding #annotate with the \(oqm\(cq prefix is the same as +#overview with the prefix. .lp "#apply " Apply (use) a tool such as a pick-axe, a key, or a lamp. Default key is \(oqa\(cq. @@ -1268,6 +1285,7 @@ Talk to someone. Default key is \(oqM-c\(cq. .lp "#chronicle" Show a list of important game events. +Default key is \(oqv\(cq. .lp "#close " Close a door. Default key is \(oqc\(cq. @@ -1368,7 +1386,6 @@ option is On, clicking on the hero (or steed when mounted) will execute this command. .lp "#history " Show long version and game history. -Default key is \(oqV\(cq. .lp #inventory Show your inventory. Default key is \(oqi\(cq. @@ -1740,6 +1757,18 @@ floor container menu. .lp "" Autocompletes. Default key is \(oqM-T\(cq. +.lp "#toggle " +Toggle a boolean option on or off. +Requires a parameter in parenthesis, the name of the option to toggle. +The option must be settable in-game. +.lp "" +For example: +.sd +.ft CR +BIND=':toggle(price_quotes) +BIND=@:toggle(autopickup) +.ft +.ed .lp "#travel " Travel to a specific location on the map. Default key is \(oq_\(cq. \" underscore @@ -1809,7 +1838,7 @@ Default key is \(oqM-v\(cq. .lp #versionshort Show the program's version number, plus the date and time that the running copy was built from sources (not the version's release date). -Default key is \(oqv\(cq. +Default key is \(oqV\(cq. .lp "#vision " Show vision array. Autocompletes. @@ -1909,6 +1938,7 @@ Wish for something. Autocompletes. Debug mode only. Default key is \(oq\(haW\(cq. +Precede this command with the \(oq\f(CRm\fP\(cq prefix to show a wish history menu. .lp "#wmode " Show wall modes. Autocompletes. @@ -2581,6 +2611,56 @@ Sometimes the bless or curse state of objects is referred to as their or \(lq\f(CRBUCX\fP\(rq for Blessed, Uncursed, Cursed, or unknown. (The term \fIbeatitude\fP is occasionally used as well.) .hn 2 +Artifacts +.pg +Some objects have been imbued with special powers and are known as +\fBArtifacts\fP. +They have specific types (such as long sword or orcish dagger) and distinct +names such as \fIGiantslayer\fP or \fIGrimtooth\fP. +Artifact weapons typically do more damage than their ordinary counterparts. +Some do extra damage against all monsters, others only against specific +types of monsters so aren't better than regular weapons against other types. +Some confer defensive capabilities when wielded or have other powers that +aren't listed here. +.pg +You might find them simply lying on the floor, including but not limited +to inside shops, or be granted as a reward for \(lq#offer\(rq on an +altar to your patron deity. +A few might be dropped by monsters, or might be converted from an ordinary +object of the same type via assigning the right name. +.\" should we mention dipping for Excalibur here? +Or you might wish for them, if you happen to be granted a wish, but such +wishes can fail. +.pg +Some artifacts have a specific alignment, others don't. +You won't obtain aligned ones that have a different alignment from yours +via offering and might get a shock if you attempt to wish for any of those +or find one and attempt to use it. +.pg +Each role has a distinct artifact that is contained in the \fIQuest\fP +dungeon branch. +These are commonly known as quest artifacts. +All are aligned and most are non-weapons. +They won't be found randomly. +.pg +The \(oq\f(CR\\\fP\(cq and \(oq\f(CR\`a\fP\(cq commands will +list artifacts that you have fully identified (knowing the name and item +type isn't sufficient). +.hn 2 +Relics +.pg +There are three unique items that are named and have limited special +powers but aren't classified as artifacts. +Each is guarded by a particular monster and you'll need to collect all +three for use late in the game. +.\" The relics are listed in the same order as the Oracle's message about +.\" them rather than in the order they need to be used for the invocation. +They are \fIthe Bell of Opening\fP, +\fIthe Book of the Dead\fP, and +\fIthe Candelabrum of Invocation\fP. +Their corresponding descriptions when not yet identified are +silver bell, papyrus spellbook, and candelabrum. +.hn 2 Weapons (\(oq)\(cq) .pg Given a chance, most monsters in the Mazes of Menace will gratuitously try to @@ -2925,7 +3005,7 @@ Some objects of subtle enchantment are difficult to identify without these. .pg A scroll whose label is known can be read even when the hero is blind. If a scroll has been discovered, it will be listed in inventory by type -rather than by label, but the label is known in that situtaion even though +rather than by label, but the label is known in that situation even though it isn't shown. .pg Many scrolls produce a different effect from usual if they are blessed or @@ -3322,6 +3402,14 @@ not penalized for being spoken to by an angry god, priest(ess), or other religious figure; a true atheist would hear the words but attach no special meaning to them. .pg +A pauper starts the game with no possessions, no spells, and no weapon or +spell skills (and if playing as a knight, your pony will not have a saddle). +Can only be initiated by starting a new game with \f(CROPTIONS=pauper\fP +set in your run-time configurtion file or \f(CRNETHACKOPTIONS\fP environment +variable. +Once the game is underway, you can acquire and use items, spells, and skills +in the usual way. +.pg Most players fight with a wielded weapon (or tool intended to be wielded as a weapon). Another challenge is to win the game without using such a wielded weapon. You are still permitted to throw, @@ -3478,7 +3566,8 @@ the Castle level's drawbridge or can be given to you via prayer boon. and \fIPauper\fP are also conducts, and they can only be enabled by setting the correspondingly named option in NETHACKOPTIONS or run-time configuration file prior to game start. -In the case of \fIBlind\fP and \fIDeaf\fP, the option also enforces the conduct. +In the case of \fIBlind\fP and \fIDeaf\fP, the option also enforces the +conduct. They aren't really significant accomplishments unless/until you make substantial progress into the dungeon. . @@ -3536,7 +3625,7 @@ is a section marker (the closing \(oq\f(CR]\fP\(cq can be followed by whitespace and then an arbitrary comment beginning with \(oq\f(CR#\fP\(cq). The text between the square brackets is the section name. Section markers are only valid after a CHOOSE directive and their names -are case insensitive. +are case-insensitive. Lines after a section marker belong to that section up until another section starts or a marker without a name is encountered or the file ends. Lines within sections are ignored unless a CHOOSE directive has selected @@ -3664,13 +3753,14 @@ See the \(lqModifying NetHack Symbols\(rq section. Example: .sd \f(CR# replace small punctuation (tick marks) with digits\fP -\f(CRSYMBOLS=S_boulder:0,S_golem:7\fP +\f(CRSYMBOLS=S_golem:7\fP .ed .lp WIZKIT Debug mode only: extra items to add to initial inventory. Value is the name of a text file containing a list of item names, one per line, up to a maximum of 128 lines. Each line is processed by the function that handles wishing. +Entries are added to the wish history; see the wizwish-command. .lp "" Example: .sd @@ -3699,7 +3789,8 @@ OPTIONS=color # Display things in color if possible OPTIONS=lit_corridor # Show lit corridors differently OPTIONS=hilite_pet,hilite_pile # Replace small punctuation (tick marks) with digits -SYMBOLS=S_boulder:0,S_golem:7 +OPTIONS=boulder:0 +SYMBOLS=S_golem:7 # # No startup splash screen. Windows GUI only. OPTIONS=!splash_screen @@ -4409,6 +4500,10 @@ The positive (no \(oq!\(cq) and negative (with \(oq!\(cq) entries can be intermixed. .lp pauper Start the character with no possessions (default false). Persistent. +.lp "" +Also start with no spells or skills, which are tied to starting equipment. +Does not inhibit acquiring and using items, spells, and skills once play +has started. .lp perm_invent If true, always display your current inventory in a window (default false). .lp "" @@ -4527,6 +4622,15 @@ user name (on multi-user systems) or specifying a particular character name (on single-user systems) or it might be disabled entirely. Requesting it when not allowed or not possible results in explore mode instead. Default is normal play. +.lp price_quotes +Whenever the game mentions the name of an object you haven't identified yet, +it also mentions the range of buy and sell prices you have seen for that +item (to help narrow down what it could be). +The price shown is the unit price for one item (even when you are looking at +a stack of multiple items). +Many players may want to turn this on while identifying objects, and then +turn it back off again for general play. +Default is off. .lp pushweapon Using the \(oqw\(cq (wield) command when already wielding something pushes the old item into your alternate weapon slot (default off). @@ -4555,6 +4659,16 @@ If \f(CRrace\fP is not specified, there is no default value; player will be prompted unless role forces a choice for race. Cannot be set with the \(oq\f(CRO\fP\(cq command. Persistent. +.lp reroll +Allows rerolling your character's starting inventory and attributes (default +false). Persistent. +.lp "" +Note that rerolling your character is not a recommended way to play if aiming +merely to win (a lucky start has a much smaller influence on whether or not +you win the game than your actions later in the game). This option exists +partly as an acknowledgement that some players will insist on doing so anyway, +and partly because rerolling may be necessary for certain types of challenge +games. .lp rest_on_space Make the space bar a synonym for the \(oq.\(cq (#wait) command (default off). Persistent. @@ -4659,7 +4773,7 @@ or you are making screenshots or streaming video. Using the .op statuslines:3 option is recommended so that there will be more room available for -status information, unless you're using nethack's \fIQt\fP interface +status information, unless you're using NetHack's \fIQt\fP interface or your terminal emulator window displays fewer than 25 lines. Persistent. .lp "silent " @@ -5552,7 +5666,9 @@ change the color or appearance of fields in the status display. .pg The format for defining status colors is: .SD n -\f(CROPTION=hilite_status:\fIfield-name\fP/\fIbehavior\fP/\fIcolor\fP&\fIattributes\fP\fP +.\" was "\f(CR...\fI...\fP\fP" but font changes don't nest so final \fP didn't +.\" restore the pre-\f(CR font; assume that was Roman and explicitly use \fR +\f(CROPTION=hilite_status:\fIfield-name\fP/\fIbehavior\fP/\fIcolor\fP&\fIattributes\fP\fR .ED .pg For example, the following line in your configuration file will cause diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index a54d02b68..8051d8117 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -1,37 +1,14 @@ -\documentstyle[titlepage,longtable]{article} -% NetHack 3.7 Guidebook.tex $NHDT-Date: 1745139202 2025/04/20 00:53:22 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.579 $ */ -%+% we're still limping along in LaTeX 2.09 compatibility mode -%-%\documentclass{article} -%-%\usepackage{hyperref} % before longtable -%-%% if hyperref isn't available, we can get by with this instead -%-%%\RequirePackage[errorshow]{tracefnt} \DeclareSymbolFont{typewriter}{OT1}{cmtt}{m}{n} -%-%\usepackage{longtable} -\textheight 220mm -\textwidth 160mm -\oddsidemargin 0mm -\evensidemargin 0mm -\topmargin 0mm +% NetHack 3.7 Guidebook.tex $NHDT-Date: 1770881338 2026/02/11 23:28:58 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.596 $ */ +\documentclass[titlepage]{article} +\usepackage[hidelinks]{hyperref} +\usepackage{longtable} +\usepackage{enumitem} +\usepackage[a4paper, text={160mm, 220mm}, centering]{geometry} -\newcommand{\nd}{\noindent} - -\newcommand{\tb}[1]{\tt #1 \hfill} -\newcommand{\bb}[1]{\bf #1 \hfill} -\newcommand{\ib}[1]{\it #1 \hfill} - -\newcommand{\blist}[1] -{\begin{list}{$\bullet$} - {\leftmargin 30mm \topsep 2mm \partopsep 0mm \parsep 0mm \itemsep 1mm - \labelwidth 28mm \labelsep 2mm - #1}} - -\newcommand{\elist}{\end{list}} +\setlist[description]{leftmargin=30mm, topsep=2mm, partopsep=0mm, parsep=0mm, itemsep=1mm, labelwidth=28mm, labelsep=2mm} \hyphenation{CRASHREPORTURL} -% this will make \tt underscores look better, but requires that -% math subscripts will never be used in this document -\catcode`\_=12 - \begin{document} % % input file: guidebook.mn @@ -42,13 +19,13 @@ %.mt \title{\LARGE A Guide to the Mazes of Menace:\\ -\Large Guidebook for {\it NetHack\/}} +\Large Guidebook for \textit{NetHack}} %.au \author{Original version - Eric S. Raymond\\ (Edited and expanded for 3.7.0 by Mike Stephenson and others)} %DO NOT REMOVE NH_DATESUB \date{Date(%B %-d, %Y)} -\date{May 1, 2025} +\date{March 25, 2026} \maketitle @@ -94,7 +71,7 @@ at the local inn, becoming more and more depressed as you watch the odds of your success being posted on the inn's walls getting lower and lower. %.pg -\nd In the morning you awake, collect your belongings, and +\noindent In the morning you awake, collect your belongings, and set off for the dungeon. After several days of uneventful travel, you see the ancient ruins that mark the entrance to the Mazes of Menace. It is late at night, so you make camp at the entrance @@ -106,7 +83,7 @@ dungeon\ldots \section{What is going on here?} %.pg -You have just begun a game of {\it NetHack}. Your goal is to grab as much +You have just begun a game of \textit{NetHack}. Your goal is to grab as much treasure as you can, retrieve the Amulet of Yendor, and escape the Mazes of Menace alive. @@ -116,24 +93,24 @@ will vary with your background and training: %.pg % -\blist{} -\item[\bb{Archeologists}]% +\begin{description} +\item[Archeologists] understand dungeons pretty well; this enables them to move quickly and sneak up on the local nasties. They start equipped with the tools for a proper scientific expedition. %.pg % -\item[\bb{Barbarians}]% +\item[Barbarians] are warriors out of the hinterland, hardened to battle. They begin their quests with naught but uncommon strength, a trusty hauberk, and a great two-handed sword. %.pg % -\item[\bb{Cavemen {\rm and} Cavewomen}] +\item[Cavemen \textrm{\textmd{and}} Cavewomen] start with exceptional strength, but unfortunately, neolithic weapons. %.pg % -\item[\bb{Healers}]% +\item[Healers] are wise in medicine and apothecary. They know the herbs and simples that can restore vitality, ease pain, anesthetize, and neutralize @@ -142,61 +119,61 @@ of health or sickness. Their medical practice earns them quite reasonable amounts of money, with which they enter the dungeon. %.pg % -\item[\bb{Knights}]% +\item[Knights] are distinguished from the common skirmisher by their devotion to the ideals of chivalry and by the surpassing excellence of their armor. %.pg % -\item[\bb{Monks}]% +\item[Monks] are ascetics, who by rigorous practice of physical and mental disciplines have become capable of fighting as effectively without weapons as with. They wear no armor but make up for it with increased mobility. %.pg % -\item[\bb{Priests {\rm and} Priestesses}]% +\item[Priests \textrm{\textmd{and}} Priestesses] are clerics militant, crusaders advancing the cause of righteousness with arms, armor, and arts thaumaturgic. Their ability to commune with deities via prayer occasionally extricates them from peril, but can also put them in it. %.pg % -\item[\bb{Rangers}]% +\item[Rangers] are most at home in the woods, and some say slightly out of place in a dungeon. They are, however, experts in archery as well as tracking and stealthy movement. %.pg % -\item[\bb{Rogues}]% +\item[Rogues] are agile and stealthy thieves, with knowledge of locks, traps, and poisons. Their advantage lies in surprise, which they employ to great advantage. %.pg % -\item[\bb{Samurai}]% +\item[Samurai] are the elite warriors of feudal Nippon. They are lightly armored and quick, and wear the % -{\it dai-sho}, two swords of the deadliest +\textit{dai-sho}, two swords of the deadliest keenness. %.pg % -\item[\bb{Tourists}]% +\item[Tourists] start out with lots of gold (suitable for shopping with), a credit card, lots of food, some maps, and an expensive camera. Most monsters don't like being photographed. %.pg % -\item[\bb{Valkyries}]% +\item[Valkyries] are hardy warrior women. Their upbringing in the harsh Northlands makes them strong, inures them to extremes of cold, and instills in them stealth and cunning. %.pg % -\item[\bb{Wizards}]% +\item[Wizards] start out with a knowledge of magic, a selection of magical items, and a particular affinity for dweomercraft. Although seemingly weak and easy to overcome at first sight, an experienced Wizard is a deadly foe. -\elist +\end{description} %.pg You may also choose the race of your character (within limits; most @@ -204,39 +181,39 @@ roles have restrictions on which races are eligible for them): %.pg % -\blist{} -\item[\bb{Dwarves}]% +\begin{description} +\item[Dwarves] are smaller than humans or elves, but are stocky and solid individuals. Dwarves' most notable trait is their great expertise in mining and metalwork. Dwarvish armor is said to be second in quality not even to the mithril armor of the Elves. %.pg % -\item[\bb{Elves}]% +\item[Elves] are agile, quick, and perceptive; very little of what goes on will escape an Elf. The quality of Elven craftsmanship often gives them an advantage in arms and armor. %.pg % -\item[\bb{Gnomes}]% +\item[Gnomes] are smaller than but generally similar to dwarves. Gnomes are known to be expert miners, and it is known that a secret underground mine complex built by this race exists within the Mazes of Menace, filled with both riches and danger. %.pg % -\item[\bb{Humans}]% +\item[Humans] are by far the most common race of the surface world, and are thus the norm to which other races are often compared. Although they have no special abilities, they can succeed in any role. %.pg % -\item[\bb{Orcs}]% +\item[Orcs] are a cruel and barbaric race that hate every living thing (including other orcs). Above all others, Orcs hate Elves with a passion unequalled, and will go out of their way to kill one at any opportunity. The armor and weapons fashioned by the Orcs are typically of inferior quality. -\elist +\end{description} %.hn 1 \section{What do all those things on the screen mean?} @@ -246,28 +223,28 @@ seen on the current dungeon level; as you explore more of the level, it appears on the screen in front of you. %.pg -When {\it NetHack\/}'s ancestor {\it rogue\/} first appeared, its screen +When \textit{NetHack}'s ancestor \textit{rogue} first appeared, its screen orientation was almost unique among computer fantasy games. Since then, screen orientation has become the norm rather than the -exception; {\it NetHack\/} continues this fine tradition. Unlike text +exception; \textit{NetHack} continues this fine tradition. Unlike text adventure games that accept commands in pseudo-English sentences and -explain the results in words, {\it NetHack\/} commands are all one or two +explain the results in words, \textit{NetHack} commands are all one or two keystrokes and the results are displayed graphically on the screen. A minimum screen size of 24 lines by 80 columns is recommended; if the screen is larger, only a $21\times80$ section will be used for the map. %.pg -{\it NetHack\/} can even be played by blind players, with the assistance of +\textit{NetHack} can even be played by blind players, with the assistance of Braille readers or speech synthesisers. Instructions for configuring -{\it NetHack\/} for the blind are included later in this document. +\textit{NetHack} for the blind are included later in this document. %.pg -{\it NetHack\/} generates a new dungeon every time you play it; even the +\textit{NetHack} generates a new dungeon every time you play it; even the authors still find it an entertaining and exciting game despite having won several times. %.pg -{\it NetHack\/} offers a variety of display options. The options available to +\textit{NetHack} offers a variety of display options. The options available to you will vary from port to port, depending on the capabilities of your hardware and software, and whether various compile-time options were enabled when your executable was created. The three possible display @@ -281,10 +258,10 @@ colors in the Guidebook, and because it is common to all ports, we will use the default ASCII characters from the monochrome character display when referring to things you might see on the screen during your game. %.pg -In order to understand what is going on in {\it NetHack}, first you must -understand what {\it NetHack\/} is doing with the screen. The {\it NetHack\/} +In order to understand what is going on in \textit{NetHack}, first you must +understand what \textit{NetHack} is doing with the screen. The \textit{NetHack} screen replaces the ``You see \ldots'' descriptions of text adventure games. -Figure 1 is a sample of what a {\it NetHack\/} screen might look like. +Figure 1 is a sample of what a \textit{NetHack} screen might look like. The way the screen looks for you depends on your platform. %.BR 2 @@ -337,13 +314,13 @@ Figure 2 The bottom two (or three) lines of the screen contain several cryptic pieces of information describing your current status. Figure 1 shows the traditional two-line status area below the map. -Figure 2 shows just the status area, when the {\it statuslines:3\/} +Figure 2 shows just the status area, when the \textit{statuslines:3} option has been set (not all interfaces support this option). If any status line becomes wider than the screen, you might not see all of it due to truncation. -When the numbers grow bigger and multiple {\it conditions\/} are present, +When the numbers grow bigger and multiple \textit{conditions} are present, the two-line format will run out of room on the second line, but -{\it statuslines:2\/} +\textit{statuslines:2} is the default because a basic 24-line terminal isn't tall enough for the third line. @@ -351,12 +328,12 @@ the third line. Here are explanations of what the various status items mean: %.lp -\blist{} -\item[\bb{Title}] +\begin{description} +\item[Title] Your character's name and professional ranking (based on role and -{\it experience level\/}, see below). +\textit{experience level}, see below). %.lp -\item[\bb{Strength}] +\item[Strength] A measure of your character's strength; one of your six basic attributes. A human character's attributes can range from 3 to 18 inclusive; non-humans may exceed these limits @@ -366,68 +343,68 @@ higher your strength, the stronger you are. Strength affects how successfully you perform physical tasks, how much damage you do in combat, and how much loot you can carry. %.lp -\item[\bb{Dexterity}] +\item[Dexterity] Dexterity affects your chances to hit in combat, to avoid traps, and do other tasks requiring agility or manipulation of objects. %.lp -\item[\bb{Constitution}] +\item[Constitution] Constitution affects your ability to recover from injuries and other strains on your stamina. When strength is low or modest, constitution also affects how much you can carry. With sufficiently high strength, the contribution to carrying capacity from your constitution no longer matters. %.lp -\item[\bb{Intelligence}] +\item[Intelligence] Intelligence affects your ability to cast spells and read spellbooks. %.lp -\item[\bb{Wisdom}] +\item[Wisdom] Wisdom comes from your practical experience (especially when dealing with magic). It affects your magical energy. %.lp -\item[\bb{Charisma}] +\item[Charisma] Charisma affects how certain creatures react toward you. In particular, it can affect the prices shopkeepers offer you. %.lp -\item[\bb{Alignment}] +\item[Alignment] % -{\it Lawful}, {\it Neutral\/} or {\it Chaotic}. Often, Lawful is +\textit{Lawful}, \textit{Neutral} or \textit{Chaotic}. Often, Lawful is taken as good and Chaotic as evil, but legal and ethical do not always coincide. Your alignment influences how other monsters react toward you. Monsters of a like alignment are more likely to be non-aggressive, while those of an opposing alignment are more likely to be seriously offended at your presence. %.lp -\item[\bb{Dungeon Level}] +\item[Dungeon Level] How deep you are in the dungeon. You start at level one and the number increases as you go deeper into the dungeon. Some levels are special, and are identified by a name and not a number. The Amulet of Yendor is reputed to be somewhere beneath the twentieth level. %.lp -\item[\bb{Gold}] +\item[Gold] The number of gold pieces you are openly carrying. Gold which you have concealed in containers is not counted. %.lp -\item[\bb{Hit Points}] +\item[Hit Points] Your current and maximum hit points. Hit points indicate how much damage you can take before you die. The more you get hit in a fight, the lower they get. You can regain hit points by resting, or by using certain magical items or spells. The number in parentheses is the maximum number your hit points can reach. %.lp -\item[\bb{Power}] -Spell points. This tells you how much mystic energy ({\it mana\/}) +\item[Power] +Spell points. This tells you how much mystic energy (\textit{mana}) you have available for spell casting. Again, resting will regenerate the amount available. %.lp -\item[\bb{Armor Class}] +\item[Armor Class] A measure of how effectively your armor stops blows from unfriendly creatures. The lower this number is, the more effective the armor; it is quite possible to have negative armor class. -See the {\it Armor\/} subsection of {\it Objects\/} for more information. +See the \textit{Armor} subsection of \textit{Objects} for more information. %.lp -\item[\bb{Experience}] +\item[Experience] Your current experience level. -If the {\it showexp\/} +If the \textit{showexp} option is set, it will be followed by a slash and experience points. As you adventure, you gain experience points. At certain experience point totals, you gain an experience level. @@ -435,56 +412,56 @@ The more experienced you are, the better you fight and withstand magical attacks. (By the time your level reaches double digits, the usefulness of showing the points with it has dropped significantly. -You can use the `{\tt O}' command to turn {\it showexp\/} +You can use the `\texttt{O}' command to turn \textit{showexp} off to avoid using up the limited status line space.) %.lp -\item[\bb{Time}] +\item[Time] The number of turns elapsed so far, displayed if you have the -{\it time\/} option set. +\textit{time} option set. %.lp -\item[\bb{Status}] +\item[Status] Hunger: your current hunger status. -Values are {\it Satiated}, {\it Not~Hungry\/} (or {\it Normal\/}), -{\it Hungry}, {\it Weak}, and {\it Fainting}. +Values are \textit{Satiated}, \textit{Not~Hungry} (or \textit{Normal}), +\textit{Hungry}, \textit{Weak}, and \textit{Fainting}. %.\" not mentioned: Fainted -Not shown when {\it Normal}. +Not shown when \textit{Normal}. %.lp "" Encumbrance: an indication of how what you are carrying affects your ability to move. -Values are {\it Unencumbered}, {\it Burdened}, {\it Stressed}, -{\it Strained}, {\it Overtaxed}, and {\it Overloaded}. -Not shown when {\it Unencumbered}. +Values are \textit{Unencumbered}, \textit{Burdened}, \textit{Stressed}, +\textit{Strained}, \textit{Overtaxed}, and \textit{Overloaded}. +Not shown when \textit{Unencumbered}. %.lp "" Fatal~conditions: -{\it Stone\/} (aka {\it Petrifying}, turning to stone), -{\it Slime\/} (turning into green slime), -{\it Strngl\/} (being strangled), -{\it FoodPois\/} (suffering from acute food poisoning), -{\it TermIll\/} (suffering from a terminal illness). +\textit{Stone} (aka \textit{Petrifying}, turning to stone), +\textit{Slime} (turning into green slime), +\textit{Strngl} (being strangled), +\textit{FoodPois} (suffering from acute food poisoning), +\textit{TermIll} (suffering from a terminal illness). %.lp "" Non-fatal~conditions: -{\it Blind\/} (can't see), {\it Deaf\/} (can't hear), -{\it Stun\/} (stunned), {\it Conf\/} (confused), {\it Hallu\/} (hallucinating). +\textit{Blind} (can't see), \textit{Deaf} (can't hear), +\textit{Stun} (stunned), \textit{Conf} (confused), \textit{Hallu} (hallucinating). %.lp "" Movement~modifiers: -{\it Lev\/} (levitating), {\it Fly\/} (flying), {\it Ride\/} (riding). +\textit{Lev} (levitating), \textit{Fly} (flying), \textit{Ride} (riding). %.lp "" Other conditions and modifiers exist, but there isn't enough room to display them with the other status fields. \\ % unindented paragraph -The {\tt \#attributes} command (default key {\tt \^{}X}) will show +The \texttt{\#attributes} command (default key \texttt{\textasciicircum X}) will show all current status information in unabbreviated format. It also shows other information which might be included on the status lines if those had more room. -\elist +\end{description} %.hn 2 \subsection*{The message line (top)} @@ -492,14 +469,14 @@ lines if those had more room. %.pg The top line of the screen is reserved for messages that describe things that are impossible to represent visually. If you see a -``{\tt --More--}'' on the top line, this means that {\it NetHack\/} has +``\texttt{--More--}'' on the top line, this means that \textit{NetHack} has another message to display on the screen, but it wants to make certain that you've read the one that is there first. To read the next message, just press the space bar. %.pg To change how and what messages are shown on the message line, -see ``{\it Configuring Message Types\/}`` and the {\it verbose\/} +see ``\textit{Configuring Message Types}`` and the \textit{verbose} option. %.hn 2 @@ -513,91 +490,90 @@ options to change some of the symbols the game uses; otherwise, the game will use default symbols. Here is a list of what the default symbols mean: -\blist{} -\item[\tb{-}] +\begin{description}[font=\mdseries\ttfamily] +\item[-] The horizontal or corner walls of a room, or an open east/west door. -\item[\tb{|}] +\item[|] The vertical walls of a room, or an open north/south door, or a grave. -\item[\tb{.}] +\item[.] The floor of a room, or ice, or a doorless doorway, or the span of an open drawbridge. -\item[\tb{\#}] +\item[\#] A corridor, or iron bars, or a tree, or the portcullis of a closed drawbridge.\\ %.lp "" Note: engravings in corridors also appear as \# but are shown in a different color from normal corridor locations. -\item[\tb{>}] +\item[>] Stairs down: a way to the next level. -\item[\tb{<}] +\item[<] Stairs up: a way to the previous level. -\item[\tb{+}] +\item[+] A closed door, or a spellbook containing a spell you may be able to learn. -\item[\tb{@}] +\item[@] Your character or a human or an elf. -\item[\tb{\$}] +\item[\textdollar] A pile of gold. -\item[\tb{\^}] +\item[\textasciicircum] A trap (once you have detected it). -\item[\tb{)}] +\item[)] A weapon. -\item[\tb{[}] +\item[{[}] A suit or piece of armor. -\item[\tb{\%}] +\item[\%] Something edible (not necessarily healthy). -\item[\tb{?}] +\item[?] A scroll. -\item[\tb{/}] +\item[/] A wand. -\item[\tb{=}] +\item[=] A ring. -\item[\tb{!}] +\item[!] A potion. -\item[\tb{(}] +\item[(] A useful item (pick-axe, key, lamp \ldots). -\item[\tb{"}] +\item["] An amulet or a spider web. -\item[\tb{*}] +\item[*] A gem or rock (possibly valuable, possibly worthless). -\item[\tb{\`}] +\item[\textasciigrave] A boulder or statue or an engraving on the floor of a room.\\ %.lp "" Note: statues are displayed as if they were the monsters they depict -so won't appear as a {\it grave accent\/} (aka {\it back-tick}). -\item[\tb{0}] +so won't appear as a \textit{grave accent} (aka \textit{back-tick}). +\item[0] An iron ball. -\item[\tb{\verb+_+}] +\item[\textunderscore] An altar, or an iron chain. -\item[\tb{\{}] +\item[\textbraceleft] A fountain or a sink. -\item[\tb{\}}] +\item[\textbraceright] A pool of water or moat or a wall of water or a pool of lava or a wall of lava. -\item[\tb{$\backslash$}] +\item[\textbackslash] An opulent throne. -\item[\tb{a-z}] {\normalfont and}] -\item[\tb{A-HJ-Z}] {\normalfont and}] -%should probably change \item[\tb{@\&\verb+'+:;}] to \item[\tb{\verb+@&':;+}] -\item[\tb{@\&\verb+'+:;}] +\item[a-z \textrm{\textmd{and}}] +\item[A-HJ-Z \textrm{\textmd{and}}] +\item[@\&\textquotesingle :;] Letters and certain other symbols represent the various inhabitants of the Mazes of Menace. Watch out, they can be nasty and vicious. Sometimes, however, they can be helpful. -\item[\tb{I}] +\item[I] Rather than a specific type of monster, this marks the last known location of an invisible or otherwise unseen monster. Note that the monster could have moved. -The `{\tt s}', `{\tt F}', and `{\tt m}' commands may be useful here. -\item[\tb{1-5}] +The `\texttt{s}', `\texttt{F}', and `\texttt{m}' commands may be useful here. +\item[1-5] The digits 1 through 5 may be displayed, marking unseen monsters sensed -via the {\it Warning\/} attribute. +via the \textit{Warning} attribute. Less dangerous monsters are indicated by lower values, more dangerous by higher values. -\elist +\end{description} %.pg You need not memorize all these symbols; you can ask the game what any -symbol represents with the `{\tt /}' command (see the next section for +symbol represents with the `\texttt{/}' command (see the next section for more info). %.hn 1 @@ -607,23 +583,23 @@ more info). Commands can be initiated by typing one or two characters to which the command is bound to, or typing the command name in the extended commands entry. Some commands, -like ``{\tt search}'', do not require that any more information be collected -by {\it NetHack\/}. Other commands might require additional information, for +like ``\texttt{search}'', do not require that any more information be collected +by \textit{NetHack}. Other commands might require additional information, for example a direction, or an object to be used. For those commands that -require additional information, {\it NetHack\/} will present you with either +require additional information, \textit{NetHack} will present you with either a menu of choices, or with a command line prompt requesting information. Which you are presented with will depend chiefly on how you have set the -`{\it menustyle\/}' +`\textit{menustyle}' option. %.pg -For example, a common question in the form ``{\tt What do you want to +For example, a common question in the form ``\texttt{What do you want to use? [a-zA-Z\ ?*]}'', asks you to choose an object you are carrying. -Here, ``{\tt a-zA-Z}'' are the inventory letters of your possible choices. -Typing `{\tt ?}' gives you an inventory list of these items, so you can see -what each letter refers to. In this example, there is also a `{\tt *}' +Here, ``\texttt{a-zA-Z}'' are the inventory letters of your possible choices. +Typing `\texttt{?}' gives you an inventory list of these items, so you can see +what each letter refers to. In this example, there is also a `\texttt{*}' indicating that you may choose an object not on the list, if you -wanted to use something unexpected. Typing a `{\tt *}' lists your entire +wanted to use something unexpected. Typing a `\texttt{*}' lists your entire inventory, so you can see the inventory letters of every object you're carrying. Finally, if you change your mind and decide you don't want to do this command after all, you can press the `ESC' key to abort the @@ -631,50 +607,50 @@ command. %.pg You can put a number before some commands to repeat them that many -times; for example, ``{\tt 10s}'' will search ten times. If you have the -{\it number\verb+_+pad\/} -option set, you must type `{\tt n}' to prefix a count, so the example above -would be typed ``{\tt n10s}'' instead. Commands for which counts make no +times; for example, ``\texttt{10s}'' will search ten times. If you have the +\textit{number\textunderscore pad} +option set, you must type `\texttt{n}' to prefix a count, so the example above +would be typed ``\texttt{n10s}'' instead. Commands for which counts make no sense ignore them. In addition, movement commands can be prefixed for greater control (see below). To cancel a count or a prefix, press the `ESC' key. %.pg The list of commands is rather long, but it can be read at any time -during the game through the `{\tt ?}' command, which accesses a menu of +during the game through the `\texttt{?}' command, which accesses a menu of helpful texts. Here are the default key bindings for your reference: -\blist{} +\begin{description}[font=\mdseries\ttfamily] %.lp -\item[\tb{?}] +\item[?] Help menu: display one of several help texts available. %.lp -\item[\tb{/}] -The {\tt whatis} command, to +\item[/] +The \texttt{whatis} command, to tell what a symbol represents. You may choose to specify a location or type a symbol (or even a whole word) to explain. Specifying a location is done by moving the cursor to a particular spot -on the map and then pressing one of `{\tt .}', `{\tt ,}', `{\tt ;}', -or `{\tt :}'. `{\tt .}' will explain the symbol at the chosen location, -conditionally check for ``{\tt More info?}'' depending upon whether the -`{\it help\/}' +on the map and then pressing one of `\texttt{.}', `\texttt{,}', `\texttt{;}', +or `\texttt{:}'. `\texttt{.}' will explain the symbol at the chosen location, +conditionally check for ``\texttt{More info?}'' depending upon whether the +`\textit{help}' option is on, and then you will be asked to pick another location; -`{\tt ,}' will explain the symbol but skip any additional +`\texttt{,}' will explain the symbol but skip any additional information, then let you pick another location; -`{\tt ;}' will skip additional info and also not bother asking -you to choose another location to examine; `{\tt :}' will show additional +`\texttt{;}' will skip additional info and also not bother asking +you to choose another location to examine; `\texttt{:}' will show additional info, if any, without asking for confirmation. When picking a location, -pressing the {\tt ESC} key will terminate this command, or pressing `{\tt ?}' +pressing the \texttt{ESC} key will terminate this command, or pressing `\texttt{?}' will give a brief reminder about how it works. %.lp "" If the -{\it autodescribe\/} +\textit{autodescribe} option is on, a short description of what you see at each location is -shown as you move the cursor. Typing `{\tt \#}' while picking a location will +shown as you move the cursor. Typing `\texttt{\#}' while picking a location will toggle that option on or off. The -{\it whatis\verb+_+coord\/} +\textit{whatis\textunderscore coord} option controls whether the short description includes map coordinates. %.lp "" @@ -685,20 +661,20 @@ always gives any additional information available about that name. You may also request a description of nearby monsters, all monsters currently displayed, nearby objects, or all objects. The -{\it whatis\verb+_+coord\/} +\textit{whatis\textunderscore coord} option controls which format of map coordinate is included with their descriptions. %.lp -\item[\tb{\&}] +\item[\&] Tell what a command does. %.lp -\item[\tb{<}] +\item[<] Go up to the previous level (if you are on a staircase or ladder). %.lp -\item[\tb{>}] +\item[>] Go down to the next level (if you are on a staircase or ladder). %.lp -\item[\tb{[yuhjklbn]}] +\item[{[yuhjklbn]}] Go one step in the direction indicated (see Figure 3). If you sense or remember a monster there, you will fight the monster instead. Only these @@ -712,7 +688,7 @@ one-step movement commands cause you to fight monsters; the others \verb+ h- . -l + & \verb+ 4- . -6 +\\ \verb+ / | \ + & \verb+ / | \ +\\ \verb+ b j n + & \verb+ 1 2 3 +\\ - & (if {\it number\verb+_+pad\/} set) + & (if \textit{number\textunderscore pad} set) \end{tabular} \end{center} %.ed @@ -720,69 +696,69 @@ one-step movement commands cause you to fight monsters; the others Figure 3 \end{center} %.lp -\item[\tb{[YUHJKLBN]}] +\item[{[YUHJKLBN]}] Go in that direction until you hit a wall or run into something. %.lp -\item[\tb{m[yuhjklbn]}] +\item[m{[yuhjklbn]}] Prefix: move without picking up objects or fighting (even if you remember a monster there).\\ %.lp "" -A few non-movement commands use the `{\tt m}' prefix to request +A few non-movement commands use the `\texttt{m}' prefix to request operating via menu (to temporarily override the -{\it menustyle:Traditional\/} +\textit{menustyle:Traditional} option). -Primarily useful for `{\tt ,}' (pickup) when there is only one class of +Primarily useful for `\texttt{,}' (pickup) when there is only one class of objects present (where there won't be any ``what kinds of objects?'' prompt, -so no opportunity to answer `{\tt m}' at that prompt). +so no opportunity to answer `\texttt{m}' at that prompt). \\ %.lp "" The prefix will -make ``{\tt \#travel}'' command show a menu of interesting targets in sight. -It can also be used with the `{\tt $\backslash$}' (known, show a -list of all discovered objects) and the `{\tt \`{}}' (knownclass, +make ``\texttt{\#travel}'' command show a menu of interesting targets in sight. +It can also be used with the `\texttt{\textbackslash}' (known, show a +list of all discovered objects) and the `\texttt{\`{}}' (knownclass, show a list of discovered objects in a particular class) commands to offer a menu of several sorting alternatives (which sets a new value for the -{\it sortdiscoveries\/} -option); also for ``{\tt \#vanquished}'' and ``{\tt \#genocided}'' commands +\textit{sortdiscoveries} +option); also for ``\texttt{\#vanquished}'' and ``\texttt{\#genocided}'' commands to offer a sorting menu. \\ %.lp "" A few other commands (eat food, offer sacrifice, apply tinning-kit, drink/quaff, dip, tip container) use -the `{\tt m}' prefix to skip checking for applicable objects on +the `\texttt{m}' prefix to skip checking for applicable objects on the floor and go straight to checking inventory, -or (for ``{\tt \#loot}'' to remove a saddle), +or (for ``\texttt{\#loot}'' to remove a saddle), skip containers and go straight to adjacent monsters. \\ %.lp "" -In debug mode (aka ``wizard mode''), the `{\tt m}' prefix may also be -used with the ``{\tt \#teleport}'' and ``{\tt \#wizlevelport}'' commands. +In debug mode (aka ``wizard mode''), the `\texttt{m}' prefix may also be +used with the ``\texttt{\#teleport}'' and ``\texttt{\#wizlevelport}'' commands. %.lp -\item[\tb{F[yuhjklbn]}] +\item[F{[yuhjklbn]}] Prefix: fight a monster (even if you only guess one is there). %.lp -\item[\tb{g[yuhjklbn]}] +\item[g{[yuhjklbn]}] Prefix: Move until something interesting is found. %.lp -\item[\tb{G[yuhjklbn] {\rm or} +[yuhjklbn]}] -Prefix: Similar to `{\tt g}', but forking of corridors is not considered +\item[G{[yuhjklbn]} \textrm{\textmd{or}} +{[yuhjklbn]}] +Prefix: Similar to `\texttt{g}', but forking of corridors is not considered interesting. \\ -Note: {\tt +} means holding the {\tt } or -{\tt } key down like {\tt } while typing and releasing -{\tt }, then releasing {\tt }. {\tt \^{}} is used as +Note: \texttt{+} means holding the \texttt{} or +\texttt{} key down like \texttt{} while typing and releasing +\texttt{}, then releasing \texttt{}. \texttt{\textasciicircum } is used as shorthand elsewhere in the Guidebook to mean the same thing. Control -characters are case-insensitive so {\tt \^{}x} and {\tt \^{}X} are the same. +characters are case-insensitive so \texttt{\textasciicircum x} and \texttt{\textasciicircum X} are the same. %.lp -\item[\tb{M[yuhjklbn]}] -Old versions supported `{\tt M}' as a movement prefix which -combined the effect of `{\tt m}' with {\tt +}. +\item[M{[yuhjklbn]}] +Old versions supported `\texttt{M}' as a movement prefix which +combined the effect of `\texttt{m}' with \texttt{+}. That is no longer supported as a prefix but similar effect can be achieved -by using {\tt m} and {\tt G} in combination. -{\tt m} can also be used in combination with {\tt g}, -{\tt +}, or {\tt +}. +by using \texttt{m} and \texttt{G} in combination. +\texttt{m} can also be used in combination with \texttt{g}, +\texttt{+}, or \texttt{+}. %.lp -\item[\tb{\tt \verb+_+}] +\item[\textunderscore] Travel to a map location via a shortest-path algorithm.\\ %.lp "" The shortest path @@ -790,170 +766,170 @@ is computed over map locations the hero knows about (e.g. seen or previously traversed). If there is no known path, a guess is made instead. Stops on most of -the same conditions as the `{\tt G}' command, but without picking up -objects, so implicitly forces the `{\tt m}' prefix. +the same conditions as the `\texttt{G}' command, but without picking up +objects, so implicitly forces the `\texttt{m}' prefix. For ports with mouse support, the command is also invoked when a mouse-click takes place on a location other than the current position. %.lp -\item[\tb{.}] +\item[.] Wait or rest, do nothing for one turn. -Precede with the `{\tt m}' prefix -to wait for a turn even next to a hostile monster, if {\it safe\verb+_+wait\/} +Precede with the `\texttt{m}' prefix +to wait for a turn even next to a hostile monster, if \textit{safe\textunderscore wait} is on. %.lp -\item[\tb{a}] +\item[a] Apply (use) a tool (pick-axe, key, lamp \ldots).\\ %.lp "" If used on a wand, that wand will be broken, releasing its magic in the process. Confirmation is required. %.lp -\item[\tb{A}] +\item[A] Remove one or more worn items, such as armor.\\ %.lp "" -Use `{\tt T}' (take off) to take off only one piece of armor -or `{\tt R}' (remove) to take off only one accessory. +Use `\texttt{T}' (take off) to take off only one piece of armor +or `\texttt{R}' (remove) to take off only one accessory. %.lp -\item[\tb{\^{}A}] +\item[\textasciicircum A] Repeat the previous command. %.lp -\item[\tb{c}] +\item[c] Close a door. %.lp -\item[\tb{C}] +\item[C] Call (name) a monster, an individual object, or a type of object.\\ %.lp "" -Same as extended command ``{\tt \#name}''. +Same as extended command ``\texttt{\#name}''. %.lp -\item[\tb{\^{}C}] +\item[\textasciicircum C] Panic button. Quit the game. %.lp -\item[\tb{d}] +\item[d] Drop something.\\ -For example {\tt d7a} --- drop seven items of object -{\it a}. +For example \texttt{d7a} --- drop seven items of object +\textit{a}. %.lp -\item[\tb{D}] +\item[D] Drop several things.\\ %.lp "" In answer to the question\\ -``{\tt What kinds of things do you want to drop? [!\%= BUCXPaium]}''\\ +``\texttt{What kinds of things do you want to drop? [!\%= BUCXPaium]}''\\ you should type zero or more object symbols possibly followed by -`{\tt a}' and/or `{\tt i}' and/or `{\tt u}' and/or `{\tt m}'. +`\texttt{a}' and/or `\texttt{i}' and/or `\texttt{u}' and/or `\texttt{m}'. In addition, one or more of the bless\-ed/\-un\-curs\-ed/\-curs\-ed groups may be typed.\\ %.sd %.si -{\tt DB} --- drop all objects known to be blessed.\\ -{\tt DU} --- drop all objects known to be uncursed.\\ -{\tt DC} --- drop all objects known to be cursed.\\ -{\tt DX} --- drop all objects of unknown B/U/C status.\\ -{\tt DP} --- drop objects picked up last.\\ -{\tt Da} --- drop all objects, without asking for confirmation.\\ -{\tt Di} --- examine your inventory before dropping anything.\\ -{\tt Du} --- drop only unpaid objects (when in a shop).\\ -{\tt Dm} --- use a menu to pick which object(s) to drop.\\ -{\tt D\%u} --- drop only unpaid food. +\texttt{DB} --- drop all objects known to be blessed.\\ +\texttt{DU} --- drop all objects known to be uncursed.\\ +\texttt{DC} --- drop all objects known to be cursed.\\ +\texttt{DX} --- drop all objects of unknown B/U/C status.\\ +\texttt{DP} --- drop objects picked up last.\\ +\texttt{Da} --- drop all objects, without asking for confirmation.\\ +\texttt{Di} --- examine your inventory before dropping anything.\\ +\texttt{Du} --- drop only unpaid objects (when in a shop).\\ +\texttt{Dm} --- use a menu to pick which object(s) to drop.\\ +\texttt{D\%u} --- drop only unpaid food. %.ei %.ed The last example shows a combination. -There are four categories of object filtering: class (`{\tt !}' for -potions, `{\tt ?}' for scrolls, and so on), shop status (`{\tt u}' for +There are four categories of object filtering: class (`\texttt{!}' for +potions, `\texttt{?}' for scrolls, and so on), shop status (`\texttt{u}' for unpaid, in other words, owned by the shop), bless/curse state -(`{\tt B}', `{\tt U}', `{\tt C}', and `{\tt X}' as shown above), -and novelty (`{\tt P}', recently picked up items; controlled by picking +(`\texttt{B}', `\texttt{U}', `\texttt{C}', and `\texttt{X}' as shown above), +and novelty (`\texttt{P}', recently picked up items; controlled by picking up or dropping things rather than by any time factor). %.lp "" \\ -If you specify more than one value in a category (such as ``{\tt !?}'' for -potions and scrolls or ``{\tt BU}'' for blessed and uncursed), an inventory +If you specify more than one value in a category (such as ``\texttt{!?}'' for +potions and scrolls or ``\texttt{BU}'' for blessed and uncursed), an inventory object will meet the criteria if it matches any of the specified -values (so ``{\tt !?}'' means `{\tt !}' or `{\tt ?}'). +values (so ``\texttt{!?}'' means `\texttt{!}' or `\texttt{?}'). If you specify more than one category, an inventory object must meet -each of the category criteria (so ``{\tt \%u}'' means class `{\tt \%}' and -unpaid `{\tt u}'). +each of the category criteria (so ``\texttt{\%u}'' means class `\texttt{\%}' and +unpaid `\texttt{u}'). Lastly, you may specify multiple values within multiple categories: -``{\tt !?BU}'' will select all potions and scrolls which are known to be +``\texttt{!?BU}'' will select all potions and scrolls which are known to be blessed or uncursed. (In versions prior to 3.6, filter combinations behaved differently.) %.lp -\item[\tb{\^{}D}] +\item[\textasciicircum D] Kick something (usually a door). %.lp -\item[\tb{e}] +\item[e] Eat food.\\ %.lp "" Normally checks for edible item(s) on the floor, then if none are found or none are chosen, checks for edible item(s) in inventory. -Precede `{\tt e}' with the `{\tt m}' prefix to bypass attempting to eat +Precede `\texttt{e}' with the `\texttt{m}' prefix to bypass attempting to eat anything off the floor.\\ %.lp "" If you attempt to eat while already satiated, you might choke to death. If you risk it, you will be asked whether -to ``continue eating?'' {\it if you survive the first bite\/}. +to ``continue eating?'' \textit{if you survive the first bite}. You can set the -{\it paranoid\verb+_+confirmation:eating\/} -option to require a response of ``{\tt yes}'' instead of just `{\tt y}'. +\textit{paranoid\textunderscore confirmation:eating} +option to require a response of ``\texttt{yes}'' instead of just `\texttt{y}'. %.lp % Make sure Elbereth is not hyphenated below, the exact spelling matters. % (Only specified here to parallel Guidebook.mn; use of \tt font implicitly % prevents automatic hyphenation in TeX and LaTeX.) \hyphenation{Elbereth} %override the deduced syllable breaks -\item[\tb{E}] +\item[E] Engrave a message on the floor.\\ %.sd %.si -{\tt E-} --- write in the dust with your fingers.\\ +\texttt{E-} --- write in the dust with your fingers.\\ %.ei %.ed %.lp "" -Engraving the word ``{\tt Elbereth}'' will cause most monsters to not attack +Engraving the word ``\texttt{Elbereth}'' will cause most monsters to not attack you hand-to-hand (but if you attack, you will rub it out); this is often useful to give yourself a breather. %.lp -\item[\tb{f}] +\item[f] Fire (shoot or throw) one of the objects placed in your quiver (or quiver sack, or that you have at the ready). -You may select ammunition with a previous `{\tt Q}' command, or let the -computer pick something appropriate if {\it autoquiver\/} is true. +You may select ammunition with a previous `\texttt{Q}' command, or let the +computer pick something appropriate if \textit{autoquiver} is true. If your wielded weapon has the throw-and-return property, your quiver -is empty, and {\it autoquiver\/} +is empty, and \textit{autoquiver} is false, you will throw that wielded weapon instead of filling the quiver. This will also automatically use a polearm if wielded. -If {\it fireassist\/} is true, firing will automatically try to wield a launcher +If \textit{fireassist} is true, firing will automatically try to wield a launcher (for example, a bow or a sling) matching the ammo in the quiver; this might take multiple turns, and get interrupted by a monster. Remember to swap back to your main melee weapon afterwards. %.lp "" \\ -See also `{\tt t}' (throw) for more general throwing and shooting. +See also `\texttt{t}' (throw) for more general throwing and shooting. %.lp -\item[\tb{i}] +\item[i] List your inventory (everything you're carrying). %.lp -\item[\tb{I}] +\item[I] List selected parts of your inventory, usually be specifying the character -for a particular set of objects, like `{\tt [}' for armor or `{\tt !}' +for a particular set of objects, like `\texttt{[}' for armor or `\texttt{!}' for potions.\\ %.sd %.si -{\tt I*} --- list all gems in inventory;\\ -{\tt Iu} --- list all unpaid items;\\ -{\tt Ix} --- list all used up items that are on your shopping bill;\\ -{\tt IB} --- list all items known to be blessed;\\ -{\tt IU} --- list all items known to be uncursed;\\ -{\tt IC} --- list all items known to be cursed;\\ -{\tt IX} --- list all items whose bless/curse status is unknown;\\ -{\tt IP} --- list items picked up last;\\ -{\tt I\$} --- count your money. +\texttt{I*} --- list all gems in inventory;\\ +\texttt{Iu} --- list all unpaid items;\\ +\texttt{Ix} --- list all used up items that are on your shopping bill;\\ +\texttt{IB} --- list all items known to be blessed;\\ +\texttt{IU} --- list all items known to be uncursed;\\ +\texttt{IC} --- list all items known to be cursed;\\ +\texttt{IX} --- list all items whose bless/curse status is unknown;\\ +\texttt{IP} --- list items picked up last;\\ +\texttt{I\$} --- count your money. %.ei %.ed %.lp -\item[\tb{o}] +\item[o] Open a door. %.lp -\item[\tb{O}] +\item[O] Set options.\\ %.lp "" A menu showing the current option values will be @@ -963,85 +939,85 @@ it, depending on your user interface). For the non-boolean choices, a further menu or prompt will appear once you've closed this menu. The available options are listed later in this Guidebook. Options are usually set before the -game rather than with the `{\tt O}' command; see the section on options below. -Precede {\tt O} with the {\tt m} prefix to show advanced options. +game rather than with the `\texttt{O}' command; see the section on options below. +Precede \texttt{O} with the \texttt{m} prefix to show advanced options. %.lp -\item[\tb{\^{}O}] +\item[\textasciicircum O] Show overview.\\ %.lp "" -Shortcut for ``{\tt \#overview}'': +Shortcut for ``\texttt{\#overview}'': list interesting dungeon levels visited.\\ %.lp "" -(Prior to 3.6.0, `{\tt \^{}O}' was a debug mode command which listed +(Prior to 3.6.0, `\texttt{\textasciicircum O}' was a debug mode command which listed the placement of all special levels. -Use ``{\tt \#wizwhere}'' to run that command.) +Use ``\texttt{\#wizwhere}'' to run that command.) %.lp -\item[\tb{p}] +\item[p] Pay your shopping bill. %.lp -\item[\tb{P}] +\item[P] Put on an accessory (ring, amulet, or blindfold).\\ %.lp "" This command may also be used to wear armor. The prompt for which inventory item to use will only list accessories, but choosing an unlisted item of armor will attempt to wear it. -(See the `{\tt W}' command below. It lists armor as the inventory +(See the `\texttt{W}' command below. It lists armor as the inventory choices but will accept an accessory and attempt to put that on.) %.lp -\item[\tb{\^{}P}] +\item[\textasciicircum P] Repeat previous message.\\ %.lp "" -Subsequent {\tt \^{}P}'s repeat earlier messages. +Subsequent \texttt{\textasciicircum P}'s repeat earlier messages. For some interfaces, the behavior can be varied via the -{\it msg\verb+_+window\/} option. +\textit{msg\textunderscore window} option. %.lp -\item[\tb{q}] +\item[q] Quaff (drink) something (potion, water, etc).\\ %.lp "" When there is a fountain or sink present, it asks whether to drink from that. If that is declined, then it offers a chance to choose a potion from inventory. -Precede {\tt q} with the {\tt m} prefix to skip asking about +Precede \texttt{q} with the \texttt{m} prefix to skip asking about drinking from a fountain or sink. %.lp -\item[\tb{Q}] +\item[Q] Select an object for your quiver, quiver sack, or just generally at the ready (only one of these is available at a time). You can then throw -this (or one of these) using the `{\tt f}' command. +this (or one of these) using the `\texttt{f}' command. %.lp -\item[\tb{r}] +\item[r] Read a scroll or spellbook. %.lp -\item[\tb{R}] +\item[R] Remove a worn accessory (ring, amulet, or blindfold).\\ %.lp "" If you're wearing more than one, you'll be prompted for which one to remove. When you're only wearing one, then by default it will be removed without asking, but you can set the -{\it paranoid\verb+_+confirmation:Remove\/} +\textit{paranoid\textunderscore confirmation:Remove} option to require a prompt.\\ %.lp "" This command may also be used to take off armor. The prompt for which inventory item to remove only lists worn accessories, but an item of worn armor can be chosen. -(See the `{\tt T}' command below. It lists armor as the inventory +(See the `\texttt{T}' command below. It lists armor as the inventory choices but will accept an accessory and attempt to remove it.) %.lp -\item[\tb{\^{}R}] +\item[\textasciicircum R] Redraw the screen. %.lp -\item[\tb{s}] +\item[s] Search for secret doors and traps around you. It usually takes several tries to find something. -Precede with the `{\tt m}' prefix to wait for a turn -even next to a hostile monster, if {\it safe\verb+_+wait\/} +Precede with the `\texttt{m}' prefix to wait for a turn +even next to a hostile monster, if \textit{safe\textunderscore wait} is on.\\ %.lp "" Can also be used to figure out whether there is still a monster at an adjacent ``remembered, unseen monster'' marker. %.lp -\item[\tb{S}] +\item[S] Save the game (which suspends play and exits the program). The saved game will be restored automatically the next time you play using the same character name.\\ @@ -1056,7 +1032,7 @@ without saving and later restore again.\\ There is no ``save current game state and keep playing'' command, not even in explore mode where saved game files can be kept and re-used. %.lp -\item[\tb{t}] +\item[t] Throw an object or shoot a projectile.\\ %.lp "" There's no separate ``shoot'' command. @@ -1065,10 +1041,10 @@ that arrow and any weapon skill bonus or penalty for bow applies. If you ``throw'' an arrow while not wielding a bow, you are throwing it by hand and it will generally be less effective than when shot.\\ %.lp "" -See also `{\tt f}' (fire) for throwing or shooting an item pre-selected -via the `{\tt Q}' (quiver) command, with some extra assistance. +See also `\texttt{f}' (fire) for throwing or shooting an item pre-selected +via the `\texttt{Q}' (quiver) command, with some extra assistance. %.lp -\item[\tb{T}] +\item[T] Take off armor.\\ %.lp "" If you're wearing more than one piece, you'll be prompted for which @@ -1077,208 +1053,208 @@ and/or a shirt, or a suit covering a shirt, as if the underlying items weren't there.) When you're only wearing one, then by default it will be taken off without asking, but you can set the -{\it paranoid\verb+_+confirmation:Remove\/} +\textit{paranoid\textunderscore confirmation:Remove} option to require a prompt.\\ %.lp "" This command may also be used to remove accessories. The prompt for which inventory item to take off only lists worn armor, but a worn accessory can be chosen. -(See the `{\tt R}' command above. It lists accessories as the inventory +(See the `\texttt{R}' command above. It lists accessories as the inventory choices but will accept an item of armor and attempt to take it off.) %.lp -\item[\tb{\^{}T}] +\item[\textasciicircum T] Teleport, if you have the ability. %.lp -\item[\tb{v}] +\item[v] Display version number. %.lp -\item[\tb{V}] +\item[V] Display the game history. %.lp -\item[\tb{w}] +\item[w] Wield weapon.\\ %.sd %.si -{\tt w-} --- wield nothing, use your bare (or gloved) hands.\\ +\texttt{w-} --- wield nothing, use your bare (or gloved) hands.\\ %.ei %.ed -Some characters can wield two weapons at once; use the `{\tt X}' command -(or the ``{\tt \#twoweapon}'' extended command) to do so. +Some characters can wield two weapons at once; use the `\texttt{X}' command +(or the ``\texttt{\#twoweapon}'' extended command) to do so. %.lp -\item[\tb{W}] +\item[W] Wear armor.\\ %.lp "" This command may also be used to put on an accessory (ring, amulet, or blindfold). The prompt for which inventory item to use will only list armor, but choosing an unlisted accessory will attempt to put it on. -(See the `{\tt P}' command above. It lists accessories as the inventory +(See the `\texttt{P}' command above. It lists accessories as the inventory choices but will accept an item of armor and attempt to wear it.) %.lp -\item[\tb{x}] +\item[x] Exchange your wielded weapon with the item in your alternate weapon slot.\\ %.lp "" The latter is used as your secondary weapon when engaging in two-weapon combat. Note that if one of these slots is empty, the exchange still takes place. %.lp -\item[\tb{X}] +\item[X] Toggle two-weapon combat, if your character can do it. Also available -via the ``{\tt \#twoweapon}'' extended command.\\ +via the ``\texttt{\#twoweapon}'' extended command.\\ %.lp "" (In versions prior to 3.6 this keystroke ran the command to switch from normal play to ``explore mode'', also known as ``discovery mode'', which has now -been moved to ``{\tt \#exploremode}'' and {\tt M-X}.) +been moved to ``\texttt{\#exploremode}'' and \texttt{M-X}.) %.lp -\item[\tb{\^{}X}] +\item[\textasciicircum X] Display basic information about your character.\\ %.lp "" Displays name, role, race, gender (unless role name makes that -redundant, such as {\tt Caveman} or {\tt Priestess}), and alignment, +redundant, such as \texttt{Caveman} or \texttt{Priestess}), and alignment, along with your patron deity and his or her opposition. It also shows most of the various items of information from the status line(s) in a less terse form, including several additional things which don't appear in the normal status display due to space considerations.\\ %.lp "" -In normal play, that's all that `{\tt \^{}X}' displays. +In normal play, that's all that `\texttt{\textasciicircum X}' displays. In explore mode, the role and status feedback is augmented by the -information provided by {\it enlightenment\/} magic. +information provided by \textit{enlightenment} magic. %.lp -\item[\tb{z}] +\item[z] Zap a wand.\\ %.sd %.si -{\tt z.} --- to aim at yourself, use `{\tt .}' for the direction. +\texttt{z.} --- to aim at yourself, use `\texttt{.}' for the direction. %.ei %.ed %.lp -\item[\tb{Z}] +\item[Z] Zap (cast) a spell.\\ %.sd %.si -{\tt Z.} --- to cast at yourself, use `{\tt .}' for the direction. +\texttt{Z.} --- to cast at yourself, use `\texttt{.}' for the direction. %.ei %.ed %.lp -\item[\tb{\^{}Z}] +\item[\textasciicircum Z] Suspend the game (UNIX versions with job control only). See ``\#suspend'' below for more details. %.lp -\item[\tb{:}] +\item[:] Look at what is here. %.lp -\item[\tb{;}] +\item[;] Show what type of thing a visible symbol corresponds to. %.lp -\item[\tb{,}] +\item[,] Pick up some things from the floor beneath you.\\ %.lp "" -May be preceded by `{\tt m}' to force a selection menu. +May be preceded by `\texttt{m}' to force a selection menu. %.lp -\item[\tb{@}] -Toggle the {\it autopickup\/} option on and off. +\item[@] +Toggle the \textit{autopickup} option on and off. %.lp -\item[\tb{\^{}}] +\item[\textasciicircum] Ask for the type of an adjacent trap you found earlier. %.lp -\item[\tb{)}] +\item[)] Tell what weapon you are wielding. %.lp -\item[\tb{[}] +\item[{]}] Tell what armor you are wearing. %.lp -\item[\tb{=}] +\item[=] Tell what rings you are wearing. %.lp -\item[\tb{"}] +\item["] Tell what amulet you are wearing. %.lp -\item[\tb{(}] +\item[(] Tell what tools you are using. %.lp -\item[\tb{*}] +\item[*] Tell what equipment you are using.\\ %.lp "" Combines the preceding five type-specific commands into one. %.lp -\item[\tb{\$}] +\item[\$] Report the gold you're carrying, possibly shop credit and/or debt too. %.lp -\item[\tb{+}] +\item[+] List the spells you know.\\ %.lp "" Using this command, you can also rearrange the order in which your spells are listed, either by sorting the entire list or by picking one spell from the menu then picking another to swap places with it. Swapping pairs of spells changes their casting letters, -so the change lasts after the current `{\tt +}' command finishes. Sorting +so the change lasts after the current `\texttt{+}' command finishes. Sorting the whole list is temporary. To make the most recent sort order persist -beyond the current `{\tt +}' command, choose the sort option again and then +beyond the current `\texttt{+}' command, choose the sort option again and then pick ``reassign casting letters''. (Any spells learned after that will be added to the end of the list rather than be inserted into the sorted ordering.) %.lp -\item[\tb{$\backslash$}] +\item[\textbackslash] Show what types of objects have been discovered. \\ %.lp "" -May be preceded by `{\tt m}' to select preferred display order. +May be preceded by `\texttt{m}' to select preferred display order. %.lp -\item[\tb{\`}] +\item[\textasciigrave] Show discovered types for one class of objects. \\ -.lp "" -May be preceded by `{\tt m}' to select preferred display order. +%.lp "" +May be preceded by `\texttt{m}' to select preferred display order. %.lp -\item[\tb{|}] +\item[|] If persistent inventory display is supported and enabled (with the -{\it perm\verb+_+invent\/} +\textit{perm\textunderscore invent} option), interact with it instead of with the map. \\ %.lp "" Allows scrolling with the -menu\verb+_+first\verb+_+page, menu\verb+_+previous\verb+_+page, -menu\verb+_+next\verb+_+page, and menu\verb+_+last\verb+_+page -keys (`{\tt \^{}}', `{\tt <}', `{\tt >}', `{\tt \verb+|+}' by default). -Some interfaces also support menu\verb+_+shift\verb+_+left and menu\verb+_+shift\verb+_+right -keys (`{\tt \verb+{+}' and `{\tt \verb+}+}' by default). -Use the {\it Return\/} (aka {\it Enter\/}) or {\it Escape\/} key to +menu\textunderscore first\textunderscore page, menu\textunderscore previous\textunderscore page, +menu\textunderscore next\textunderscore page, and menu\textunderscore last\textunderscore page +keys (`\texttt{\textasciicircum}', `\texttt{<}', `\texttt{>}', `\texttt{|}' by default). +Some interfaces also support menu\textunderscore shift\textunderscore left and menu\textunderscore shift\textunderscore right +keys (`\texttt{\textbraceleft}' and `\texttt{\textbraceright}' by default). +Use the \textit{Return} (aka \textit{Enter}) or \textit{Escape} key to resume play. %.lp -\item[\tb{!}] +\item[!] Escape to a shell. See ``\#shell'' below for more details. %.lp -\item[\tb{Del}] +\item[Del] Show map without obstructions. You can view the explored portion of the current level's map without monsters; without monsters and objects; or without monsters, objects, and traps.\\ %.lp "" -The {\tt } key is also shown as {\tt } on some keyboards or -{\tt } on others. -It is sometimes displayed as {\tt \^{}?} even though that is not an actual +The \texttt{} key is also shown as \texttt{} on some keyboards or +\texttt{} on others. +It is sometimes displayed as \texttt{\textasciicircum ?} even though that is not an actual control character.\\ %.lp "" -Many terminals have an option to swap the {\tt } and {\tt } -keys, so typing the {\tt } key might not execute this command. -If that happens, you can use the extended command ``{\tt \#terrain}'' instead. +Many terminals have an option to swap the \texttt{} and \texttt{} +keys, so typing the \texttt{} key might not execute this command. +If that happens, you can use the extended command ``\texttt{\#terrain}'' instead. %.lp -\item[\tb{\#}] +\item[\#] Perform an extended command.\\ %.lp "" -As you can see, the authors of {\it NetHack\/} +As you can see, the authors of \textit{NetHack} used up all the letters, so this is a way to introduce the less frequently used commands. What extended commands are available depends on what features the game was compiled with. %.lp -\item[\tb{\#adjust}] +\item[\#adjust] Adjust inventory letters (most useful when the -{\it fixinv\/} -option is ``on''). Autocompletes. Default key is `{\tt M-a}'.\\ +\textit{fixinv} +option is ``on''). Autocompletes. Default key is `\texttt{M-a}'.\\ %.lp "" This command allows you to move an item from one particular inventory slot to another so that it has a letter which is more meaningful for you @@ -1287,262 +1263,266 @@ are displayed. You can move to a currently empty slot, or if the destination is occupied---and won't merge---the item there will swap slots with the one being moved. -``{\tt \#adjust}'' can also be used to split a stack of objects; when +``\texttt{\#adjust}'' can also be used to split a stack of objects; when choosing the item to adjust, enter a count prior to its letter.\\ %.lp "" Adjusting without a count used to collect all compatible stacks when moving to the destination. That behavior has been changed; to gather -compatible stacks, ``{\tt \#adjust}'' a stack into its own inventory slot. +compatible stacks, ``\texttt{\#adjust}'' a stack into its own inventory slot. If it has a name assigned, other stacks with the same name or with no name will merge provided that all their other attributes match. If it does not have a name, only other stacks with no name are eligible. In either case, otherwise compatible stacks with a different name -will not be merged. This contrasts with using ``{\tt \#adjust}'' to move +will not be merged. This contrasts with using ``\texttt{\#adjust}'' to move from one slot to a different slot. In that situation, moving (no count given) a compatible stack will merge if either stack has a name when the other doesn't and give that name to the result, while splitting (count given) will ignore the source stack's name when deciding whether to merge with the destination stack. %.lp -\item[\tb{\#annotate}] +\item[\#annotate] Allows you to specify one line of text to associate with the current dungeon level. All levels with annotations are displayed by the -``{\tt \#overview}'' command. Autocompletes. -Default key is `{\tt M-A}', -and also `{\tt \^{}N}' if {\it number\verb+_+pad\/} is on. -%.lp -\item[\tb{\#apply}] -Apply (use) a tool such as a pick-axe, a key, or a lamp. -Default key is `{\tt a}'.\\ +``\texttt{\#overview}'' command. Autocompletes. +Default key is `\texttt{M-A}', +and also `\texttt{\textasciicircum N}' if \textit{number\textunderscore pad} is on. +\\ %.lp "" -If the tool used acts on items on the floor, using the `{\tt m}' prefix +Preceding \#annotate with the `\texttt{m}' prefix is the same as +\#overview with the prefix. +%.lp +\item[\#apply] +Apply (use) a tool such as a pick-axe, a key, or a lamp. +Default key is `\texttt{a}'.\\ +%.lp "" +If the tool used acts on items on the floor, using the `\texttt{m}' prefix skips those items.\\ %.lp "" If used on a wand, that wand will be broken, releasing its magic in the process. Confirmation is required. %.lp -\item[\tb{\#attributes}] -Show your attributes. Default key is `{\tt \^{}X}'. +\item[\#attributes] +Show your attributes. Default key is `\texttt{\textasciicircum X}'. %.lp -\item[\tb{\#autopickup}] -Toggle the {\it autopickup\/} option. Default key is `{\tt @}'. +\item[\#autopickup] +Toggle the \textit{autopickup} option. Default key is `\texttt{@}'. %.lp -\item[\tb{\#bugreport}] -Bring up a browser window to submit a report to the {\it NetHack Development +\item[\#bugreport] +Bring up a browser window to submit a report to the \textit{NetHack Development Team}. Can be disabled at the time the program is built; when enabled, CRASHREPORTURL must be set in the system configuration file. %.lp -\item[\tb{\#call}] +\item[\#call] Call (name) a monster, or an object in inventory, on the floor, or in the discoveries list, or add an annotation for the -current level (same as ``{\tt \#annotate}''). Default key is `{\tt C}'. +current level (same as ``\texttt{\#annotate}''). Default key is `\texttt{C}'. %.lp -\item[\tb{\#cast}] -Cast a spell. Default key is `{\tt Z}'. +\item[\#cast] +Cast a spell. Default key is `\texttt{Z}'. %.lp -\item[\tb{\#chat}] -Talk to someone. Default key is `{\tt M-c}'. +\item[\#chat] +Talk to someone. Default key is `\texttt{M-c}'. %.lp -\item[\tb{\#chronicle}] -Show a list of important game events. +\item[\#chronicle] +Show a list of important game events. Default key is `\texttt{v}'. %.lp -\item[\tb{\#close}] -Close a door. Default key is `{\tt c}'. +\item[\#close] +Close a door. Default key is `\texttt{c}'. %.lp -\item[\tb{\#conduct}] +\item[\#conduct] List voluntary challenges you have maintained. Autocompletes. -Default key is `{\tt M-C}'.\\ +Default key is `\texttt{M-C}'.\\ %.lp "" See the section below entitled ``Conduct'' for details. %.lp -\item[\tb{\#debugfuzzer}] +\item[\#debugfuzzer] Start the fuzz tester. Debug mode only. %.lp -\item[\tb{\#dip}] -Dip an object into something. Autocompletes. Default key is `{\tt M-d}'.\\ +\item[\#dip] +Dip an object into something. Autocompletes. Default key is `\texttt{M-d}'.\\ %.lp "" -The {\tt m} prefix skips dipping into a fountain or pool if there +The \texttt{m} prefix skips dipping into a fountain or pool if there is one at your location. %.lp -\item[\tb{\#down}] -Go down a staircase. Default key is `{\tt >}'. +\item[\#down] +Go down a staircase. Default key is `\texttt{>}'. %.lp -\item[\tb{\#drop}] -Drop an item. Default key is `{\tt d}'. +\item[\#drop] +Drop an item. Default key is `\texttt{d}'. %.lp -\item[\tb{\#droptype}] -Drop specific item types. Default key is `{\tt D}'. +\item[\#droptype] +Drop specific item types. Default key is `\texttt{D}'. %.lp -\item[\tb{\#eat}] -Eat something. Default key is `{\tt e}'. -The `{\tt m}' prefix skips eating items on the floor. +\item[\#eat] +Eat something. Default key is `\texttt{e}'. +The `\texttt{m}' prefix skips eating items on the floor. %.lp -\item[\tb{\#engrave}] -Engrave writing on the floor. Default key is `{\tt E}'. +\item[\#engrave] +Engrave writing on the floor. Default key is `\texttt{E}'. %.lp -\item[\tb{\#enhance}] +\item[\#enhance] Advance or check weapon and spell skills. Autocompletes. -Default key is `{\tt M-e}'. +Default key is `\texttt{M-e}'. %.lp -\item[\tb{\#exploremode}] +\item[\#exploremode] Switch from normal play to non-scoring explore mode. -Default key is `{\tt M-X}'.\\ +Default key is `\texttt{M-X}'.\\ %.lp "" -Requires confirmation; default response is `{\tt n}' (no). -To really switch to explore mode, respond with `{\tt y}'. +Requires confirmation; default response is `\texttt{n}' (no). +To really switch to explore mode, respond with `\texttt{y}'. You can set the -{\it paranoid\verb+_+confirmation:quit\/} -option to require a response of ``{\tt yes}'' instead. +\textit{paranoid\textunderscore confirmation:quit} +option to require a response of ``\texttt{yes}'' instead. %.lp -\item[\tb{\#fight}] +\item[\#fight] Prefix key to force fight a direction, even if you see nothing to fight there. -Default key is `{\tt F}', or `{\tt -}' with -{\it number\verb+_+pad\/} +Default key is `\texttt{F}', or `\texttt{-}' with +\textit{number\textunderscore pad} %.lp -\item[\tb{\#fire}] +\item[\#fire] Fire ammunition from quiver, possibly autowielding a launcher, or hit with a wielded polearm. -Default key is `{\tt f}'. +Default key is `\texttt{f}'. %.lp -\item[\tb{\#force}] -Force a lock. Autocompletes. Default key is `{\tt M-f}'. +\item[\#force] +Force a lock. Autocompletes. Default key is `\texttt{M-f}'. %.lp -\item[\tb{\#genocided}] +\item[\#genocided] List any monster types which have been genocided. In explore mode and debug mode it also shows types which have become extinct. \\ %.lp "" -The display order is the same as is used by {\\tt \#vanquished}. -The `{\tt m}' prefix brings up a menu of available sorting orders, and -doing that for either {\\tt \#genocided} or {\\tt \#vanquished} changes the order for both. +The display order is the same as is used by \texttt{\#vanquished}. +The `\texttt{m}' prefix brings up a menu of available sorting orders, and +doing that for either \texttt{\#genocided} or \texttt{\#vanquished} changes the order for both. \\ %.lp "" If the sorting order is ``count high to low'' or ``count low to high'' -(which are applicable for {\tt \#vanquished}), that will be ignored -for {\tt \#genocided} and alphabetical will be used instead. -The menu omits those two choices when used for {\tt \#genocide}. +(which are applicable for \texttt{\#vanquished}), that will be ignored +for \texttt{\#genocided} and alphabetical will be used instead. +The menu omits those two choices when used for \texttt{\#genocide}. \\ %.lp "" Autocompletes. -Default key is `{\tt M-g}'. +Default key is `\texttt{M-g}'. %.lp -\item[\tb{\#glance}] -Show what type of thing a map symbol corresponds to. Default key is `{\tt ;}'. +\item[\#glance] +Show what type of thing a map symbol corresponds to. Default key is `\texttt{;}'. %.lp -\item[\tb{\#help}] +\item[\#help] Show the help menu. -Default key is `{\tt ?}', -and also `{\tt h}' if {\it number\verb+_+pad\/} is on. +Default key is `\texttt{?}', +and also `\texttt{h}' if \textit{number\textunderscore pad} is on. %.lp -\item[\tb{\#herecmdmenu}] +\item[\#herecmdmenu] Show a menu of possible actions directed at your current location. The menu is limited to a subset of the likeliest actions, not an exhaustive set of all possibilities. Autocompletes.\\ %.lp "" -If mouse support is enabled and the {\it herecmd\verb+_+menu\/} +If mouse support is enabled and the \textit{herecmd\textunderscore menu} option is On, clicking on the hero (or steed when mounted) will execute this command. %.lp -\item[\tb{\#history}] -Show long version and game history. Default key is `{\tt V}'. +\item[\#history] +Show long version and game history. %.lp -\item[\tb{\#inventory}] -Show your inventory. Default key is `{\tt i}'. +\item[\#inventory] +Show your inventory. Default key is `\texttt{i}'. %.lp -\item[\tb{\#inventtype}] -Inventory specific item types. Default key is `{\tt I}'. +\item[\#inventtype] +Inventory specific item types. Default key is `\texttt{I}'. %.lp -\item[\tb{\#invoke}] -Invoke an object's special powers. Autocompletes. Default key is `{\tt M-i}'. +\item[\#invoke] +Invoke an object's special powers. Autocompletes. Default key is `\texttt{M-i}'. %.lp -\item[\tb{\#jump}] +\item[\#jump] Jump to another location. Autocompletes. -Default key is `{\tt M-j}', -and also `{\tt j}' if {\it number\verb+_+pad\/} is on. +Default key is `\texttt{M-j}', +and also `\texttt{j}' if \textit{number\textunderscore pad} is on. %.lp -\item[\tb{\#kick}] +\item[\#kick] Kick something. -Default key is `{\tt \^{}D}', -and also `{\tt k}' if {\it number\verb+_+pad\/} is on. +Default key is `\texttt{\textasciicircum D}', +and also `\texttt{k}' if \textit{number\textunderscore pad} is on. %.lp -\item[\tb{\#known}] +\item[\#known] Show what object types have been discovered. -Default key is `{\tt $\backslash$}'. +Default key is `\texttt{\textbackslash}'. \\ %.lp "" -The `{\tt m}' prefix allows assigning a new value to the -{\it sortdiscoveries\/} +The `\texttt{m}' prefix allows assigning a new value to the +\textit{sortdiscoveries} option to control the order in which the discoveries are displayed. %.lp -\item[\tb{\#knownclass}] +\item[\#knownclass] Show discovered types for one class of objects. -Default key is `{\tt `}'. +Default key is `\texttt{`}'. \\ %.lp "" -The `{\tt m}' prefix operates the same as for {\tt \#known}. +The `\texttt{m}' prefix operates the same as for \texttt{\#known}. %.lp -\item[\tb{\#levelchange}] +\item[\#levelchange] Change your experience level. Autocompletes. Debug mode only. %.lp -\item[\tb{\#lightsources}] +\item[\#lightsources] Show mobile light sources. Autocompletes. Debug mode only. %.lp -\item[\tb{\#look}] -Look at what is here, under you. Default key is `{\tt :}'. +\item[\#look] +Look at what is here, under you. Default key is `\texttt{:}'. %.lp -\item[\tb{\#lookaround}] +\item[\#lookaround] Describe what you can see, or remember, of your surroundings. %.lp -\item[\tb{\#loot}] +\item[\#loot] Loot a box or bag on the floor beneath you, or the saddle from a steed standing next to you. Autocompletes. -Precede with the `{\tt m}' prefix to skip containers at your location +Precede with the `\texttt{m}' prefix to skip containers at your location and go directly to removing a saddle. -Default key is `{\tt M-l}', -and also `{\tt l}' if {\it number\verb+_+pad\/} is on. +Default key is `\texttt{M-l}', +and also `\texttt{l}' if \textit{number\textunderscore pad} is on. %.lp -\item[\tb{\#monster}] +\item[\#monster] Use a monster's special ability (when polymorphed into monster form). -Autocompletes. Default key is `{\tt M-m}'. +Autocompletes. Default key is `\texttt{M-m}'. %.lp -\item[\tb{\#name}] +\item[\#name] Name a monster, an individual object, or a type of object. -Same as ``{\tt \#call}''. +Same as ``\texttt{\#call}''. Autocompletes. -Default keys are `{\tt N}', `{\tt M-n}', and `{\tt M-N}'. +Default keys are `\texttt{N}', `\texttt{M-n}', and `\texttt{M-N}'. %.lp -\item[\tb{\#offer}] -Offer a sacrifice to the gods. Autocompletes. Default key is `{\tt M-o}'.\\ +\item[\#offer] +Offer a sacrifice to the gods. Autocompletes. Default key is `\texttt{M-o}'.\\ %.lp "" You'll need to find an altar to have any chance at success. Corpses of recently killed monsters are the fodder of choice. %.lp "" -The `{\tt m}' prefix skips offering any items which are on the altar.\\ +The `\texttt{m}' prefix skips offering any items which are on the altar.\\ %.lp -\item[\tb{\#open}] -Open a door. Default key is `{\tt o}'. +\item[\#open] +Open a door. Default key is `\texttt{o}'. %.lp -\item[\tb{\#options}] -Show and change option settings. Default key is `{\tt O}'. -Precede with the {\tt m} prefix to show advanced options. +\item[\#options] +Show and change option settings. Default key is `\texttt{O}'. +Precede with the \texttt{m} prefix to show advanced options. %.lp -\item[\tb{\#optionsfull}] +\item[\#optionsfull] Show advanced game option settings. No default key. -Precede with the `{\tt m}' prefix to execute the simpler options command. -(Mainly useful if you use {\tt BINDING=O:optionsfull} to switch -`{\tt O}' from simple options back to traditional advanced options.) +Precede with the `\texttt{m}' prefix to execute the simpler options command. +(Mainly useful if you use \texttt{BINDING=O:optionsfull} to switch +`\texttt{O}' from simple options back to traditional advanced options.) %.lp -\item[\tb{\#overview}] +\item[\#overview] Display information you've discovered about the dungeon. Any visited level % [note: amnesia no longer causes levels to be forgotten so exclude this] @@ -1554,7 +1534,7 @@ If dungeon overview is chosen during end-of-game disclosure, every visited level will be included regardless of annotations. \\ %.lp "" -Precede \#overview with the `{\tt m}' prefix to display the dungeon +Precede \#overview with the `\texttt{m}' prefix to display the dungeon overview as a menu where you can select any visited level to add or remove an annotation without needing to return to that level. This will also force all visited levels to be displayed rather than just @@ -1562,43 +1542,43 @@ the ``interesting'' subset. \\ %.lp "" Autocompletes. -Default keys are `{\tt \^{}O}', and `{\tt M-O}'. +Default keys are `\texttt{\textasciicircum O}', and `\texttt{M-O}'. % DON'T PANIC! %.lp -\item[\tb{\#panic}] +\item[\#panic] Test the panic routine. Terminates the current game. Autocompletes. Debug mode only.\\ %.lp "" -Asks for confirmation; default is `{\tt n}' (no); continue playing. -To really panic, respond with `{\tt y}'. +Asks for confirmation; default is `\texttt{n}' (no); continue playing. +To really panic, respond with `\texttt{y}'. You can set the -{\it paranoid\verb+_+confirmation:quit\/} -option to require a response of ``{\tt yes}'' instead. +\textit{paranoid\textunderscore confirmation:quit} +option to require a response of ``\texttt{yes}'' instead. %.lp -\item[\tb{\#pay}] -Pay your shopping bill. Default key is `{\tt p}'. +\item[\#pay] +Pay your shopping bill. Default key is `\texttt{p}'. %.lp -\item[\tb{\#perminv}] +\item[\#perminv] If persistent inventory display is supported and enabled (with the -{\it perm\verb+_+invent\/} option), interact with it instead of with the map. +\textit{perm\textunderscore invent} option), interact with it instead of with the map. You'll be prompted for menu scrolling keystrokes such -as `{\tt \verb+>+}' and `{\tt \verb+<+}'. -Press {\tt Return} or {\tt Escape} to resume normal play. -Default key is {\tt \verb+|+}. +as `\texttt{>}' and `\texttt{<}'. +Press \texttt{Return} or \texttt{Escape} to resume normal play. +Default key is \texttt{|}. %.lp -\item[\tb{\#pickup}] -Pick up things at the current location. Default key is `{\tt ,}'. -The `{\tt m}' prefix forces use of a menu. +\item[\#pickup] +Pick up things at the current location. Default key is `\texttt{,}'. +The `\texttt{m}' prefix forces use of a menu. %.lp -\item[\tb{\#polyself}] +\item[\#polyself] Polymorph self. Autocompletes. Debug mode only. %.lp -\item[\tb{\#pray}] -Pray to the gods for help. Autocompletes. Default key is `{\tt M-p}'.\\ +\item[\#pray] +Pray to the gods for help. Autocompletes. Default key is `\texttt{M-p}'.\\ %.lp "" Praying too soon after receiving prior help is a bad idea. (Hint: entering the dungeon alive is treated as having received help. @@ -1606,200 +1586,200 @@ You probably shouldn't start off a new game by praying right away.) Since using this command by accident can cause trouble, there is an option to make you confirm your intent before praying. It is enabled by default, and you can reset the -{\it paranoid\verb+_+confirmation\/} +\textit{paranoid\textunderscore confirmation} option to disable it. %.lp -\item[\tb{\#prevmsg}] -Show previously displayed game messages. Default key is `{\tt \^{}P}'. +\item[\#prevmsg] +Show previously displayed game messages. Default key is `\texttt{\textasciicircum P}'. %.lp -\item[\tb{\#puton}] -Put on an accessory (ring, amulet, etc). Default key is `{\tt P}'. +\item[\#puton] +Put on an accessory (ring, amulet, etc). Default key is `\texttt{P}'. %.lp -\item[\tb{\#quaff}] -Quaff (drink) something. Default key is `{\tt q}'.\\ +\item[\#quaff] +Quaff (drink) something. Default key is `\texttt{q}'.\\ %.lp "" -The {\tt m} prefix skips drinking from a fountain or sink if there +The \texttt{m} prefix skips drinking from a fountain or sink if there is one at your location. %.lp -\item[\tb{\#quit}] +\item[\#quit] Quit the program without saving your game. Autocompletes.\\ %.lp "" Since using this command by accident would throw away the current game, you are asked to confirm your intent before quitting. -Default response is `{\tt n}' (no); continue playing. -To really quit, respond with `{\tt y}'. +Default response is `\texttt{n}' (no); continue playing. +To really quit, respond with `\texttt{y}'. You can set the -{\it paranoid\verb+_+confirmation:quit\/} -option to require a response of ``{\tt yes}'' instead. +\textit{paranoid\textunderscore confirmation:quit} +option to require a response of ``\texttt{yes}'' instead. %.lp -\item[\tb{\#quiver}] -Select ammunition for quiver. Default key is `{\tt Q}'. +\item[\#quiver] +Select ammunition for quiver. Default key is `\texttt{Q}'. %.lp -\item[\tb{\#read}] -Read a scroll, a spellbook, or something else. Default key is `{\tt r}'. +\item[\#read] +Read a scroll, a spellbook, or something else. Default key is `\texttt{r}'. %.lp -\item[\tb{\#redraw}] +\item[\#redraw] Redraw the screen. -Default key is `{\tt \^{}R}', -and also `{\tt \^{}L}' if {\it number\verb+_+pad\/} is on. +Default key is `\texttt{\textasciicircum R}', +and also `\texttt{\textasciicircum L}' if \textit{number\textunderscore pad} is on. %.lp -\item[\tb{\#remove}] -Remove an accessory (ring, amulet, etc). Default key is `{\tt R}'. +\item[\#remove] +Remove an accessory (ring, amulet, etc). Default key is `\texttt{R}'. %.lp -\item[{\tb{\#repeat}}] +\item[\#repeat] Repeat the previous command. -Default key is~`{\tt \^{}A}'. +Default key is~`\texttt{\textasciicircum A}'. %.lp -\item[\tb{\#reqmenu}] +\item[\#reqmenu] Prefix key to modify the behavior or request menu from some commands. Prevents autopickup when used with movement commands. -Default key is `{\tt m}'. +Default key is `\texttt{m}'. %.lp -\item[\tb{\#retravel}] +\item[\#retravel] Travel to a previously selected travel destination. -Default key is `{\tt C-\verb+_+}'. -See also {\tt \#travel}. +Default key is `\texttt{C-\textunderscore }'. +See also \texttt{\#travel}. %.lp -\item[\tb{\#ride}] +\item[\#ride] Ride (or stop riding) a saddled creature. Autocompletes. -Default key is `{\tt M-R}'. +Default key is `\texttt{M-R}'. %.lp -\item[\tb{\#rub}] -Rub a lamp or a stone. Autocompletes. Default key is `{\tt M-r}'. +\item[\#rub] +Rub a lamp or a stone. Autocompletes. Default key is `\texttt{M-r}'. %.lp -\item[\tb{\#run}] +\item[\#run] Prefix key to run towards a direction. -Default key is `{\tt G}' when -{\it number\verb+_+pad\/} +Default key is `\texttt{G}' when +\textit{number\textunderscore pad} is off, -`{\tt 5}' when -{\it number\verb+_+pad\/} +`\texttt{5}' when +\textit{number\textunderscore pad} is set to 1~or~3, -otherwise `{\tt M-5}' when it is set to 2~or~4. +otherwise `\texttt{M-5}' when it is set to 2~or~4. %.lp -\item[\tb{\#rush}] +\item[\#rush] Prefix key to rush towards a direction. -Default key is `{\tt g}' when -{\it number\verb+_+pad\/} +Default key is `\texttt{g}' when +\textit{number\textunderscore pad} is off, -`{\tt M-5}' when -{\it number\verb+_+pad\/} +`\texttt{M-5}' when +\textit{number\textunderscore pad} is set to 1~or~3, -otherwise `{\tt 5}' when it is set to 2~or~4. +otherwise `\texttt{5}' when it is set to 2~or~4. %.lp -\item[\tb{\#save}] +\item[\#save] Save the game and exit the program. -Default key is `{\tt S}'. +Default key is `\texttt{S}'. %.lp -\item[\tb{\#saveoptions}] +\item[\#saveoptions] Save configuration options to the config file. This will overwrite the file, removing all comments, so if you have manually edited the config file, don't use this. %.lp -\item[\tb{\#search}] -Search for traps and secret doors around you. Default key is `{\tt s}'. +\item[\#search] +Search for traps and secret doors around you. Default key is `\texttt{s}'. %.lp -\item[\tb{\#seeall}] -Show all equipment in use. Default key is `{\tt *}'. +\item[\#seeall] +Show all equipment in use. Default key is `\texttt{*}'. %.lp "" \\ Will display in-use items in a menu even when there is only one. %.lp -\item[\tb{\#seeamulet}] -Show the amulet currently worn. Default key is `{\tt "}'. +\item[\#seeamulet] +Show the amulet currently worn. Default key is `\texttt{"}'. %.lp "" \\ -Using the `{\tt m}' prefix will force the display of a worn +Using the `\texttt{m}' prefix will force the display of a worn amulet in a menu rather than with just a message. %.lp -\item[\tb{\#seearmor}] -Show the armor currently worn. Default key is `{\tt [}'. +\item[\#seearmor] +Show the armor currently worn. Default key is `\texttt{[}'. %.lp "" \\ Will display worn armor in a menu even when there is only thing worn. %.lp -\item[\tb{\#seerings}] -Show the ring(s) currently worn. Default key is `{\tt =}'. +\item[\#seerings] +Show the ring(s) currently worn. Default key is `\texttt{=}'. %.lp "" Will display worn rings in a menu if there are two (or there is just one and is a meat ring rather than a ``real'' ring). -Use the `{\tt m}' prefix to force a menu for one ring. +Use the `\texttt{m}' prefix to force a menu for one ring. %.lp -\item[\tb{\#seetools}] -Show the tools currently in use. Default key is `{\tt (}'. +\item[\#seetools] +Show the tools currently in use. Default key is `\texttt{(}'. %.lp "" Will display the result in a message if there is one tool in use (worn blindfold or towel or lenses, lit lamp(s) and/or candle(s), leashes attached to pets). Will display a menu if there are more than one or if the command is -preceded by the `{\tt m}' prefix. +preceded by the `\texttt{m}' prefix. %.lp -\item[\tb{\#seeweapon}] -Show the weapon currently wielded. Default key is `{\tt )}'. +\item[\#seeweapon] +Show the weapon currently wielded. Default key is `\texttt{)}'. %.lp "" If dual-wielding, a separate message about the secondary weapon will be given. -Using the `{\tt m}' prefix will force a menu and it will include +Using the `\texttt{m}' prefix will force a menu and it will include primary weapon, alternate weapon even when not dual-wielding, and also whatever is currently assigned to the quiver slot. %.lp -\item[\tb{\#shell}] +\item[\#shell] Do a shell escape, switching from NetHack to a subprocess. Can be disabled at the time the program is built. When enabled, access for specific users can be controlled by the system configuration file. -Use the shell command `{\tt exit}' to return to the game. -Default key is `{\tt !}'. +Use the shell command `\texttt{exit}' to return to the game. +Default key is `\texttt{!}'. %.lp -\item[\tb{\#showgold}] +\item[\#showgold] Report the gold in your inventory, including gold you know about in containers you're carrying. If you are inside a shop, report any credit or debt you have in that shop. -Default key is `{\tt \$}'. +Default key is `\texttt{\$}'. %.lp -\item[\tb{\#showspells}] +\item[\#showspells] List and reorder known spells. -Default key is `{\tt +}'. +Default key is `\texttt{+}'. %.lp -\item[\tb{\#showtrap}] +\item[\#showtrap] Describe an adjacent trap, possibly covered by objects or a monster. To be eligible, the trap must already be discovered. -(The ``{\tt \#terrain}'' command can display your map with all objects and +(The ``\texttt{\#terrain}'' command can display your map with all objects and monsters temporarily removed, making it possible to see all discovered traps.) -Default key is `{\tt \^{}}'. +Default key is `\texttt{\textasciicircum }'. %.lp -\item[\tb{\#sit}] -Sit down. Autocompletes. Default key is `{\tt M-s}'. +\item[\#sit] +Sit down. Autocompletes. Default key is `\texttt{M-s}'. %.lp -\item[\tb{\#stats}] +\item[\#stats] Show memory usage statistics. Autocompletes. Debug mode only. %.lp -\item[\tb{\#suspend}] +\item[\#suspend] Suspend the game, switching from NetHack to the terminal it was started from without performing save-and-exit. Can be disabled at the time the program is built. -When enabled, mainly useful for {\it tty\/} and {\it curses\/} interfaces on +When enabled, mainly useful for \textit{tty} and \textit{curses} interfaces on %.UX \. \" yields "UNIX." UNIX. -Use the shell command `{\tt fg}' to return to the game. -Default key is `{\tt \^{}Z}'. +Use the shell command `\texttt{fg}' to return to the game. +Default key is `\texttt{\textasciicircum Z}'. %.lp -\item[\tb{\#swap}] -Swap wielded and secondary weapons. Default key is `{\tt x}'. +\item[\#swap] +Swap wielded and secondary weapons. Default key is `\texttt{x}'. %.lp -\item[\tb{\#takeoff}] -Take off one piece of armor. Default key is `{\tt T}'. +\item[\#takeoff] +Take off one piece of armor. Default key is `\texttt{T}'. %.lp -\item[\tb{\#takeoffall}] -Remove all armor. Default key is `{\tt A}'. +\item[\#takeoffall] +Remove all armor. Default key is `\texttt{A}'. %.lp -\item[\tb{\#teleport}] -Teleport around the level. Default key is `{\tt \^{}T}'. +\item[\#teleport] +Teleport around the level. Default key is `\texttt{\textasciicircum T}'. %.lp -\item[\tb{\#terrain}] +\item[\#terrain] Show map without obstructions. In normal play you can view the explored portion of the current level's map without monsters; without monsters and objects; or without monsters, @@ -1813,9 +1793,9 @@ its explored portion. In debug mode there are additional choices.\\ %.lp "" Autocompletes. -Default key is `{\tt }' or `{\tt }' (see {\it Del\/} above). +Default key is `\texttt{}' or `\texttt{}' (see \textit{Del} above). %.lp -\item[\tb{\#therecmdmenu}] +\item[\#therecmdmenu] Show a menu of possible actions directed at a location next to you. The menu is limited to a subset of the likeliest actions, not an exhaustive set of all possibilities. @@ -1823,18 +1803,18 @@ Autocompletes. %%--invoking it by mouse seems to be broken %% \\ %% .lp "" -%% If mouse support is enabled and the {\it herecmd\verb+_+menu\/} +%% If mouse support is enabled and the \textit{herecmd\textunderscore menu} %% option is On, clicking on an adjacent location will execute this command. %.lp -\item[\tb{\#throw}] -Throw something. Default key is `{\tt t}'. +\item[\#throw] +Throw something. Default key is `\texttt{t}'. %.lp -\item[\tb{\#timeout}] +\item[\#timeout] Look at the timeout queue. Autocompletes. Debug mode only. %.lp -\item[\tb{\#tip}] +\item[\#tip] Tip over a container (bag or box) to pour out its contents. When there are containers on the floor, the game will prompt to pick one of them or ``tip something being carried''. @@ -1842,47 +1822,62 @@ of them or ``tip something being carried''. %.lp "" If the latter is chosen, there will be another prompt for which item from inventory to tip. -The `{\tt m}' prefix makes the command skip containers on the +The `\texttt{m}' prefix makes the command skip containers on the floor and pick one from inventory, except for the special case of -{\it menustyle:Traditional\/} +\textit{menustyle:Traditional} with two or more containers present; that situation will start with the floor container menu. \\ %.lp "" -Autocompletes. Default key is `{\tt M-T}'. +Autocompletes. Default key is `\texttt{M-T}'. %.lp -\item[\tb{\#travel}] +\item[\#toggle] +Toggle a boolean option on or off. +Requires a parameter in parenthesis, the name of the option to toggle. +The option must be settable in-game. + +%.lp "" +For example: +%.sd +\begin{verbatim} + BIND=':toggle(price_quotes) + BIND=@:toggle(autopickup) +\end{verbatim} +%.ed + +%.lp +\item[\#travel] Travel to a specific location on the map. -Default key is `{\tt \verb+_+}'. +Default key is `\texttt{\textunderscore }'. Using the ``request menu'' prefix shows a menu of interesting targets in sight without asking to move the cursor. -When picking a target with cursor and the {\it autodescribe\/} +When picking a target with cursor and the \textit{autodescribe} option is on, the top line will show ``(no travel path)'' if your character does not know of a path to that location. -See also {\tt \#retravel}. +See also \texttt{\#retravel}. %.lp -\item[\tb{\#turn}] -Turn undead away. Autocompletes. Default key is `{\tt M-t}'. +\item[\#turn] +Turn undead away. Autocompletes. Default key is `\texttt{M-t}'. %.lp -\item[\tb{\#twoweapon}] +\item[\#twoweapon] Toggle two-weapon combat on or off. Autocompletes. -Default key is `{\tt X}', -and also `{\tt M-2}' if {\it number\verb+_+pad\/} is off.\\ +Default key is `\texttt{X}', +and also `\texttt{M-2}' if \textit{number\textunderscore pad} is off.\\ %.lp "" Note that you must use suitable weapons for this type of combat, or it will be automatically turned off. %.lp -\item[\tb{\#untrap}] +\item[\#untrap] Untrap something (trap, door, or chest). -Default key is `{\tt M-u}', and `{\tt u}' if {\it number\verb+_+pad\/} is on.\\ +Default key is `\texttt{M-u}', and `\texttt{u}' if \textit{number\textunderscore pad} is on.\\ %.lp "" In some circumstances it can also be used to rescue trapped monsters. %.lp -\item[\tb{\#up}] -Go up a staircase. Default key is `{\tt <}'. +\item[\#up] +Go up a staircase. Default key is `\texttt{<}'. %.lp -\item[\tb{\#vanquished}] +\item[\#vanquished] List vanquished monsters by type and count. \\ %.lp "" @@ -1896,118 +1891,118 @@ on the map. Using the ``request menu'' prefix prior to \#vanquished brings up a menu of sorting orders available (provided that the vanquished monsters list contains at least two types of monsters). -Whichever ordering is picked gets assigned to the {\it sortvanquished} +Whichever ordering is picked gets assigned to the \textit{sortvanquished} option so is remembered for subsequent \#vanquished requests. -The {\tt \#genocided} command shares this sorting order. +The \texttt{\#genocided} command shares this sorting order. \\ %.lp "" During end-of-game disclosure, when asked whether to show vanquished -monsters answering `{\tt a}' will let you choose from the sort menu. +monsters answering `\texttt{a}' will let you choose from the sort menu. \\ %.lp "" Autocompletes. -Default key is `{\tt M-V}'. +Default key is `\texttt{M-V}'. %.lp -\item[\tb{\#version}] -Print compile time options for this version of {\it NetHack\/}. +\item[\#version] +Print compile time options for this version of \textit{NetHack}. %.lp The second paragraph lists the user interface(s) that are included. -If there are more than one, you can use the {\it windowtype\/} +If there are more than one, you can use the \textit{windowtype} option in your run-time configuration file to select the one you want. %.lp -Autocompletes. Default key is `{\tt M-v}'. +Autocompletes. Default key is `\texttt{M-v}'. %.lp -\item[\tb{\#versionshort}] +\item[\#versionshort] Show the program's version number, plus the date and time that the running copy was built from sources (not the version's release date). -Default key is `{\tt v}'. +Default key is `\texttt{V}'. %.lp -\item[\tb{\#vision}] +\item[\#vision] Show vision array. Autocompletes. Debug mode only. %.lp -\item[\tb{\#wait}] +\item[\#wait] Rest one move while doing nothing. -Default key is `{\tt .}', and also `{\tt{ }}' if -{\it rest\verb+_+on\verb+_+space\/} is on. +Default key is `\texttt{.}', and also `{\tt{ }}' if +\textit{rest\textunderscore on\textunderscore space} is on. %.lp -\item[\tb{\#wear}] -Wear a piece of armor. Default key is `{\tt W}'. +\item[\#wear] +Wear a piece of armor. Default key is `\texttt{W}'. %.lp -\item[\tb{\#whatdoes}] -Tell what a key does. Default key is `{\tt \&}'. +\item[\#whatdoes] +Tell what a key does. Default key is `\texttt{\&}'. %.lp -\item[\tb{\#whatis}] -Show what type of thing a symbol corresponds to. Default key is `{\tt /}'. +\item[\#whatis] +Show what type of thing a symbol corresponds to. Default key is `\texttt{/}'. %.lp -\item[\tb{\#wield}] -Wield a weapon. Default key is `{\tt w}'. +\item[\#wield] +Wield a weapon. Default key is `\texttt{w}'. %.lp -\item[\tb{\#wipe}] -Wipe off your face. Autocompletes. Default key is `{\tt M-w}'. +\item[\#wipe] +Wipe off your face. Autocompletes. Default key is `\texttt{M-w}'. %.lp -\item[\tb{\#wizborn}] +\item[\#wizborn] Show monster birth, death, genocide, and extinct statistics. Debug mode only. %.lp -\item[\tb{\#wizbury}] +\item[\#wizbury] Bury objects under and around you. Autocompletes. Debug mode only. %.lp -\item[\tb{\#wizcast}] +\item[\#wizcast] Cast any spell. Debug mode only. %.lp -\item[\tb{\#wizdetect}] +\item[\#wizdetect] Reveal hidden things (secret doors or traps or unseen monsters) within a modest radius. No time elapses. Autocompletes. Debug mode only. -Default key is `{\tt \^{}E}'. +Default key is `\texttt{\textasciicircum E}'. %.lp -\item[\tb{\#wizgenesis}] +\item[\#wizgenesis] Create a monster. May be prefixed by a count to create more than one. Autocompletes. Debug mode only. -Default key is `{\tt \^{}G}'. +Default key is `\texttt{\textasciicircum G}'. %.lp -\item[\tb{\#wizidentify}] +\item[\#wizidentify] Identify all items in inventory. Autocompletes. Debug mode only. -Default key is `{\tt \^{}I}'. +Default key is `\texttt{\textasciicircum I}'. %.lp -\item[\tb{\#wizintrinsic}] +\item[\#wizintrinsic] Set one or more intrinsic attributes. Autocompletes. Debug mode only. %.lp -\item[\tb{\#wizkill}] +\item[\#wizkill] Remove monsters from play by just pointing at them. By default the hero gets credit or blame for killing the targets. -Precede this command with the `{\tt m}' prefix to override that. +Precede this command with the `\texttt{m}' prefix to override that. Autocompletes. Debug mode only. %.lp -\item[\tb{\#wizlevelport}] +\item[\#wizlevelport] Teleport to another level. Autocompletes. Debug mode only. -Default key is `{\tt \^{}V}'. +Default key is `\texttt{\textasciicircum V}'. %.lp -\item[\tb{\#wizmap}] +\item[\#wizmap] Map the level. Autocompletes. Debug mode only. -Default key is `{\tt \^{}F}'. +Default key is `\texttt{\textasciicircum F}'. %.lp -\item[\tb{\#wizrumorcheck}] +\item[\#wizrumorcheck] Verify rumor boundaries by displaying first and last true rumors and first and last false rumors.\\ %.lp "" @@ -2017,174 +2012,175 @@ and hallucinatory monsters.\\ Autocompletes. Debug mode only. %.lp -\item[\tb{\#wizseenv}] +\item[\#wizseenv] Show map locations' seen vectors. Autocompletes. Debug mode only. %.lp -\item[\tb{\#wizsmell}] +\item[\#wizsmell] Smell monster. Autocompletes. Debug mode only. %.lp -\item[\tb{\#wizwhere}] +\item[\#wizwhere] Show locations of special levels. Autocompletes. Debug mode only. %.lp -\item[\tb{\#wizwish}] +\item[\#wizwish] Wish for something. Autocompletes. Debug mode only. -Default key is `{\tt \^{}W}'. +Default key is `\texttt{\textasciicircum W}'. +Precede this command with the `\texttt{m}' prefix to show a wish history menu. %.lp -\item[\tb{\#wmode}] +\item[\#wmode] Show wall modes. Autocompletes. Debug mode only. %.lp -\item[\tb{\#zap}] -Zap a wand. Default key is `{\tt z}'. +\item[\#zap] +Zap a wand. Default key is `\texttt{z}'. %.lp -\item[\tb{\#?}] +\item[\#?] Help menu: get the list of available extended commands. -\elist +\end{description} %.pg -\nd If your keyboard has a meta key (which, when pressed in combination +\noindent If your keyboard has a meta key (which, when pressed in combination with another key, modifies it by setting the `meta' [8th, or `high'] bit), you can invoke many extended commands by meta-ing the first letter of the command. -On {\it Windows\/} and {\it MS-DOS\/}, +On \textit{Windows} and \textit{MS-DOS}, the `Alt' key can be used in this fashion. On other systems, if typing `Alt' plus another key transmits a -two character sequence consisting of an {\tt Escape} -followed by the other key, you may set the {\it altmeta\/} -option to have {\it NetHack\/} combine them into {\tt meta+}. +two character sequence consisting of an \texttt{Escape} +followed by the other key, you may set the \textit{altmeta} +option to have \textit{NetHack} combine them into \texttt{meta+}. (This combining action only takes place when NetHack is expecting a command to execute, not when accepting input to name something or to make a wish.) %.pg -Unlike control characters, where {\tt \^{}x} and {\tt \^{}X} denote the same -thing, meta characters are case-sensitive: {\tt M-x} and {\tt M-X} +Unlike control characters, where \texttt{\textasciicircum x} and \texttt{\textasciicircum X} denote the same +thing, meta characters are case-sensitive: \texttt{M-x} and \texttt{M-X} represent different things. Some commands which can be run via a meta character require that the letter be capitalized because the lower-case equivalent is used for another command, so the three key combination -{\tt meta+Shift+letter} is needed. +\texttt{meta+Shift+letter} is needed. %.BR 1 -\blist{} +\begin{description}[font=\mdseries\ttfamily] %.lp -\item[\tb{M-?}] +\item[M-?] {\tt\#?} (not supported by all platforms) %.lp -\item[\tb{M-2}] -{\tt\#twoweapon} (unless the {\it number\verb+_+pad\/} option is enabled) +\item[M-2] +{\tt\#twoweapon} (unless the \textit{number\textunderscore pad} option is enabled) %.lp -\item[\tb{M-a}] +\item[M-a] {\tt\#adjust} %.lp -\item[\tb{M-A}] +\item[M-A] {\tt\#annotate} %.lp -\item[\tb{M-c}] +\item[M-c] {\tt\#chat} %.lp -\item[\tb{M-C}] +\item[M-C] {\tt\#conduct} %.lp -\item[\tb{M-d}] +\item[M-d] {\tt\#dip} %.lp -\item[\tb{M-e}] +\item[M-e] {\tt\#enhance} %.lp -\item[\tb{M-f}] +\item[M-f] {\tt\#force} %.lp -\item[\tb{M-g}] +\item[M-g] {\tt\#genocided} %.lp -\item[\tb{M-i}] +\item[M-i] {\tt\#invoke} %.lp -\item[\tb{M-j}] +\item[M-j] {\tt\#jump} %.lp -\item[\tb{M-l}] +\item[M-l] {\tt\#loot} %.lp -\item[\tb{M-m}] +\item[M-m] {\tt\#monster} %.lp -\item[\tb{M-n}] +\item[M-n] {\tt\#name} %.lp -\item[\tb{M-o}] +\item[M-o] {\tt\#offer} %.lp -\item[\tb{M-O}] +\item[M-O] {\tt\#overview} %.lp -\item[\tb{M-p}] +\item[M-p] {\tt\#pray} %.Ip -\item[\tb{M-r}] +\item[M-r] {\tt\#rub} %.lp -\item[\tb{M-R}] +\item[M-R] {\tt\#ride} %.lp -\item[\tb{M-s}] +\item[M-s] {\tt\#sit} %.lp -\item[\tb{M-t}] +\item[M-t] {\tt\#turn} %.lp -\item[\tb{M-T}] +\item[M-T] {\tt\#tip} %.lp -\item[\tb{M-u}] +\item[M-u] {\tt\#untrap} %.lp -\item[\tb{M-v}] +\item[M-v] {\tt\#version} %.lp -\item[\tb{M-V}] +\item[M-V] {\tt\#vanquished} %.lp -\item[\tb{M-w}] +\item[M-w] {\tt\#wipe} %.lp -\item[\tb{M-X}] +\item[M-X] {\tt\#exploremode} -\elist +\end{description} %.pg -\nd If the {\it number\verb+_+pad\/} option is on, some additional letter commands +\noindent If the \textit{number\textunderscore pad} option is on, some additional letter commands are available: -\blist{} +\begin{description}[font=\mdseries\ttfamily] %.lp -\item[\tb{h}] +\item[h] {\tt\#help} %.lp -\item[\tb{j}] +\item[j] {\tt\#jump} %.lp -\item[\tb{k}] +\item[k] {\tt\#kick} %.lp -\item[\tb{l}] +\item[l] {\tt\#loot} %.lp -\item[\tb{N}] +\item[N] {\tt\#name} %.lp -\item[\tb{u}] +\item[u] {\tt\#untrap} -\elist +\end{description} %.BR 1 \"blank line for extra separation; plain text output looks better @@ -2199,7 +2195,7 @@ Walls and corridors remain on the map as you explore them. %.pg Secret corridors are hidden and appear to be solid rock. -You can find them with the `{\tt s}' (search) command when adjacent +You can find them with the `\texttt{s}' (search) command when adjacent to them. Multiple search attempts may be needed. When searching is successful, secret corridors become ordinary open @@ -2214,16 +2210,16 @@ corridors and shows them as such. Doorways connect rooms and corridors. Some doorways have no doors; you can walk right through. Others have doors in them, which may be open, closed, or locked. -To open a closed door, use the `{\tt o}' (open) -command; to close it again, use the `{\tt c}' (close) command. +To open a closed door, use the `\texttt{o}' (open) +command; to close it again, use the `\texttt{c}' (close) command. By default the -{\it autoopen} +\textit{autoopen} option is enabled, so simply attempting to walk onto a closed door's -location will attempt to open it without needing `{\tt o}'. +location will attempt to open it without needing `\texttt{o}'. Opening via -{\it autoopen} -will not work if you are {\it confused\/} or {\it stunned\/} or suffer from -the {\it fumbling\/} attribute. +\textit{autoopen} +will not work if you are \textit{confused} or \textit{stunned} or suffer from +the \textit{fumbling} attribute. %.pg Open doors cannot be entered diagonally; you must approach them @@ -2231,20 +2227,20 @@ straight on, horizontally or vertically. Doorways without doors are not restricted in this fashion except on one particular level %.\" the rogue level -(described by ``{\tt \#overview}'' as ``a primitive area''). +(described by ``\texttt{\#overview}'' as ``a primitive area''). %.pg Unlocking magic exists but usually won't be available early on. You can get through a locked door without magic by first using an -unlocking tool with the `{\tt a}' (apply) command, and then opening it. +unlocking tool with the `\texttt{a}' (apply) command, and then opening it. By default the -{\it autounlock} -option is also enabled, so if you attempt to open (via `{\tt o}' or -{\it autoopen}) +\textit{autounlock} +option is also enabled, so if you attempt to open (via `\texttt{o}' or +\textit{autoopen}) a locked door while carrying an unlocking tool, you'll be asked whether to use it on the door's lock. Alternatively, you can break a closed door (whether locked or not) down -by kicking it via the ``{\tt \^{}D}'' (kick) command. +by kicking it via the ``\texttt{\textasciicircum D}'' (kick) command. Kicking down a door destroys it and makes a lot of noise which might wake sleeping monsters. @@ -2252,7 +2248,7 @@ wake sleeping monsters. Some closed doors are booby-trapped and will explode if an attempt is made to open (when unlocked) or unlock (when locked) or kick down. Like kicking, an explosion destroys the door and makes a lot of noise. -The ``{\tt \#untrap}'' command can be used to search a door for traps but +The ``\texttt{\#untrap}'' command can be used to search a door for traps but might take multiple attempts to find one. When one is found, you'll be asked whether to try to disarm it. If you accede, success will eliminate the trap but @@ -2270,13 +2266,13 @@ And some (giants) can smash doors. %.pg Secret doors are hidden and appear to be ordinary wall (from inside a room) or solid rock (from outside). -You can find them with the `{\tt s}' (search) command but it might +You can find them with the `\texttt{s}' (search) command but it might take multiple tries (possibly many tries if your luck is poor). Once found they are in all ways equivalent to normal doors. Mapping magic does not reveal secret doors. %.hn 2 -\subsection*{Traps (`{\tt \^{}}')} +\subsection*{Traps (`\texttt{\textasciicircum }')} %.pg There are traps throughout the dungeon to snare the unwary intruder. @@ -2284,9 +2280,9 @@ For example, you may suddenly fall into a pit and be stuck for a few turns trying to climb out (see below). A trap usually won't appear on your map until you trigger it by moving onto it, you see someone else trigger it, or you discover it with -the `{\tt s}' (search) command (multiple attempts are often needed; +the `\texttt{s}' (search) command (multiple attempts are often needed; if your luck is poor, many attempts might be needed). -{\it Wands of secret door detection\/} and the spell of {\it detect unseen} +\textit{Wands of secret door detection} and the spell of \textit{detect unseen} also reveal traps within a modest radius but only if the trap is also within line-of-sight (whether you can see at the time or not). There is also other magic which can reveal traps. @@ -2323,7 +2319,7 @@ necessarily to a specific location there. %.pg There is a special multi-level branch of the dungeon with pre-mapped levels -based on the classic computer game ``{\it Sokoban}.'' +based on the classic computer game ``\textit{Sokoban}.'' In that game, you operate as a warehouse worker who pushes crates around obstacles to position them at designated locations. In NetHack, the goal is to push boulders into pits or holes until those @@ -2340,24 +2336,24 @@ With careful foresight, it is possible to complete all of the levels according to the traditional rules of Sokoban. (Hint: to solve Sokoban puzzles, you often need to move things away from their eventual destinations in order to open up more room to maneuver.) -Since NetHack does not support an {\it undo\/} capability, some allowances +Since NetHack does not support an \textit{undo} capability, some allowances are permitted in case you get stuck. For example, each level has at least one extra boulder. Also, it is possible to drop everything in order to be able to squeeze into the same location as a boulder (and then presumably move past it), or to destroy a boulder with magic or tools, or to create new boulders -with a {\it scroll of earth}. +with a \textit{scroll of earth}. However, doing such things will lower your luck without any specific message given about that. -See the {\it Conduct\/} section for information about getting feedback for +See the \textit{Conduct} section for information about getting feedback for your actions in Sokoban. %.hn 2 -\subsection*{Stairs and ladders (`{\tt <}', `{\tt >}')} +\subsection*{Stairs and ladders (`\texttt{<}', `\texttt{>}')} %.pg In general, each level in the dungeon will have a staircase going up -(`{\tt <}') to the previous level and another going down (`{\tt >}') +(`\texttt{<}') to the previous level and another going down (`\texttt{>}') to the next level. There are some exceptions though. For instance, fairly early in the dungeon you will find a level with two down staircases, one @@ -2396,8 +2392,8 @@ inter-level connections are nearly indistinguishable during game play. %.pg Occasionally you will run across a room with a shopkeeper near the door and many items lying on the floor. You can buy items by picking them -up and then using the `{\tt p}' command. You can inquire about the price -of an item prior to picking it up by using the ``{\tt \#chat}'' command +up and then using the `\texttt{p}' command. You can inquire about the price +of an item prior to picking it up by using the ``\texttt{\#chat}'' command while standing on it. Using an item prior to paying for it will incur a charge, and the shopkeeper won't allow you to leave the shop until you have paid any debt you owe. @@ -2425,11 +2421,11 @@ find a ``credit card'' in the dungeon, don't bother trying to use it in shops; shopkeepers will not accept it.) %.pg -The {\tt \$} command, which reports the amount of gold you are carrying, +The \texttt{\$} command, which reports the amount of gold you are carrying, will also show current shop debt or credit, if any. -The {\tt Iu} command lists unpaid items (those which still belong to the +The \texttt{Iu} command lists unpaid items (those which still belong to the shop) if you are carrying any. -The {\tt Ix} command shows an inventory-like display of any unpaid items +The \texttt{Ix} command shows an inventory-like display of any unpaid items which have been used up, along with other shop fees, if any. %.hn 3 @@ -2441,21 +2437,21 @@ Several aspects of shop behavior might be unexpected. \begin{itemize} % note: a bullet is the default item label so we could omit [$\bullet$] here %.lp \(bu 2 -\item[$\bullet$] +\item The price of a given item can vary due to a variety of factors. %.lp \(bu 2 -\item[$\bullet$] +\item A shopkeeper treats the spot immediately inside the door as if it were outside the shop. %.lp \(bu 2 -\item[$\bullet$] +\item While the shopkeeper watches you like a hawk, he or she will generally ignore any other customers. %.lp \(bu 2 -\item[$\bullet$] +\item If a shop is ``closed for inventory,'' it will not open of its own accord. %.lp \(bu 2 -\item[$\bullet$] +\item Shops do not get restocked with new items, regardless of inventory depletion. \end{itemize} @@ -2471,29 +2467,29 @@ There are several options which can be used to augment the normal feedback. %.pg The -{\it pile\verb+_+limit\/} +\textit{pile\textunderscore limit} option controls how many objects can be in a pile---sharing the same map location---for the game to state ``there are objects here'' instead of listing them. -The default is {\tt 5}. -Setting it to {\tt 1} would always give that message instead of listing +The default is \texttt{5}. +Setting it to \texttt{1} would always give that message instead of listing any objects. -Setting it to {\tt 0} is a special case which will always list all +Setting it to \texttt{0} is a special case which will always list all objects no matter how big a pile is. Note that the number refers to the count of separate stacks of objects present rather than the sum of the quantities of those stacks (so -{\tt 7 arrows} or {\tt 25 gold pieces} will each count as 1 rather +\texttt{7 arrows} or \texttt{25 gold pieces} will each count as 1 rather than as 7 and 25, respectively, and total to 2 when both are at the same location). %.pg -The {\tt nopickup} command prefix (default `{\tt m}') can be +The \texttt{nopickup} command prefix (default `\texttt{m}') can be used before a movement direction to step on objects without attempting auto-pickup and without giving feedback about them. %.pg The -{\it mention\verb+_+walls\/} +\textit{mention\textunderscore walls} option controls whether you get feedback if you try to walk into a wall or solid stone or off the edge of the map. Normally nothing happens (unless the hero is blind and no wall is shown, @@ -2503,7 +2499,7 @@ some non-obvious reason. %.pg The -{\it mention\verb+_+decor\/} +\textit{mention\textunderscore decor} option controls whether you get feedback when walking on ``furniture.'' Normally stepping onto stairs or a fountain or an altar or various other things doesn't elicit anything unless it is covered by one or more objects @@ -2522,27 +2518,27 @@ case the back on land circumstance is implied. %.pg The -{\it confirm\/} +\textit{confirm} and -{\it safe\verb+_+pet\/} +\textit{safe\textunderscore pet} options control what happens when you try to move onto a peaceful monster's spot or a tame one's spot. %.\" getting away from "Movement feedback" here; oh well... %.pg -The {\tt nopickup} command prefix (default `{\tt m}') is +The \texttt{nopickup} command prefix (default `\texttt{m}') is also the move-without-attacking prefix and can be used to try to step onto a visible monster's spot without the move being considered an attack -(see the {\it Fighting\/} subsection of {\it Monsters\/} below). -The `{\tt fight}' command prefix (default `{\tt F}'; -also `{\tt -}' if -{\it number\verb+_+pad\/} +(see the \textit{Fighting} subsection of \textit{Monsters} below). +The `\texttt{fight}' command prefix (default `\texttt{F}'; +also `\texttt{-}' if +\textit{number\textunderscore pad} is on) can be used to force an attack, when guessing where an unseen monster is or when deliberately attacking a peaceful or tame creature. %.pg The -{\it run\verb+_+mode} +\textit{run\textunderscore mode} option controls how frequently the map gets redrawn when moving more than one step in a single command (so when rushing, running, or traveling). @@ -2551,13 +2547,13 @@ than one step in a single command (so when rushing, running, or traveling). %.pg One dungeon level (occurring in mid to late teens of the main dungeon) -is a tribute to the ancestor game {\it hack}'s inspiration {\it rogue}. +is a tribute to the ancestor game \textit{hack}'s inspiration \textit{rogue}. %.pg It is usually displayed differently from other levels: possibly in characters instead of tiles, or without line-drawing symbols if already -in characters; also, gold is shown as {\tt *} rather than {\tt \verb+$+} -and stairs are shown as {\tt \verb+%+} rather than {\tt <} and {\tt >}. +in characters; also, gold is shown as \texttt{*} rather than \texttt{\textdollar} +and stairs are shown as \texttt{\%} rather than \texttt{<} and \texttt{>}. There are some minor differences in actual game play: doorways lack doors; a scroll, wand, or spell of light used in a room lights up the whole room rather than within a radius around your character. @@ -2577,16 +2573,16 @@ help you locate them before they locate you (which some monsters can do very well). %.pg -The commands `{\tt /}' and `{\tt ;}' may be used to obtain information +The commands `\texttt{/}' and `\texttt{;}' may be used to obtain information about those -monsters who are displayed on the screen. The command ``{\tt \#name}'' -(by default bound to `{\tt C}'), allows you +monsters who are displayed on the screen. The command ``\texttt{\#name}'' +(by default bound to `\texttt{C}'), allows you to assign a name to a monster, which may be useful to help distinguish one from another when multiple monsters are present. Assigning a name which is just a space will remove any prior name. %.pg -The extended command ``{\tt \#chat}'' can be used to interact with an adjacent +The extended command ``\texttt{\#chat}'' can be used to interact with an adjacent monster. There is no actual dialog (in other words, you don't get to choose what you'll say), but chatting with some monsters such as a shopkeeper or the Oracle of Delphi can produce useful results. @@ -2603,27 +2599,27 @@ Remember: discretion is the better part of valor. %.pg In most circumstances, if you attempt to attack a peaceful monster by moving into its location, you'll be asked to confirm your intent. By -default an answer of `{\tt y}' acknowledges that intent, -which can be error prone if you're using `{\tt y}' to move. You can set the -{\it paranoid\verb+_+confirmation:attack\/} -option to require a response of ``{\tt yes}'' instead. +default an answer of `\texttt{y}' acknowledges that intent, +which can be error prone if you're using `\texttt{y}' to move. You can set the +\textit{paranoid\textunderscore confirmation:attack} +option to require a response of ``\texttt{yes}'' instead. %.pg If you can't see a monster (if it is invisible, or if you are blinded), -the symbol `{\tt I}' will be shown when you learn of its presence. +the symbol `\texttt{I}' will be shown when you learn of its presence. If you attempt to walk into it, you will try to fight it just like a monster that you can see; of course, if the monster has moved, you will attack empty air. If you guess that the monster has moved and you don't wish to fight, you can use the -`{\tt m}' command to move without fighting; likewise, if you don't remember -a monster but want to try fighting anyway, you can use the `{\tt F}' command. +`\texttt{m}' command to move without fighting; likewise, if you don't remember +a monster but want to try fighting anyway, you can use the `\texttt{F}' command. %.hn 2 \subsection*{Your pet} %.pg -You start the game with a little dog (`{\tt d}'), kitten (`{\tt f}'), -or pony (`{\tt u}'), which follows +You start the game with a little dog (`\texttt{d}'), kitten (`\texttt{f}'), +or pony (`\texttt{u}'), which follows you about the dungeon and fights monsters with you. Like you, your pet needs food to survive. Dogs and cats usually feed themselves on fresh carrion and other meats; @@ -2656,22 +2652,22 @@ have the right equipment and skill. Convincing a wild beast to let you saddle it up is difficult to say the least. Many a dungeoneer has had to resort to magic and wizardry in order to forge the alliance. Once you do have the beast under your control however, you can -easily climb in and out of the saddle with the ``{\tt \#ride}'' command. Lead +easily climb in and out of the saddle with the ``\texttt{\#ride}'' command. Lead the beast around the dungeon when riding, in the same manner as you would move yourself. It is the beast that you will see displayed on the map. %.pg -Riding skill is managed by the ``{\tt \#enhance}'' command. See the section +Riding skill is managed by the ``\texttt{\#enhance}'' command. See the section on Weapon proficiency for more information about that. %.pg -Use the `{\tt a}' (apply) command and pick a saddle in your inventory to +Use the `\texttt{a}' (apply) command and pick a saddle in your inventory to attempt to put that saddle on an adjacent creature. If successful, it will be transferred to that creature's inventory. %.pg -Use the ``{\tt \#loot}'' command while adjacent to a saddled creature to +Use the ``\texttt{\#loot}'' command while adjacent to a saddled creature to try to remove the saddle from that creature. If successful, it will be transferred to your inventory. @@ -2712,11 +2708,11 @@ location ordinarily wouldn't be seen any more. %.pg When you find something in the dungeon, it is common to want to pick -it up. In {\it NetHack}, this is accomplished by using the `{\tt ,}' command. -If {\it autopickup\/} option is on, you will automatically pick up the object -by walking over it, unless you move with the `{\tt m}' prefix. +it up. In \textit{NetHack}, this is accomplished by using the `\texttt{,}' command. +If \textit{autopickup} option is on, you will automatically pick up the object +by walking over it, unless you move with the `\texttt{m}' prefix. %.pg -If you're carrying too many items, {\it NetHack\/} will tell you so and you +If you're carrying too many items, \textit{NetHack} will tell you so and you won't be able to pick up anything more. Otherwise, it will add the object(s) to your pack and tell you what you just picked up. %.pg @@ -2731,16 +2727,16 @@ will get slower and you'll burn calories faster, requiring food more frequently to cope with it. Eventually, you'll be so overloaded that you'll either have to discard some of what you're carrying or collapse under its weight. %.pg -{\it NetHack\/} will tell you how badly you have loaded yourself. +\textit{NetHack} will tell you how badly you have loaded yourself. If you are encumbered, one of the conditions -{\tt Burdened}, {\tt Stressed}, {\tt Strained}, -{\tt Overtaxed}, or {\tt Overloaded} will be +\texttt{Burdened}, \texttt{Stressed}, \texttt{Strained}, +\texttt{Overtaxed}, or \texttt{Overloaded} will be shown on the bottom line status display. %.pg When you pick up an object, it is assigned an inventory letter. Many commands that operate on objects must ask you to find out which object -you want to use. When {\it NetHack\/} asks you to choose a particular object +you want to use. When \textit{NetHack} asks you to choose a particular object you are carrying, you are usually presented with a list of inventory letters to choose from (see Commands, above). @@ -2751,13 +2747,13 @@ type. During a game, any two objects with the same description are the same type. However, the descriptions will vary from game to game. %.pg -When you use one of these objects, if its effect is obvious, {\it NetHack\/} +When you use one of these objects, if its effect is obvious, \textit{NetHack} will remember what it is for you. If its effect isn't extremely obvious, you will be asked what you want to call this type of object -so you will recognize it later. You can also use the ``{\tt \#name}'' +so you will recognize it later. You can also use the ``\texttt{\#name}'' command, for the same purpose at any time, to name all objects of a particular type or just an individual object. -When you use ``{\tt \#name}'' on an object which has already been named, +When you use ``\texttt{\#name}'' on an object which has already been named, specifying a space as the value will remove the prior name instead of assigning a new one. @@ -2799,23 +2795,81 @@ provided that you can see them land. %.pg An item with unknown status will be reported in your inventory with no prefix. An item which you know the state of will be distinguished in your inventory -by the presence of the word {\tt cursed}, {\tt uncursed} or -{\tt blessed} in the description of the item. -In some cases {\tt uncursed} will be omitted as being redundant when +by the presence of the word \texttt{cursed}, \texttt{uncursed} or +\texttt{blessed} in the description of the item. +In some cases \texttt{uncursed} will be omitted as being redundant when enough other information is displayed. The -{\it implicit\verb+_+uncursed\/} -option can be used to control this; toggle it off to have {\tt uncursed} +\textit{implicit\textunderscore uncursed} +option can be used to control this; toggle it off to have \texttt{uncursed} be displayed even when that can be deduced from other attributes. %.pg Sometimes the bless or curse state of objects is referred to as their -``{\tt BUC}'' attribute, for Blessed, Uncursed, or Cursed state, -or ``{\tt BUCX}'' for Blessed, Uncursed, Cursed, or unknown. -(The term {\it beatitude\/} is occasionally used as well.) +``\texttt{BUC}'' attribute, for Blessed, Uncursed, or Cursed state, +or ``\texttt{BUCX}'' for Blessed, Uncursed, Cursed, or unknown. +(The term \textit{beatitude} is occasionally used as well.) %.hn 2 -\subsection*{Weapons (`{\tt )}')} +\subsection*{Artifacts} + +%.pg +Some objects have been imbued with special powers and are known as +\textit{Artifacts}. +They have specific types (such as long sword or orcish dagger) and distinct +names such as \textit{Giantslayer} or \textit{Grimtooth}. +Artifact weapons typically do more damage than their ordinary counterparts. +Some do extra damage against all monsters, others only against specific +types of monsters so aren't better than regular weapons against other types. +Some confer defensive capabilities when wielded or have other powers that +aren't listed here. + +%.pg +You might find them simply lying on the floor, including but not limited +to inside shops, or be granted as a reward for ``\texttt{\#offer}'' on an +altar to your patron deity. +A few might be dropped by monsters, or might be converted from an ordinary +object of the same type via assigning the right name. +% should we mention dipping for Excalibur here? +Or you might wish for them, if you happen to be granted a wish, but such +wishes can fail. + +%.pg +Some artifacts have a specific alignment, others don't. +You won't obtain aligned ones that have a different alignment from yours +via offering and might get a shock if you attempt to wish for any of those +or find one and attempt to use it. + +%pg +Each role has a distinct artifact that is contained in the \textit{Quest} +dungeon branch. +These are commonly known as quest artifacts. +All are aligned and most are non-weapons. +They won't be found randomly. + +%.pg +The `\texttt{\textbackslash}' and `\texttt{\textasciigrave{}a}' commands will +list artifacts that you have fully identified (knowing the name and item +type isn't sufficient). + +%.hn 2 +\subsection*{Relics} + +%.pg +There are three unique items that are named and have limited special +powers but aren't classified as artifacts. +Each is guarded by a particular monster and you'll need to collect all +three for use late in the game. +% The relics are listed in the same order as the Oracle's message about +% them rather than in the order they need to be used for the invocation. +They are \textit{the Bell of Opening}, +\textit{the Book of the Dead}, and +\textit{the Candelabrum of Invocation}. +Their corresponding descriptions when not yet identified are +silver bell, papyrus spellbook, and candelabrum. + +%.hn 2 +\subsection*{Weapons (`\texttt{)}')} %.pg Given a chance, most monsters in the Mazes of Menace will gratuitously try to @@ -2856,11 +2910,11 @@ vulnerable to certain types of weapons. Many weapons can be wielded in one hand; some require both hands. When wielding a two-handed weapon, you can not wear a shield, and vice versa. When wielding a one-handed weapon, you can have another -weapon ready to use by setting things up with the `{\tt x}' command, which +weapon ready to use by setting things up with the `\texttt{x}' command, which exchanges your primary (the one being wielded) and alternate weapons. And if you have proficiency in the ``two weapon combat'' skill, you may wield both weapons simultaneously as primary and secondary; use the -`{\tt X}' command to engage or disengage that. +`\texttt{X}' command to engage or disengage that. Only some types of characters (barbarians, for instance) have the necessary skill available. Even with that skill, using two weapons at once incurs a penalty in the chance to hit your target compared to using just one @@ -2868,30 +2922,30 @@ weapon at a time. %.pg There might be times when you'd rather not wield any weapon at all. -To accomplish that, wield `{\tt -}', or else use the `{\tt A}' command which +To accomplish that, wield `\texttt{-}', or else use the `\texttt{A}' command which allows you to unwield the current weapon in addition to taking off other worn items. %.pg Those of you in the audience who are AD\&D players, be aware that each weapon which existed in AD\&D does roughly the same damage to monsters in -{\it NetHack}. Some of the more obscure weapons (such as the -{\it aklys}, {\it lucern hammer}, and {\it bec-de-corbin\/}) are defined -in an appendix to {\it Unearthed Arcana}, an AD\&D supplement. +\textit{NetHack}. Some of the more obscure weapons (such as the +\textit{aklys}, \textit{lucern hammer}, and \textit{bec-de-corbin}) are defined +in an appendix to \textit{Unearthed Arcana}, an AD\&D supplement. %.pg -The commands to use weapons are `{\tt w}' (wield), `{\tt t}' (throw), -`{\tt f}' (fire), `{\tt Q}' (quiver), -`{\tt x}' (exchange), `{\tt X}' (twoweapon), and ``{\tt \#enhance}'' +The commands to use weapons are `\texttt{w}' (wield), `\texttt{t}' (throw), +`\texttt{f}' (fire), `\texttt{Q}' (quiver), +`\texttt{x}' (exchange), `\texttt{X}' (twoweapon), and ``\texttt{\#enhance}'' (see below). %.hn 3 \subsection*{Throwing and shooting} %.pg -You can throw just about anything via the `{\tt t}' command. It will prompt -for the item to throw; picking `{\tt ?}' will list things in your inventory -which are considered likely to be thrown, or picking `{\tt *}' will list +You can throw just about anything via the `\texttt{t}' command. It will prompt +for the item to throw; picking `\texttt{?}' will list things in your inventory +which are considered likely to be thrown, or picking `\texttt{*}' will list your entire inventory. After you've chosen what to throw, you will be prompted for a direction rather than for a specific target. The distance something can be thrown depends mainly on the type of object @@ -2904,26 +2958,26 @@ Some weapons will return when thrown. A boomerang---provided it fails to hit anything---is an obvious example. If an aklys (thonged club) is thrown while it is wielded, it will return even when it hits something. -A sufficiently strong hero can throw the warhammer {\it Mjollnir\/}; -when thrown by a {\it Valkyrie\/} it will return too. -However, aklyses and {\it Mjollnir\/} occasionally fail to return. +A sufficiently strong hero can throw the warhammer \textit{Mjollnir}; +when thrown by a \textit{Valkyrie} it will return too. +However, aklyses and \textit{Mjollnir} occasionally fail to return. Returning thrown objects occasionally fail to be caught, sometimes even hitting the thrower, but when caught they become re-wielded. %.pg -You can simplify the throwing operation by using the `{\tt Q}' command to -select your preferred ``missile'', then using the `{\tt f}' command to +You can simplify the throwing operation by using the `\texttt{Q}' command to +select your preferred ``missile'', then using the `\texttt{f}' command to throw it. You'll be prompted for a direction as above, but you don't -have to specify which item to throw each time you use `{\tt f}'. There is +have to specify which item to throw each time you use `\texttt{f}'. There is also an option, -{\it autoquiver}, -which has {\it NetHack\/} choose another item to automatically fill your +\textit{autoquiver}, +which has \textit{NetHack} choose another item to automatically fill your quiver (or quiver sack, or have at the ready) when the inventory slot used -for `{\tt Q}' runs out. -If your quiver is empty, {\it autoquiver\/} +for `\texttt{Q}' runs out. +If your quiver is empty, \textit{autoquiver} is false, and you are wielding a weapon which returns when thrown, you will throw that weapon instead of filling the quiver. -The fire command also has extra assistance, if {\it fireassist\/} +The fire command also has extra assistance, if \textit{fireassist} is on it will try to wield a launcher matching the ammo in the quiver. %.pg @@ -2939,12 +2993,12 @@ shoot arrows, in crossbow skill if you're wielding one to shoot bolts, or in sling skill if you're wielding one to shoot stones). The number of items that the character has a chance to fire varies from turn to turn. You can explicitly limit the number of shots by using a -numeric prefix before the `{\tt t}' or `{\tt f}' command. -For example, ``{\tt 2f}'' (or ``{\tt n2f}'' if using -{\it number\verb+_+pad\/} +numeric prefix before the `\texttt{t}' or `\texttt{f}' command. +For example, ``\texttt{2f}'' (or ``\texttt{n2f}'' if using +\textit{number\textunderscore pad} mode) would ensure that at most 2 arrows are shot even if you could have fired 3. If you specify -a larger number than would have been shot (``{\tt 4f}'' in this example), +a larger number than would have been shot (``\texttt{4f}'' in this example), you'll just end up shooting the same number (3, here) as if no limit had been specified. Once the volley is in motion, all of the items will travel in the same direction; if the first ones kill a monster, @@ -2968,14 +3022,14 @@ can achieve for each group. For instance, wizards can become highly skilled in daggers or staves but not in swords or bows. %.pg -The ``{\tt \#enhance}'' extended command is used to review current weapons +The ``\texttt{\#enhance}'' extended command is used to review current weapons proficiency (also spell proficiency) and to choose which skill(s) to improve when you've used one or more skills enough to become eligible to do so. The skill rankings are ``none'' (sometimes also referred to as ``restricted'', because you won't be able to advance), ``unskilled'', ``basic'', ``skilled'', and ``expert''. Restricted skills simply will not appear in the list -shown by ``{\tt \#enhance}''. +shown by ``\texttt{\#enhance}''. (Divine intervention might unrestrict a particular skill, in which case it will start at unskilled and be limited to basic.) Some characters can enhance their barehanded combat or martial arts skill @@ -2991,7 +3045,7 @@ higher. A successful hit has a chance to boost your training towards the next skill level (unless you've already reached the limit for this skill). Once such training reaches the threshold for that next level, you'll be told that you feel more confident in your skills. At that -point you can use ``{\tt \#enhance}'' to increase one or more skills. +point you can use ``\texttt{\#enhance}'' to increase one or more skills. Such skills are not increased automatically because there is a limit to your total overall skills, so you need to actively choose which skills to enhance @@ -3003,7 +3057,7 @@ and which to ignore. %.pg Some characters can use two weapons at once. Setting things up to do so can seem cumbersome but becomes second nature with use. -To wield two weapons, you need to use the ``{\tt \#twoweapon}'' command. +To wield two weapons, you need to use the ``\texttt{\#twoweapon}'' command. But first you need to have a weapon in each hand. (Note that your two weapons are not fully equal; the one in the hand you normally wield with is considered primary and the other @@ -3017,35 +3071,35 @@ as alternate weapon.) %.pg If your primary weapon is wielded but your off hand is empty or has -the wrong weapon, use the sequence `{\tt x}', `{\tt w}', `{\tt x}' to +the wrong weapon, use the sequence `\texttt{x}', `\texttt{w}', `\texttt{x}' to first swap your primary into your off hand, wield whatever you want as secondary weapon, then swap them both back into the intended hands. If your secondary or alternate weapon is correct but your primary -one is not, simply use `{\tt w}' to wield the primary. +one is not, simply use `\texttt{w}' to wield the primary. Lastly, if neither hand holds the correct weapon, -use `{\tt w}', `{\tt x}', `{\tt w}' +use `\texttt{w}', `\texttt{x}', `\texttt{w}' to first wield the intended secondary, swap it to off hand, and then wield the primary. %.pg The whole process can be simplified via use of the -{\it pushweapon\/} -option. When it is enabled, then using `{\tt w}' to wield something +\textit{pushweapon} +option. When it is enabled, then using `\texttt{w}' to wield something causes the currently wielded weapon to become your alternate weapon. -So the sequence `{\tt w}', `{\tt w}' can be used to first wield the weapon you +So the sequence `\texttt{w}', `\texttt{w}' can be used to first wield the weapon you intend to be secondary, and then wield the one you want as primary which will push the first into secondary position. %.pg -When in two-weapon combat mode, using the `{\tt X}' command +When in two-weapon combat mode, using the `\texttt{X}' command toggles back to single-weapon mode. Throwing or dropping either of the weapons or having one of them be stolen or destroyed will also make you revert to single-weapon combat. %.hn 2 -\subsection*{Armor (`{\tt [}')} +\subsection*{Armor (`\texttt{[}')} %.pg Lots of unfriendly things lurk about; you need armor to protect @@ -3054,7 +3108,7 @@ protection than others. Your armor class is a measure of this protection. Armor class (AC) is measured as in AD\&D, with 10 being the equivalent of no armor, and lower numbers meaning better armor. Each suit of armor which exists in AD\&D gives the same protection in -{\it NetHack}. +\textit{NetHack}. Here is a list of the armor class values provided by suits of armor: @@ -3073,7 +3127,7 @@ leather jacket & 9 & & no armor & 10\\ \end{center} %.pg -\nd You can also wear other pieces of armor (cloak over suit, shirt under +\noindent You can also wear other pieces of armor (cloak over suit, shirt under suit, helmet, gloves, boots, shield) to lower your armor class even further. %--too obvious to mention unless we include polymorph into ettin or maralith @@ -3085,7 +3139,7 @@ enchanted. Shirts are an exception; they don't provide any protection unless enchanted. Some cloaks also don't improve AC when unenchanted but all cloaks offer some protection against rust or corrosion to suits worn under them and -against some monster {\it touch\/} attacks. +against some monster \textit{touch} attacks. %.pg If a piece of armor is enchanted, its armor protection will be better @@ -3102,21 +3156,21 @@ Many types of armor are subject to some kind of damage like rust. Such damage can be repaired. Some types of armor may inhibit spell casting. %.pg -The {\it nudist\/} +The \textit{nudist} option can be set (prior to game start) to attempt to play the entire game without wearing any armor (a self-imposed challenge which is extremely difficult to accomplish). %.pg -The commands to use armor are `{\tt W}' (wear) and `{\tt T}' (take off). -The `{\tt A}' command can be used to take off armor as well as other +The commands to use armor are `\texttt{W}' (wear) and `\texttt{T}' (take off). +The `\texttt{A}' command can be used to take off armor as well as other worn items. -Also, `{\tt P}' (put on) and `{\tt R}' (remove) which are normally for +Also, `\texttt{P}' (put on) and `\texttt{R}' (remove) which are normally for accessories can be used for armor, but pieces of armor won't be shown as likely candidates in a prompt for choosing what to put on or remove. %.hn 2 -\subsection*{Food (`{\tt \%}')} +\subsection*{Food (`\texttt{\%}')} %.pg Food is necessary to survive. If you go too long without eating you @@ -3140,13 +3194,13 @@ but with some rather unpleasant side-effects. %.pg You can name one food item after something you like to eat with the -{\it fruit\/} option. +\textit{fruit} option. %.pg -The command to eat food is `{\tt e}'. +The command to eat food is `\texttt{e}'. %.hn 2 -\subsection*{Scrolls (`{\tt ?}')} +\subsection*{Scrolls (`\texttt{?}')} %.pg Scrolls are labeled with various titles, probably chosen by ancient wizards @@ -3156,7 +3210,7 @@ magic spells on them). %.pg One of the most useful of these is the % -{\it scroll of identify}, which +\textit{scroll of identify}, which can be used to determine what another object is, whether it is cursed or blessed, and how many uses it has left. Some objects of subtle enchantment are difficult to identify without these. @@ -3164,7 +3218,7 @@ Some objects of subtle enchantment are difficult to identify without these. %.pg A scroll whose label is known can be read even when the hero is blind. If a scroll has been discovered, it will be listed in inventory by type -rather than by label, but the label is known in that situtaion even though +rather than by label, but the label is known in that situation even though it isn't shown. %.pg @@ -3173,24 +3227,24 @@ cursed, or read while the hero is confused. %.pg A mail daemon may run up and deliver mail to you as a % -{\it scroll of mail} (on versions compiled with this feature). -To use this feature on versions where {\it NetHack\/} +\textit{scroll of mail} (on versions compiled with this feature). +To use this feature on versions where \textit{NetHack} mail delivery is triggered by electronic mail appearing in your system mailbox, -you must let {\it NetHack\/} know where to look for new mail by setting the -{\tt MAIL} environment variable to the file name of your mailbox. -You may also want to set the {\tt MAILREADER} environment variable to the -file name of your favorite reader, so {\it NetHack\/} can shell to it when you +you must let \textit{NetHack} know where to look for new mail by setting the +\texttt{MAIL} environment variable to the file name of your mailbox. +You may also want to set the \texttt{MAILREADER} environment variable to the +file name of your favorite reader, so \textit{NetHack} can shell to it when you read the scroll. -On versions of {\it NetHack\/} where mail is randomly +On versions of \textit{NetHack} where mail is randomly generated internal to the game, these environment variables are ignored. You can disable the mail daemon by turning off the -{\it mail\/} option. +\textit{mail} option. %.pg -The command to read a scroll is `{\tt r}'. +The command to read a scroll is `\texttt{r}'. %.hn 2 -\subsection*{Potions (`{\tt !}')} +\subsection*{Potions (`\texttt{!}')} %.pg Potions are distinguished by the color of the liquid inside the flask. @@ -3200,20 +3254,20 @@ They disappear after you quaff them. Clear potions are potions of water. Sometimes these are blessed or cursed, resulting in holy or unholy water. Holy water is the bane of the undead, so potions of holy water are good things to -throw (`{\tt t}') at them. It is also sometimes very useful to dip -(``{\tt \#dip}'') an object into a potion. +throw (`\texttt{t}') at them. It is also sometimes very useful to dip +(``\texttt{\#dip}'') an object into a potion. %.pg -The command to drink a potion is `{\tt q}' (quaff). +The command to drink a potion is `\texttt{q}' (quaff). %.hn 2 -\subsection*{Wands (`{\tt /}')} +\subsection*{Wands (`\texttt{/}')} %.pg Wands usually have multiple magical charges. Some types of wands require a direction in which to zap them. You can also -zap them at yourself (just give a `{\tt .}' or `{\tt s}' for the direction). +zap them at yourself (just give a `\texttt{.}' or `\texttt{s}' for the direction). Be warned, however, for this is often unwise. Other types of wands don't require a direction. The number of charges in a @@ -3238,15 +3292,15 @@ magical energies. When you have fully identified a particular wand, inventory display will include additional information in parentheses: the number of times it has been recharged followed by a colon and then by its current number of charges. -A current charge count of {\tt -1} is a special case indicating that the wand +A current charge count of \texttt{-1} is a special case indicating that the wand has been cancelled. %.pg -The command to use a wand is `{\tt z}' (zap). To break one, use the `{\tt a}' +The command to use a wand is `\texttt{z}' (zap). To break one, use the `\texttt{a}' (apply) command. %.hn 2 -\subsection*{Rings (`{\tt =}')} +\subsection*{Rings (`\texttt{=}')} %.pg Rings are very useful items, since they are relatively permanent @@ -3271,14 +3325,14 @@ off before putting on or removing a ring and then re-wear them after. That's done implicitly to avoid unnecessary tedium. %.pg -The commands to use rings are `{\tt P}' (put on) and `{\tt R}' (remove). -`{\tt A}', `{\tt W}', and `{\tt T}' can also be used; see {\it Amulets\/}. +The commands to use rings are `\texttt{P}' (put on) and `\texttt{R}' (remove). +`\texttt{A}', `\texttt{W}', and `\texttt{T}' can also be used; see \textit{Amulets}. %.hn 2 -\subsection*{Spellbooks (`{\tt +}')} +\subsection*{Spellbooks (`\texttt{+}')} %.pg -Spellbooks are tomes of mighty magic. When studied with the `{\tt r}' (read) +Spellbooks are tomes of mighty magic. When studied with the `\texttt{r}' (read) command, they transfer to the reader the knowledge of a spell (and therefore eventually become unreadable)---unless the attempt backfires. @@ -3304,7 +3358,7 @@ your memory of each spell will dim, and you will need to relearn it. %.pg Some spells require a direction in which to cast them, similar to wands. -To cast one at yourself, just give a `{\tt .}' or `{\tt s}' for the direction. +To cast one at yourself, just give a `\texttt{.}' or `\texttt{s}' for the direction. A few spells require you to pick a target location rather than just specify a particular direction. Other spells don't require any direction or target. @@ -3313,7 +3367,7 @@ Other spells don't require any direction or target. Just as weapons are divided into groups in which a character can become proficient (to varying degrees), spells are similarly grouped. Successfully casting a spell exercises its skill group; using the -``{\tt \#enhance}'' command to advance a sufficiently exercised skill +``\texttt{\#enhance}'' command to advance a sufficiently exercised skill will affect all spells within the group. Advanced skill may increase the potency of spells, reduce their risk of failure during casting attempts, and improve the accuracy of the estimate for how much longer they will @@ -3326,14 +3380,14 @@ Casting a spell also requires flexible movement, and wearing various types of armor may interfere with that. %.pg -The command to read a spellbook is the same as for scrolls, `{\tt r}' (read). -The `{\tt +}' command lists each spell you know along with its level, skill +The command to read a spellbook is the same as for scrolls, `\texttt{r}' (read). +The `\texttt{+}' command lists each spell you know along with its level, skill category, chance of failure when casting, and an estimate of how strongly it is remembered. -The `{\tt Z}' (cast) command casts a spell. +The `\texttt{Z}' (cast) command casts a spell. %.hn 2 -\subsection*{Tools (`{\tt (}')} +\subsection*{Tools (`\texttt{(}')} %.pg Tools are miscellaneous objects with various purposes. Some tools @@ -3342,8 +3396,8 @@ out after a while. Other tools are containers, which objects can be placed into or taken out of. %.pg -Some tools (such as a blindfold) can be {\it worn\/} and can be put on and -removed like other accessories (rings, amulets); see {\it Amulets\/}. +Some tools (such as a blindfold) can be \textit{worn} and can be put on and +removed like other accessories (rings, amulets); see \textit{Amulets}. Other tools (such as pick-axe) can be wielded as weapons in addition to being applied for their usual purpose, and in some cases (again, pick-axe) become wielded as a weapon even when applied. @@ -3352,38 +3406,38 @@ become wielded as a weapon even when applied. % Mentioned here because of the old method of attempting "Zen" conduct: % restart until there's a blindfold in starting inventory and put it on % first thing. -The {\it blind\/} +The \textit{blind} option can be set (prior to game start) to attempt to play the entire game without being able to see (a self-imposed challenge which is very difficult to accomplish). %.pg -The command to use a tool is `{\tt a}' (apply). +The command to use a tool is `\texttt{a}' (apply). %.hn 3 \subsection*{Containers} %.pg You may encounter bags, boxes, and chests in your travels. A tool of -this sort can be opened with the ``{\tt \#loot}'' extended command when +this sort can be opened with the ``\texttt{\#loot}'' extended command when you are standing on top of it (that is, on the same floor spot), -or with the `{\tt a}' (apply) command when you are carrying it. However, +or with the `\texttt{a}' (apply) command when you are carrying it. However, chests are often locked, and are in any case unwieldy objects. You must set one down before unlocking it by -using a key or lock-picking tool with the `{\tt a}' (apply) command, -by kicking it with the `{\tt \^{}D}' command, -or by using a weapon to force the lock with the ``{\tt \#force}'' +using a key or lock-picking tool with the `\texttt{a}' (apply) command, +by kicking it with the `\texttt{\textasciicircum D}' command, +or by using a weapon to force the lock with the ``\texttt{\#force}'' extended command. %.pg Some chests are trapped, causing nasty things to happen when you unlock or open them. You can check for and try to deactivate traps -with the ``{\tt \#untrap}'' extended command. +with the ``\texttt{\#untrap}'' extended command. %.pg When the contents of a container are known, that container will be described as something like ``a sack containing 3 items''. -In this example, the 3 refers to number of {\it stacks\/} of compatible +In this example, the 3 refers to number of \textit{stacks} of compatible items, not to the total number of individual items. So a sack holding 2 sky blue potions, 7 arrows, and 350 gold pieces would be described as having 3 items rather than 10 or 359. @@ -3396,10 +3450,10 @@ If a chest or large box is described as ``broken'', that means that it can't be locked rather than that it no longer functions as a container. %.pg -The {\it apply\/} and {\it loot\/} commands allow you to take out and/or +The \textit{apply} and \textit{loot} commands allow you to take out and/or put in an arbitrary number of items in a single operation. If you want to take everything out of a container, you can use the -``{\tt \#tip}'' command to pour the contents onto the floor. +``\texttt{\#tip}'' command to pour the contents onto the floor. This may be your only way to get things out if your hands are stuck to a cursed two-handed weapon. When your hands aren't stuck, you have the potential to pour the @@ -3408,7 +3462,7 @@ contents into another container. the floor.) %.hn 2 -\subsection*{Amulets (`{\tt "}')} +\subsection*{Amulets (`\texttt{"}')} %.pg Amulets are very similar to rings, and often more powerful. Like @@ -3421,16 +3475,16 @@ Like wearing rings, wearing an amulet affects your metabolism, causing you to grow hungry more rapidly. %.pg -The commands to use amulets are the same as for rings, `{\tt P}' (put on) -and `{\tt R}' (remove). -`{\tt A}' can be used to remove various worn items including amulets. -Also, '{\tt W}' (wear) and `{\tt T}' (take off) which are normally for +The commands to use amulets are the same as for rings, `\texttt{P}' (put on) +and `\texttt{R}' (remove). +`\texttt{A}' can be used to remove various worn items including amulets. +Also, '\texttt{W}' (wear) and `\texttt{T}' (take off) which are normally for armor can be used for amulets and other accessories (rings and eyewear), but accessories won't be shown as likely candidates in a prompt for choosing what to wear or take off. %.hn 2 -\subsection*{Gems (`{\tt *}')} +\subsection*{Gems (`\texttt{*}')} %.pg Some gems are valuable, and can be sold for a lot of gold. @@ -3444,7 +3498,7 @@ All rocks, however, can be used as projectile weapons (if you have a sling). In the most desperate of cases, you can still throw them by hand. %.hn 2 -\subsection*{Large rocks (`{\tt `}')} +\subsection*{Large rocks (`\texttt{`}')} %.pg Statues and boulders are not particularly useful, and are generally heavy. It is rumored that some statues are not what they seem. @@ -3452,12 +3506,12 @@ It is rumored that some statues are not what they seem. %.pg Boulders occasionally block your path. You can push one forward (by attempting to walk onto its spot) -when nothing blocks {\it its\/} path, or you can +when nothing blocks \textit{its} path, or you can smash it into a pile of small rocks with breaking magic or a pick-axe. It is possible to move onto a boulder's location if certain conditions are met; ordinarily one of those conditions is that pushing it any further be blocked. -Using the move-without-picking-up prefix (default key `{\tt m}') +Using the move-without-picking-up prefix (default key `\texttt{m}') prior to the direction of movement will attempt to move to a boulder's location without pushing it in addition to the prefix's usual action of suppressing auto-pickup at the destination. @@ -3474,11 +3528,11 @@ They can be smashed into rocks though. %.pg For some configurations of the program, statues are no longer shown -as `{\tt `}' +as `\texttt{`}' but by the letter representing the monster they depict instead. %.hn 2 -\subsection*{Gold (`{\tt \$}')} +\subsection*{Gold (`\texttt{\$}')} %.pg Gold adds to your score, and you can buy things in shops with it. @@ -3490,12 +3544,12 @@ you are carrying (shopkeepers aside). Gold pieces are the only type of object where bless/curse state does not apply. They're always uncursed but never described as uncursed even if you turn -off the ``{\it implicit\verb+_+uncursed\/}'' option. -You can set the ``{\it goldX\/}'' +off the ``\textit{implicit\textunderscore uncursed}'' option. +You can set the ``\textit{goldX}'' option if you prefer to have gold pieces be treated as bless/curse state -{\it unknown\/} rather than as known to be uncursed. +\textit{unknown} rather than as known to be uncursed. Only matters when you're using an object selection prompt that can filter -by ``{\tt BUCX}'' state. +by ``\texttt{BUCX}'' state. %.hn 2 \subsection*{Persistence of Objects} @@ -3520,7 +3574,7 @@ again you will re-discover the object and resume remembering it. The situation is the same for a pile of objects, except that only the top item of the pile is displayed. The -{\it hilite\verb+_+pile\/} +\textit{hilite\textunderscore pile} option can be enabled in order to show an item differently when it is the top one of a pile. @@ -3528,10 +3582,10 @@ the top one of a pile. \section{Conduct} %.pg -As if winning {\it NetHack\/} were not difficult enough, certain players +As if winning \textit{NetHack} were not difficult enough, certain players seek to challenge themselves by imposing restrictions on the way they play the game. The game automatically tracks some of -these challenges, which can be checked at any time with the {\tt \#conduct} +these challenges, which can be checked at any time with the \texttt{\#conduct} command or at the end of the game. When you perform an action which breaks a challenge, it will no longer be listed. This gives players extra ``bragging rights'' for winning the game with these @@ -3551,8 +3605,8 @@ not violate any food challenges either. %.pg A strict vegan diet is one which avoids any food derived from animals. The primary source of nutrition is fruits and vegetables. The -corpses and tins of blobs (`{\tt b}'), jellies (`{\tt j}'), and fungi -(`{\tt F}') are also considered to be vegetable matter. Certain human +corpses and tins of blobs (`\texttt{b}'), jellies (`\texttt{j}'), and fungi +(`\texttt{F}') are also considered to be vegetable matter. Certain human food is prepared without animal products; namely, lembas wafers, cram rations, food rations (gunyoki), K-rations, and C-rations. Metal or another normally indigestible material eaten while polymorphed @@ -3563,7 +3617,7 @@ Note however that eating such items still counts against foodless conduct. Vegetarians do not eat animals; however, they are less selective about eating animal byproducts than vegans. In addition to the vegan items listed above, they may eat any kind -of pudding (`{\tt P}') other than the black puddings, +of pudding (`\texttt{P}') other than the black puddings, eggs and food made from eggs (fortune cookies and pancakes), food made with milk (cream pies and candy bars), and lumps of royal jelly. Monks are expected to observe a vegetarian diet. @@ -3594,8 +3648,8 @@ from ``cherries'' to ``pork chops'', are also assumed to be vegan. %.pg An atheist is one who rejects religion. This means that you cannot -{\tt \#pray}, {\tt \#offer} sacrifices to any god, -{\tt \#turn} undead, or {\tt \#chat} with a priest. +\texttt{\#pray}, \texttt{\#offer} sacrifices to any god, +\texttt{\#turn} undead, or \texttt{\#chat} with a priest. Particularly selective readers may argue that playing Monk or Priest characters should violate this conduct; that is a choice left to the player. Offering the Amulet of Yendor to your god is necessary to @@ -3604,6 +3658,15 @@ not penalized for being spoken to by an angry god, priest(ess), or other religious figure; a true atheist would hear the words but attach no special meaning to them. +%.pg +A pauper starts the game with no possessions, no spells, and no weapon or +spell skills (and if playing as a knight, your pony will not have a saddle). +Can only be initiated by starting a new game with \texttt{OPTIONS=pauper} +set in your run-time configurtion file or \texttt{NETHACKOPTIONS} environment +variable. +Once the game is underway, you can acquire and use items, spells, and skills +in the usual way. + %.pg Most players fight with a wielded weapon (or tool intended to be wielded as a weapon). Another challenge is to win the game without @@ -3612,7 +3675,7 @@ fire, and kick weapons; use a wand, spell, or other type of item; or fight with your hands and feet. %.pg -In {\it NetHack}, a pacifist refuses to cause the death of any other monster +In \textit{NetHack}, a pacifist refuses to cause the death of any other monster (i.e. if you would get experience for the death). This is a particularly difficult challenge, although it is still possible to gain experience by other means. @@ -3630,7 +3693,7 @@ counted. %.pg There is a side-branch to the main dungeon called ``Sokoban,'' briefly -described in the earlier section about {\it Traps}. +described in the earlier section about \textit{Traps}. As mentioned there, the goal is to push boulders into pits and/or holes to plug those in order to both get the boulders out of your way and be able to go past the traps. @@ -3641,7 +3704,7 @@ diagonally. Other rules can, such as not smashing boulders with magic or tools, but doing so causes you to receive a luck penalty. No message about that is given at the time, but it is tracked as a conduct. -The {\tt \#conduct} command and end of game disclosure will report whether +The \texttt{\#conduct} command and end of game disclosure will report whether you have abided by the special rules of Sokoban, and if not, how many times you violated them, providing you with a way to discover which actions incur bad luck so that you can be better informed about whether @@ -3653,7 +3716,7 @@ you haven't done anything interesting there. Ending the game with ``never broke the Sokoban rules'' conduct is most meaningful if you also manage to perform the ``obtained the Sokoban prize'' achievement -(see {\it Achievements\/} below).) +(see \textit{Achievements} below).) %.pg There are several other challenges tracked by the game. It is possible @@ -3679,7 +3742,7 @@ choose ``nothing'' if you want to decline. End of game disclosure will also display various achievements representing progress toward ultimate ascension, if any have been attained. -They aren't directly related to {\it conduct\/} but are grouped with +They aren't directly related to \textit{conduct} but are grouped with it because they fall into the same category of ``bragging rights'' and to limit the number of questions during disclosure. Listed here roughly in order of difficulty and not necessarily in the order @@ -3688,64 +3751,64 @@ in which you might accomplish them. % [length stuff copied from paranoid_confirmation] \newlength{\achwidth} %.PS "Mines'\~End\~" -\settowidth{\achwidth}{\tt Mines'~End~} +\settowidth{\achwidth}{\texttt{Mines'~End~}} \addtolength{\achwidth}{\labelsep} -\blist{\leftmargin \achwidth \topsep 1mm \itemsep 0mm} +\begin{description}[leftmargin=\achwidth, topsep=1mm, itemsep=0mm, labelwidth=*, font=\ttfamily, align=right] %.PL Shop -\item[{\tt }] -Attained rank title {\it Rank}. -\item[{\tt Shop}] +\item[] +Attained rank title \textit{Rank}. +\item[Shop] Entered a shop. -\item[{\tt Temple}] +\item[Temple] Entered a temple. -\item[{\tt Mines}] +\item[Mines] Entered the Gnomish Mines. -\item[{\tt Town}] +\item[Town] Entered Mine Town. -\item[{\tt Oracle}] +\item[Oracle] Consulted the Oracle of Delphi. -\item[{\tt Novel}] +\item[Novel] Read a passage from a Discworld Novel. -\item[{\tt Sokoban}] +\item[Sokoban] Entered Sokoban. -\item[{\tt "Big~Room"}] +\item["Big~Room"] Entered the Big Room. -\item[{\tt "Soko-Prize"}] +\item["Soko-Prize"] Explored to the top of Sokoban and found a special item there. -\item[{\tt Mines'~End}] +\item[Mines'~End] Explored to the bottom of the Gnomish Mines and found a special item there. -\item[{\tt Medusa}] +\item[Medusa] Defeated Medusa. -\item[{\tt Tune}] +\item[Tune] Discovered the tune that can be used to open and close the drawbridge on the Castle level. -\item[{\tt Bell}] +\item[Bell] Acquired the Bell of Opening. -\item[{\tt Gehennom}] +\item[Gehennom] Entered Gehennom. -\item[{\tt Candle}] +\item[Candle] Acquired the Candelabrum of Invocation. -\item[{\tt Book}] +\item[Book] Acquired the Book of the Dead. -\item[{\tt Invocation}] +\item[Invocation] Gained access to the bottommost level of Gehennom. -\item[{\tt Amulet}] +\item[Amulet] Acquired the fabled Amulet of Yendor. -\item[{\tt Endgame}] +\item[Endgame] Reached the Elemental Planes. -\item[{\tt Astral}] +\item[Astral] Reached the Astral Plane level. -\item[{\tt Blind}] +\item[Blind] Blind from birth. -\item[{\tt Deaf}] +\item[Deaf] Deaf from birth. -\item[{\tt Nudist}] +\item[Nudist] Never wore any armor. -\item[{\tt Pauper}] +\item[Pauper] Started out with no possessions. -\item[{\tt Ascended}] +\item[Ascended] Delivered the Amulet to its final destination. -\elist +\end{description} %.PE %.ED @@ -3758,20 +3821,20 @@ Achievements are recorded and subsequently reported in the order in which they happen during your current game rather than the order listed here. %.pg -There are nine {\it Rank} titles for each role, bestowed at experience +There are nine \textit{Rank} titles for each role, bestowed at experience levels 1, 3, 6, 10, 14, 18, 22, 26, and 30. The one for experience level 1 is not recorded as an achievement. Losing enough levels to revert to lower rank(s) does not discard the corresponding achievement(s). %.pg -There's no guaranteed {\it Novel} so the achievement to read one might +There's no guaranteed \textit{Novel} so the achievement to read one might not always be attainable (except perhaps by wishing). -Similarly, the {\it Big Room} level is not always present. +Similarly, the \textit{Big Room} level is not always present. Unlike with the Novel, there's no way to wish for this opportunity. %.pg -The ``special items'' hidden in {\it Mines'~End\/} and {\it Sokoban\/} +The ``special items'' hidden in \textit{Mines'~End} and \textit{Sokoban} are not unique but are considered to be prizes or rewards for exploring those levels since doing so is not necessary to complete the game. @@ -3779,20 +3842,21 @@ Finding other instances of the same objects doesn't record the corresponding achievement. %.pg -The {\it Medusa\/} achievement is recorded if she dies for any reason, +The \textit{Medusa} achievement is recorded if she dies for any reason, even if you are not directly responsible, and only if she dies. %.pg -The 5-note {\it tune\/} can be learned via trial and error with a musical +The 5-note \textit{tune} can be learned via trial and error with a musical instrument played closely enough---but not too close!---to the Castle level's drawbridge or can be given to you via prayer boon. %.pg -{\it Blind\/}, {\it Deaf\/}, {\it Nudist\/}, and {\it Pauper\/} are also conducts, and they can only be -enabled by setting the correspondingly named option in {\tt NETHACKOPTIONS} +\textit{Blind}, \textit{Deaf}, \textit{Nudist}, and \textit{Pauper} are also conducts, and they can only be +enabled by setting the correspondingly named option in \texttt{NETHACKOPTIONS} or run-time configuration file prior to game start. -In the case of {\it Blind\/} and {\it Deaf\/}, the option also enforces the conduct. +In the case of \textit{Blind} and \textit{Deaf}, the option also enforces the +conduct. They aren't really significant accomplishments unless/until you make substantial progress into the dungeon. @@ -3800,19 +3864,19 @@ substantial progress into the dungeon. \section{Options} %.pg -Due to variations in personal tastes and conceptions of how {\it NetHack\/} -should do things, there are options you can set to change how {\it NetHack\/} +Due to variations in personal tastes and conceptions of how \textit{NetHack} +should do things, there are options you can set to change how \textit{NetHack} behaves. %.hn 2 \subsection*{Setting the options} %.pg -Options may be set in a number of ways. Within the game, the `{\tt O}' +Options may be set in a number of ways. Within the game, the `\texttt{O}' command allows you to view all options and change most of them. You can also set options automatically by placing them in a configuration -file, or in the ``{\tt NETHACKOPTIONS}'' environment variable. -Some versions of {\it NetHack\/} also have front-end programs that allow +file, or in the ``\texttt{NETHACKOPTIONS}'' environment variable. +Some versions of \textit{NetHack} also have front-end programs that allow you to set options before starting the game or a global configuration for system administrators. @@ -3835,27 +3899,27 @@ On Windows, the name is \mbox{``.nethackrc''} location in the folder The file may not exist, but it is a normal ASCII text file and can be created with any text editor. -After running {\it NetHack\/} for the first time, you should find a default +After running \textit{NetHack} for the first time, you should find a default template for ths configuration file named \mbox{``.nethackrc.template''} in \mbox{{``\%USERPROFILE\%\textbackslash NetHack\textbackslash''}}. -If you have not created the configuration file, {\it NetHack\/} will create +If you have not created the configuration file, \textit{NetHack} will create the configuration file for you using the default template file.\\ %.lp "" On MS-DOS it is \mbox{``defaults.nh''} in the same folder as -\mbox{{\it nethack.exe\/}}.\\ +\mbox{\textit{nethack.exe}}.\\ %.lp "" -Any line in the configuration file starting with `{\tt \#}' is treated +Any line in the configuration file starting with `\texttt{\#}' is treated as a comment and ignored. Empty lines are ignored. -Any line beginning with `{\tt \verb+[+}' and ending in `{\tt \verb+]+}' -is a section marker (the closing `{\tt \verb+]+}' can be followed -by whitespace and then an arbitrary comment beginning with `{\tt \#}'). +Any line beginning with `\texttt{[}' and ending in `\texttt{]}' +is a section marker (the closing `\texttt{]}' can be followed +by whitespace and then an arbitrary comment beginning with `\texttt{\#}'). The text between the square brackets is the section name. Section markers are only valid after a CHOOSE directive and their names -are case insensitive. +are case-insensitive. Lines after a section marker belong to that section up until another section starts or a marker without a name is encountered or the file ends. Lines within sections are ignored unless a CHOOSE directive has selected @@ -3872,8 +3936,8 @@ settings particular to that directive. Here is a list of allowed directives: %.lp -\blist{} -\item[\bb{OPTIONS}] +\begin{description} +\item[OPTIONS] There are two types of options, boolean and compound options. Boolean options toggle a setting on or off, while compound options take more diverse values. @@ -3894,40 +3958,40 @@ Example: %.ed %.lp -\item[\bb{HACKDIR}] -Default location of files {\it NetHack\/} needs. On Windows HACKDIR -defaults to the location of the {\it NetHack.exe\/} or {\it NetHackw.exe\/} file +\item[HACKDIR] +Default location of files \textit{NetHack} needs. On Windows HACKDIR +defaults to the location of the \textit{NetHack.exe} or \textit{NetHackw.exe} file so setting HACKDIR to override that is not usually necessary or recommended. %.lp -\item[\bb{LEVELDIR}] +\item[LEVELDIR] The location that in-progress level files are stored. Defaults to HACKDIR, must be writable. %.lp -\item[\bb{SAVEDIR}] +\item[SAVEDIR] The location where saved games are kept. Defaults to HACKDIR, must be writable. %.lp -\item[\bb{BONESDIR}] +\item[BONESDIR] The location that bones files are kept. Defaults to HACKDIR, must be writable. %.lp -\item[\bb{LOCKDIR}] +\item[LOCKDIR] The location that file synchronization locks are stored. Defaults to HACKDIR, must be writable. %.lp -\item[\bb{TROUBLEDIR}] +\item[TROUBLEDIR] The location that a record of game aborts and self-diagnosed game problems is kept. Defaults to HACKDIR, must be writable. % % config file entries beyond this point are shown alphabetically % %.lp -\item[\bb{AUTOCOMPLETE}] +\item[AUTOCOMPLETE] Enable or disable an extended command autocompletion. Autocompletion has no effect for the X11 windowport. You can specify multiple autocompletions. To enable autocompletion, list the extended command. Prefix the -command with ``{{\tt !}}'' to disable the autocompletion +command with ``\texttt{!}'' to disable the autocompletion for that command. %.lp "" @@ -3939,11 +4003,11 @@ Example: %.ed %.lp -\item[\bb{AUTOPICKUP\_EXCEPTION}] -Set exceptions to the {{\it pickup\_types\/}} +\item[AUTOPICKUP\textunderscore EXCEPTION] +Set exceptions to the {\textit{pickup\textunderscore types}} option. See the ``Configuring Autopickup Exceptions'' section. %.lp -\item[\bb{BINDINGS}] +\item[BINDINGS] Change the key bindings of some special keys, menu accelerators, extended commands, or mouse buttons. You can specify multiple bindings. Format is key followed by the command, separated by a colon. @@ -3958,7 +4022,7 @@ Example: %.ed %.lp -\item[\bb{CHOOSE}] +\item[CHOOSE] Chooses at random one of the comma-separated parameters as an active section name. Lines in other sections are ignored. @@ -3979,35 +4043,35 @@ Example: %.ed %.lp "" -If {\tt []} is present, the preceding section is closed and no new +If \texttt{[]} is present, the preceding section is closed and no new section begins; whatever follows will be common to all sections. Otherwise the last section extends to the end of the options file. %.lp -\item[\bb{MENUCOLOR}] +\item[MENUCOLOR] Highlight menu lines with different colors. See the ``Configuring Menu Colors`` section. %.lp -\item[\bb{MSGTYPE}] +\item[MSGTYPE] Change the way messages are shown in the top status line. See the ``Configuring Message Types`` section. %.lp -\item[\bb{ROGUESYMBOLS}] +\item[ROGUESYMBOLS] Custom symbols for the rogue level's symbol set. -See {\it SYMBOLS} below. +See \textit{SYMBOLS} below. %.lp -\item[\bb{SOUND}] +\item[SOUND] Define a sound mapping. See the ``Configuring User Sounds'' section. %.lp -\item[\bb{SOUNDDIR}] +\item[SOUNDDIR] Define the directory that contains the sound files. See the ``Configuring User Sounds'' section. %.lp -\item[\bb{SYMBOLS}] +\item[SYMBOLS] Override one or more symbols in the symbol set used for all dungeon levels except for the special rogue level. -See the ``Modifying {\it NetHack\/} Symbols'' section. +See the ``Modifying \textit{NetHack} Symbols'' section. %.pg %.lp "" @@ -4015,16 +4079,17 @@ Example: %.sd \begin{verbatim} # replace small punctuation (tick marks) with digits - SYMBOLS=S_boulder:0,S_golem:7 + SYMBOLS=S_golem:7 \end{verbatim} %.ed %.lp -\item[\bb{WIZKIT}] +\item[WIZKIT] Debug mode only: extra items to add to initial inventory. Value is the name of a text file containing a list of item names, one per line, up to a maximum of 128 lines. Each line is processed by the function that handles wishing. +Entries are added to the wish history; see the wizwish-command. %.lp "" Example: @@ -4033,7 +4098,7 @@ Example: WIZKIT=~/wizkit.txt \end{verbatim} %.ed -\elist +\end{description} %.lp "" %.pg @@ -4051,7 +4116,8 @@ Here is an example of configuration file contents: OPTIONS=lit_corridor # Show lit corridors differently OPTIONS=hilite_pet,hilite_pile # Replace small punctuation (tick marks) with digits - SYMBOLS=S_boulder:0,S_golem:7 + OPTIONS=boulder:0 + SYMBOLS=S_golem:7 # No startup splash screen. Windows GUI only. OPTIONS=!splash_screen @@ -4060,13 +4126,13 @@ Here is an example of configuration file contents: %.BR 2 %.hn 2 -\subsection*{Using the {\tt NETHACKOPTIONS} environment variable} +\subsection*{Using the \texttt{NETHACKOPTIONS} environment variable} %.pg The NETHACKOPTIONS variable is a comma-separated list of initial values for the various options. Some can only be turned on or off. You turn one of these on by adding the name of the option to the list, -and turn it off by typing a `{\tt !}' or ``{\tt no}'' before the name. +and turn it off by typing a `\texttt{!}' or ``\texttt{no}'' before the name. Others take a character string as a value. You can set string options by typing the option name, a colon or equals sign, and then the value of the string. @@ -4074,10 +4140,10 @@ The value is terminated by the next comma or the end of string. %.pg For example, to set up an environment variable so that -{\it color\/} is {\tt on}, -{\it legacy\/} is {\tt off}, -character {\it name\/} is set to ``{\tt Blue Meanie}'', -and named {\it fruit\/} is set to ``{\tt lime}'', +\textit{color} is \texttt{on}, +\textit{legacy} is \texttt{off}, +character \textit{name} is set to ``\texttt{Blue Meanie}'', +and named \textit{fruit} is set to ``\texttt{lime}'', you would enter the command %.SD i \begin{verbatim} @@ -4085,7 +4151,7 @@ you would enter the command \end{verbatim} %.ED -\nd in {\it csh} +\noindent in \textit{csh} (note the need to escape the `!' since it's special to that shell), or the pair of commands %.SD i @@ -4095,7 +4161,7 @@ to that shell), or the pair of commands \end{verbatim} %.ED -\nd in {\it sh}, {\it ksh}, or {\it bash}. +\noindent in \textit{sh}, \textit{ksh}, or \textit{bash}. %.pg The NETHACKOPTIONS value is effectively the same as a single OPTIONS @@ -4109,9 +4175,9 @@ not allowed. Instead of a comma-separated list of options, NETHACKOPTIONS can be set to the full name of a configuration file you want to use. -If that full name doesn't start with a slash, precede it with `{\tt @}' +If that full name doesn't start with a slash, precede it with `\texttt{@}' (at-sign) to let NetHack know that the rest is intended as a file name. -If it does start with `{\tt /}', the at-sign is optional. +If it does start with `\texttt{/}', the at-sign is optional. %.hn 2 \subsection*{Customization options} @@ -4126,56 +4192,56 @@ Some options are persistent, and are saved and reloaded along with the game. Changing a persistent option in the configuration file applies only to new games. -\blist{} +\begin{description}[font=\mdseries\itshape] %.lp -\item[\ib{accessiblemsg}] +\item[accessiblemsg] Add location or direction information to messages (default is off). %.lp -\item[\ib{acoustics}] +\item[acoustics] Enable messages about what your character hears (default on). Note that this has nothing to do with your computer's audio capabilities. Persistent. %.lp -\item[\ib{alignment}] -Your starting alignment ({\tt align:lawful}, {\tt align:neutral}, -or {\tt align:chaotic}). +\item[alignment] +Your starting alignment (\texttt{align:lawful}, \texttt{align:neutral}, +or \texttt{align:chaotic}). You may specify just the first letter. Many roles and the non-human races restrict which alignments are allowed. -See {\it role\/} +See \textit{role} for a description of how to use negation to exclude choices. %.lp "" \\ -If {\tt align} is not specified, there is no default value; +If \texttt{align} is not specified, there is no default value; player will be prompted unless role and/or race forces a choice for alignment. -Cannot be set with the `{\tt O}' command. Persistent. +Cannot be set with the `\texttt{O}' command. Persistent. %.lp -\item[\ib{autodescribe}] +\item[autodescribe] Automatically describe the terrain under cursor when asked to get a location on the map (default true). -The {\it whatis\verb+_+coord\/} +The \textit{whatis\textunderscore coord} option controls whether the description includes map coordinates. %.lp -\item[\ib{autodig}] +\item[autodig] Automatically dig if you are wielding a digging tool and moving into a place that can be dug (default false). Persistent. %.lp -\item[\ib{autoopen}] +\item[autoopen] Walking into a closed door attempts to open it (default true). Persistent. %.lp -\item[\ib{autopickup}] +\item[autopickup] Automatically pick up things onto which you move (default off). Persistent. \\ %.lp "" -See ``{\it pickup\verb+_+types\/}'' and also -``{\it autopickup\verb+_+exception\/}'' for ways to refine the behavior. +See ``\textit{pickup\textunderscore types}'' and also +``\textit{autopickup\textunderscore exception}'' for ways to refine the behavior. \\ %.lp "" -Note: prior to version 3.7.0, the default for {\it autopickup\/} was {\it on}. +Note: prior to version 3.7.0, the default for \textit{autopickup} was \textit{on}. %.lp -\item[\ib{autoquiver}] -This option controls what happens when you attempt the `{\tt f}' (fire) +\item[autoquiver] +This option controls what happens when you attempt the `\texttt{f}' (fire) command when nothing is quivered or readied (default false). When true, the computer will fill your quiver or quiver sack or make ready some suitable weapon. @@ -4183,11 +4249,11 @@ Note that it will not take into account the blessed/cursed status, enchantment, damage, or quality of the weapon; you are free to manually fill your quiver or quiver sack or make ready -with the `{\tt Q}' command instead. +with the `\texttt{Q}' command instead. If no weapon is found or the option is -false, the `{\tt t}' (throw) command is executed instead. Persistent. +false, the `\texttt{t}' (throw) command is executed instead. Persistent. %.lp -\item[\ib{autounlock}] +\item[autounlock] %\hyphenation{apply\-key}%this needs to be tested... Controls what action to take when attempting to walk into a locked door or to loot a locked container. @@ -4196,37 +4262,37 @@ Takes a plus-sign separated list of values: % au => autounlock \newlength{\auwidth} %.PS Apply-Key -\settowidth{\auwidth}{\tt Apply-Key} +\settowidth{\auwidth}{\texttt{Apply-Key}} \addtolength{\auwidth}{\labelsep} -\blist{\leftmargin \auwidth \topsep 1mm \itemsep 0mm} +\begin{description}[leftmargin=\auwidth, topsep=1mm, itemsep=0mm, labelwidth=*, font=\ttfamily, align=right] %.PL Untrap -\item[{\tt Untrap}] +\item[Untrap] prompt about whether to attempt to find a trap; it might fail to find one even when present; if it does find one, it will ask whether you want to try to disarm the trap; if you decline, your character will forget that the door or box is trapped; %.PL Apply-Key -\item[{\tt Apply-Key}] +\item[Apply-Key] if carrying a key or other unlocking tool, prompt about using it; %.PL Kick -\item[{\tt Kick}] +\item[Kick] kick the door (if you omit untrap or decline to attempt untrap and you omit apply-key or you lack a key or you decline to use the key; has no effect on containers); %.PL Force -\item[{\tt Force}] +\item[Force] try to force a container's lid with your currently wielded weapon (if you omit untrap or decline to attempt untrap and you omit apply-key or you lack a key or you decline to use the key; has no effect on doors); %.PL None -\item[{\tt None}] +\item[None] none of the above; can't be combined with the other choices. %.PE -\elist -Omitting the value is treated as if {\tt autounlock:apply-key}. -Preceding {\tt autounlock} with `{\tt !}' or ``{\tt no}'' is treated as -{\tt autounlock:none}. +\end{description} +Omitting the value is treated as if \texttt{autounlock:apply-key}. +Preceding \texttt{autounlock} with `\texttt{!}' or ``\texttt{no}'' is treated as +\texttt{autounlock:none}. \\ %.lp "" Applying a key might set off a trap if the door or container is trapped. @@ -4238,64 +4304,64 @@ destroy some of its contents or damage your weapon or both. The default is Apply-Key. Persistent. %.lp -\item[\ib{blind}] +\item[blind] Start the character permanently blind (default false). Persistent. %.lp -\item[\ib{bones}] +\item[bones] Allow saving and loading bones files (default true). Persistent. %.lp -\item[\ib{boulder}] +\item[boulder] Set the character used to display boulders (default is the ``large rock'' -class symbol, `{\tt `}'). +class symbol, `\texttt{`}'). %.lp -\item[\ib{catname}] -Name your starting cat (for example, ``{\tt catname:Morris}''). -Cannot be set with the `{\tt O}' command. +\item[catname] +Name your starting cat (for example, ``\texttt{catname:Morris}''). +Cannot be set with the `\texttt{O}' command. %.lp character -\item[\ib{character}] -Synonym for ``{\tt role}'' to pick the type of your character -(for example ``{\tt character:Monk}''). See {\it role\/} for more details. +\item[character] +Synonym for ``\texttt{role}'' to pick the type of your character +(for example ``\texttt{character:Monk}''). See \textit{role} for more details. %.lp -\item[\ib{checkpoint}] +\item[checkpoint] Save game state after each level change, for possible recovery after program crash (default on). Persistent. %.lp -\item[\ib{cmdassist}] +\item[cmdassist] Have the game provide some additional command assistance for new players if it detects some anticipated mistakes (default on). %.lp -\item[\ib{confirm}] +\item[confirm] Have user confirm attacks on pets, shopkeepers, and other peaceable creatures (default on). Persistent. %.lp -\item[\ib{dark\verb+_+room}] +\item[dark\textunderscore room] Show out-of-sight areas of lit rooms (default on). Persistent. %.lp -\item[\ib{deaf}] +\item[deaf] Start the character permanently deaf (default false). Persistent. %.lp -\item[\ib{dropped\verb+_+nopick}] +\item[dropped\textunderscore nopick] If this option is on, items you dropped will not be automatically picked up, -even if ``{\it autopickup\/}'' is also on and they are in -``{\it pickup\verb+_+types\/}'' or match a positive autopickup exception +even if ``\textit{autopickup}'' is also on and they are in +``\textit{pickup\textunderscore types}'' or match a positive autopickup exception (default on). Persistent. %.lp -\item[\ib{disclose}] +\item[disclose] Controls what information the program reveals when the game ends. Value is a space separated list of prompting/category pairs -(default is `{\tt ni na nv ng nc no}', -prompt with default response of `{\tt n}' for each candidate). +(default is `\texttt{ni na nv ng nc no}', +prompt with default response of `\texttt{n}' for each candidate). Persistent. The possibilities are: %.sd %.si -{\tt i} --- disclose your inventory;\\ -{\tt a} --- disclose your attributes;\\ -{\tt v} --- summarize monsters that have been vanquished;\\ -{\tt g} --- list monster species that have been genocided;\\ -{\tt c} --- display your conduct; also achievements, if any;\\ -{\tt o} --- display dungeon overview. +\texttt{i} --- disclose your inventory;\\ +\texttt{a} --- disclose your attributes;\\ +\texttt{v} --- summarize monsters that have been vanquished;\\ +\texttt{g} --- list monster species that have been genocided;\\ +\texttt{c} --- display your conduct; also achievements, if any;\\ +\texttt{o} --- display dungeon overview. %.ei %.ed @@ -4304,45 +4370,45 @@ lets you refine how it behaves. Here are the valid prefixes: %.sd %.si -{\tt y} --- prompt you and default to yes on the prompt;\\ -{\tt n} --- prompt you and default to no on the prompt;\\ -{\tt +} --- disclose it without prompting;\\ -{\tt -} --- do not disclose it and do not prompt. +\texttt{y} --- prompt you and default to yes on the prompt;\\ +\texttt{n} --- prompt you and default to no on the prompt;\\ +\texttt{+} --- disclose it without prompting;\\ +\texttt{-} --- do not disclose it and do not prompt. %.ei %.ed The listing of vanquished monsters can be sorted, -so there are two additional choices for `{\tt v}': +so there are two additional choices for `\texttt{v}': The listings of vanquished monsters and of genocided types can be sorted, so there are two additional choices for `q' and `g': %.sd %.si -{\tt ?} --- prompt you and default to ask on the prompt;\\ +\texttt{?} --- prompt you and default to ask on the prompt;\\ {\tt\#} --- disclose it without prompting, ask for sort order. %.ei %.ed Asking refers to picking one of the orderings from a menu. -The `{\tt +}' disclose without prompting choice, -or being prompted and answering `{\tt y}' rather than `{\tt a}', +The `\texttt{+}' disclose without prompting choice, +or being prompted and answering `\texttt{y}' rather than `\texttt{a}', will default to showing monsters in the order specified by the -{\it sortvanquished\/} option. +\textit{sortvanquished} option. \\ %.lp "" -Omitted categories are implicitly added with `{\tt n}' prefix. -Specified categories with omitted prefix implicitly use `{\tt +}' prefix. +Omitted categories are implicitly added with `\texttt{n}' prefix. +Specified categories with omitted prefix implicitly use `\texttt{+}' prefix. Order of the disclosure categories does not matter, program display for end-of-game disclosure follows a set sequence. %.lp "" -(for example, ``{\tt disclose:yi na +v -g o}'') +(for example, ``\texttt{disclose:yi na +v -g o}'') The example sets -{\tt inventory} to {\it prompt\/} and default to {\it yes\/}, -{\tt attributes} to {\it prompt\/} and default to {\it no\/}, -{\tt vanquished} to {\it disclose without prompting\/}, -{\tt genocided} to {\it not disclose\/} and {\it not prompt\/}, -{\tt conduct} to implicitly {\it prompt\/} and default to {\it no\/}, -{\tt overview} to {\it disclose without prompting\/}. +\texttt{inventory} to \textit{prompt} and default to \textit{yes}, +\texttt{attributes} to \textit{prompt} and default to \textit{no}, +\texttt{vanquished} to \textit{disclose without prompting}, +\texttt{genocided} to \textit{not disclose} and \textit{not prompt}, +\texttt{conduct} to implicitly \textit{prompt} and default to \textit{no}, +\texttt{overview} to \textit{disclose without prompting}. %.lp "" Note that the vanquished monsters list includes all monsters killed by @@ -4350,11 +4416,11 @@ traps and each other as well as by you. And the dungeon overview shows all levels you had visited but does not reveal things about them that you hadn't discovered. %.lp -\item[\ib{dogname}] -Name your starting dog (for example, ``{\tt dogname:Fang}''). -Cannot be set with the `{\tt O}' command. +\item[dogname] +Name your starting dog (for example, ``\texttt{dogname:Fang}''). +Cannot be set with the `\texttt{O}' command. %.lp -\item[\ib{extmenu}] +\item[extmenu] Changes the extended commands interface to pop-up a menu of available commands. It is keystroke compatible with the traditional interface except that it does not require that you hit Enter. @@ -4365,84 +4431,84 @@ command, it controls whether the menu shows all available commands (on) or just the subset of commands which have traditionally been considered extended ones (off). %.lp -\item[\ib{female}] -An obsolete synonym for ``{\tt gender:female}''. Cannot be set with the -`{\tt O}' command. +\item[female] +An obsolete synonym for ``\texttt{gender:female}''. Cannot be set with the +`\texttt{O}' command. %.lp -\item[\ib{fireassist}] -This option controls what happens when you attempt the `{\tt f}' (fire) +\item[fireassist] +This option controls what happens when you attempt the `\texttt{f}' (fire) and don't have an appropriate launcher, such as a bow or a sling, wielded. If on, you will automatically wield the launcher. Default is on. %.lp -\item[\ib{fixinv}] +\item[fixinv] An object's inventory letter sticks to it when it's dropped (default on). If this is off, dropping an object shifts all the remaining inventory letters. Persistent. %.lp -\item[\ib{force\_invmenu}] +\item[force\textunderscore invmenu] Commands asking for an inventory item show a menu instead of a text query with possible menu letters. Default is off. %.lp -\item[\ib{fruit}] -Name a fruit after something you enjoy eating (for example, ``{\tt fruit:mango}'') -(default ``{\tt slime mold}''). Basically a nostalgic whimsy that -{\it NetHack\/} uses from time to time. You should set this to something you +\item[fruit] +Name a fruit after something you enjoy eating (for example, ``\texttt{fruit:mango}'') +(default ``\texttt{slime mold}''). Basically a nostalgic whimsy that +\textit{NetHack} uses from time to time. You should set this to something you find more appetizing than slime mold. Apples, oranges, pears, bananas, and -melons already exist in {\it NetHack\/}, so don't use those. +melons already exist in \textit{NetHack}, so don't use those. %.lp -\item[\ib{gender}] -Your starting gender ({\tt gender:male} or {\tt gender:female}). +\item[gender] +Your starting gender (\texttt{gender:male} or \texttt{gender:female}). You may specify just the first letter. Although you can still denote your gender using either of the deprecated -``{\it male\/}'' and ``{\it female\/}'' -options, the ``{\it gender\/}'' option will take precedence. -See {\it role\/} +``\textit{male}'' and ``\textit{female}'' +options, the ``\textit{gender}'' option will take precedence. +See \textit{role} for a description of how to use negation to exclude choices. %.lp "" \\ -If {\tt gender} is not specified, there is no default value; +If \texttt{gender} is not specified, there is no default value; player will be prompted unless role and/or race forces a choice for gender. -Cannot be set with the `{\tt O}' command. Persistent. +Cannot be set with the `\texttt{O}' command. Persistent. %.lp -\item[\ib{goldX}] +\item[goldX] When filtering objects based on bless/curse state (BUCX), whether to -treat gold pieces as {\tt X} (unknown bless/curse state, when `on') -or {\tt U} (known to be uncursed, when `off', the default). +treat gold pieces as \texttt{X} (unknown bless/curse state, when `on') +or \texttt{U} (known to be uncursed, when `off', the default). Gold is never blessed or cursed, but it is not described as ``uncursed'' -even when the {\it implicit\verb+_+uncursed\/} option is `off'. +even when the \textit{implicit\textunderscore uncursed} option is `off'. %.lp -\item[\ib{help}] +\item[help] If more information is available for an object looked at -with the `{\tt /}' command, ask if you want to see it (default on). +with the `\texttt{/}' command, ask if you want to see it (default on). Turning help off makes just looking at things faster, since you aren't -interrupted with the ``{\tt More info?}'' prompt, but it also means that you +interrupted with the ``\texttt{More info?}'' prompt, but it also means that you might miss some interesting and/or important information. Persistent. %.lp -\item[\ib{herecmd\verb+_+menu}] +\item[herecmd\textunderscore menu] When using a windowport that supports mouse and clicking on yourself or next to you, show a menu of possible actions for the location. -Same as ``{\tt \#herecmdmenu}'' and ``{\tt \#therecmdmenu}'' commands. +Same as ``\texttt{\#herecmdmenu}'' and ``\texttt{\#therecmdmenu}'' commands. %.lp -\item[\ib{hilite\verb+_+pet}] +\item[hilite\textunderscore pet] Visually distinguish pets from similar animals (default off). The behavior of this option depends on the type of windowing you use. In text windowing, text highlighting or inverse video is often used; with tiles, generally displays a heart symbol near pets. %.lp "" -With the tty or curses interface, the {\it petattr\/} +With the tty or curses interface, the \textit{petattr} option controls how to highlight pets and setting it will turn the -{\it hilite\verb+_+pet\/} option on or off as warranted. +\textit{hilite\textunderscore pet} option on or off as warranted. %.lp -\item[\ib{hilite\verb+_+pile}] +\item[hilite\textunderscore pile] Visually distinguish piles of objects from individual objects (default off). The behavior of this option depends on the type of windowing you use. In text windowing, text highlighting or inverse video is often used; with tiles, generally displays a small plus-symbol beside the object on the top of the pile. %.lp -\item[\ib{hitpointbar}] +\item[hitpointbar] Show a hit point bar graph behind your name and title in the status display (default off). \\ @@ -4456,27 +4522,27 @@ the bar. If there is one for hitpoints in effect and it specifies color, that color will be used for the bar. However if it specifies video attributes, they will be ignored in -favor of {\it inverse}. -For tty and curses, {\it blink\/} will also be used if the current -hitpoint value is at or below the {\it critical HP\/} threshold. +favor of \textit{inverse}. +For tty and curses, \textit{blink} will also be used if the current +hitpoint value is at or below the \textit{critical HP} threshold. \\ %.lp The ``Qt'' interface also supports hitpointbar, by drawing a solid bar above the name and title with a hard-coded color scheme. (As of this writing, having the bar enabled unintentionally inhibits resizing the status panel. -To resize that, use the {\tt \#optionsfull} command to toggle the -{\it hitpointbar\/} option off, perform the resize while it's off, then +To resize that, use the \texttt{\#optionsfull} command to toggle the +\textit{hitpointbar} option off, perform the resize while it's off, then use the same command to toggle it back on.) %.lp -\item[\ib{horsename}] -Name your starting horse (for example, ``{\tt horsename:Trigger}''). -Cannot be set with the `{\tt O}' command. +\item[horsename] +Name your starting horse (for example, ``\texttt{horsename:Trigger}''). +Cannot be set with the `\texttt{O}' command. %.lp -\item[\ib{ignintr}] +\item[ignintr] Ignore interrupt signals, including breaks (default off). Persistent. %.lp -\item[\ib{implicit\verb+_+uncursed}] +\item[implicit\textunderscore uncursed] Omit ``uncursed'' from object descriptions when it can be deduced from other aspects of the description (default on). Persistent. @@ -4484,48 +4550,48 @@ Persistent. %.lp "" If you use menu coloring, you may want to turn this off. %.lp -\item[\ib{legacy}] +\item[legacy] Display an introductory message when starting the game (default on). Persistent. %.lp -\item[\ib{lit\verb+_+corridor}] +\item[lit\textunderscore corridor] Show corridor squares seen by night vision or a light source held by your character as lit (default off). Persistent. %.lp -\item[\ib{lootabc}] +\item[lootabc] When using a menu to interact with a container, -use the old `{\tt a}', `{\tt b}', and `{\tt c}' keyboard shortcuts -rather than the mnemonics `{\tt o}', `{\tt i}', and `{\tt b}' (default off). +use the old `\texttt{a}', `\texttt{b}', and `\texttt{c}' keyboard shortcuts +rather than the mnemonics `\texttt{o}', `\texttt{i}', and `\texttt{b}' (default off). Persistent. %.lp -\item[\ib{mail}] +\item[mail] Enable mail delivery during the game (default on). Persistent. %.lp -\item[\ib{male}] -An obsolete synonym for ``{\tt gender:male}''. Cannot be set with the -`{\tt O}' command. +\item[male] +An obsolete synonym for ``\texttt{gender:male}''. Cannot be set with the +`\texttt{O}' command. %.lp -\item[\ib{mention\verb+_+decor}] +\item[mention\textunderscore decor] Give feedback when walking onto various dungeon features such as stairs, fountains, or altars which are ordinarily only described when covered by one or more objects (default off). Persistent. %.lp -\item[\ib{mention\verb+_+map}] +\item[mention\textunderscore map] Give feedback when interesting map locations change (default off). %.lp -\item[\ib{mention\verb+_+walls}] +\item[mention\textunderscore walls] Give feedback when walking against a wall (default off). Persistent. %.lp -\item[\ib{menucolors}] +\item[menucolors] Enable coloring menu lines (default off). -See ``{\it Configuring Menu Colors\/}'' on how to configure the colors. +See ``\textit{Configuring Menu Colors}'' on how to configure the colors. %.lp -\item[\ib{menustyle}] +\item[menustyle] Controls the method used when you need to choose various objects (in -response to the {\tt Drop} (aka {\tt droptype}) command, for instance). +response to the \texttt{Drop} (aka \texttt{droptype}) command, for instance). The value specified should be the first letter of one of the following: traditional, combination, full, or partial. -Default is {\tt full}. +Default is \texttt{full}. Persistent. \\ %.lp "" @@ -4541,43 +4607,43 @@ object classes rather than a character prompt, and then a menu of matching objects for selection. (Choosing its `A' (Autoselect-All) choice skips the second menu. To avoid choosing that by accident, -set {\it paranoid\verb+_+confirm:AutoAll\/} to require confirmation.) +set \textit{paranoid\textunderscore confirm:AutoAll} to require confirmation.) Partial skips the object class filtering and immediately displays a menu of all objects. -\item[\ib{menu\verb+_+deselect\verb+_+all}] +\item[menu\textunderscore deselect\textunderscore all] Key to deselect all items in a menu. Implemented by the Amiga, Gem, X11 and tty ports. Default `-'. -\item[\ib{menu\verb+_+deselect\verb+_+page}] +\item[menu\textunderscore deselect\textunderscore page] Key to deselect all items on this page of a menu. Implemented by the Amiga, Gem and tty ports. -Default `\verb+\+'. -\item[\ib{menu\verb+_+first\verb+_+page}] +Default `\textbackslash'. +\item[menu\textunderscore first\textunderscore page] Key to jump to the first page in a menu. Implemented by the Amiga, Gem and tty ports. -Default `\verb+^+'. -\item[\ib{menu\verb+_+headings}] +Default `\textasciicircum'. +\item[menu\textunderscore headings] Controls how the headings in a menu are highlighted. Takes a text attribute, or text color and attribute separated by ampersand. -For allowed attributes and colors, see ``{\it Configuring Menu Colors\/}``. +For allowed attributes and colors, see ``\textit{Configuring Menu Colors}``. Not all ports can actually display all types. -\item[\ib{menu\verb+_+invert\verb+_+all}] +\item[menu\textunderscore invert\textunderscore all] Key to invert all items in a menu. Implemented by the Amiga, Gem, X11 and tty ports. Default `@'. -\item[\ib{menu\verb+_+invert\verb+_+page}] +\item[menu\textunderscore invert\textunderscore page] Key to invert all items on this page of a menu. Implemented by the Amiga, Gem and tty ports. -Default `\verb+~+'. -\item[\ib{menu\verb+_+last\verb+_+page}] +Default `\textasciitilde'. +\item[menu\textunderscore last\textunderscore page] Key to jump to the last page in a menu. Implemented by the Amiga, Gem and tty ports. -Default `\verb+|+'. -\item[\ib{menu\verb+_+next\verb+_+page}] +Default `|'. +\item[menu\textunderscore next\textunderscore page] Key to go to the next menu page. Implemented by the Amiga, Gem and tty ports. -Default `\verb+>+'. -\item[\ib{menu\verb+_+objsyms}] +Default `>'. +\item[menu\textunderscore objsyms] % [originally menu_objsyms was a boolean] % Show object symbols in menu headings in menus where % the object symbols act as menu accelerators (default off). @@ -4611,104 +4677,104 @@ objects among classes. Supported by tty and curses. When setting the value, it can be specified by digit or keyword. -The default value is {\tt Conditional} (4). -\item[\ib{menu\verb+_+overlay}] +The default value is \texttt{Conditional} (4). +\item[menu\textunderscore overlay] Do not clear the screen before drawing menus, and align menus to the right edge of the screen. Only for the tty port. (default on) -\item[\ib{menu\verb+_+previous\verb+_+page}] +\item[menu\textunderscore previous\textunderscore page] Key to go to the previous menu page. Implemented by the Amiga, Gem and tty ports. -Default `\verb+<+'. -\item[\ib{menu\verb+_+search}] +Default `<'. +\item[menu\textunderscore search] Key to search for some text and toggle selection state of matching menu items. Default `:'. -\item[\ib{menu\verb+_+select\verb+_+all}] +\item[menu\textunderscore select\textunderscore all] Key to select all items in a menu. Implemented by the Amiga, Gem, X11 and tty ports. Default `.'. -\item[\ib{menu\verb+_+select\verb+_+page}] +\item[menu\textunderscore select\textunderscore page] Key to select all items on this page of a menu. Implemented by the Amiga, Gem and tty ports. Default `,'. %.lp -\item[\ib{menu\verb+_+shift\verb+_+left}] +\item[menu\textunderscore shift\textunderscore left] Key to scroll a menu---one which has been scrolled right---back to the left. -Implemented for {\it perm\verb+_+invent\/} only by curses and X11. -Default `{\tt \verb+{+}'. +Implemented for \textit{perm\textunderscore invent} only by curses and X11. +Default `\texttt{\textbraceleft}'. %.lp -\item[\ib{menu\verb+_+shift\verb+_+right}] +\item[menu\textunderscore shift\textunderscore right] Key to scroll a menu which has text beyond the right edge to the right. -Implemented for {\it perm\verb+_+invent\/} only by curses by X11. -Default `{\tt \verb+}+}'. +Implemented for \textit{perm\textunderscore invent} only by curses by X11. +Default `\texttt{\textbraceright}'. % %.lp -% \item[\ib{menu\verb+_+tab\verb+_+sep}] +% \item[menu\textunderscore tab\textunderscore sep] % Format menu entries using TAB to separate columns (default off). % Only applicable to some menus, and only useful to some interfaces. % Debug mode only. %.lp -\item[\ib{mon\verb+_+movement}] +\item[mon\textunderscore movement] Show a message when hero notices a monster movement (default is off). %.lp -\item[\ib{monpolycontrol}] +\item[monpolycontrol] Prompt for new form whenever any monster changes shape (default off). Debug mode only. %.lp -\item[\ib{montelecontrol}] +\item[montelecontrol] Prompt for destination whenever any monster gets teleported (default off). Debug mode only. %.lp -\item[\ib{mouse\verb+_+support}] +\item[mouse\textunderscore support] Allow use of the mouse for input and travel. Valid settings are: %.sd %.si -{\tt 0} --- disabled\\ -{\tt 1} --- enabled and make OS adjustments to support mouse use\\ -{\tt 2} --- like {\tt 1}, but does not make any OS adjustments\\ +\texttt{0} --- disabled\\ +\texttt{1} --- enabled and make OS adjustments to support mouse use\\ +\texttt{2} --- like \texttt{1}, but does not make any OS adjustments\\ %.ei %.ed -Omitting a value is the same as specifying {\tt 1} +Omitting a value is the same as specifying \texttt{1} and negating -{\it mouse\verb+_+support\/} -is the same as specifying {\tt 0}. +\textit{mouse\textunderscore support} +is the same as specifying \texttt{0}. %.lp -\item[\ib{msghistory}] +\item[msghistory] The number of top line messages to save (and be able to recall -with `{\tt \^{}P}') (default 20). -Cannot be set with the `{\tt O}' command. +with `\texttt{\textasciicircum P}') (default 20). +Cannot be set with the `\texttt{O}' command. %.lp -\item[\ib{msg\verb+_+window}] +\item[msg\textunderscore window] Allows you to change the way recalled messages are displayed. Currently it is only supported for tty (all four choices) and for curses -(`{\tt f}' and `{\tt r}' choices, default `{\tt r}'). +(`\texttt{f}' and `\texttt{r}' choices, default `\texttt{r}'). The possible values are: %.sd %.si -{\tt s} --- single message (default; only choice prior to 3.4.0);\\ -{\tt c} --- combination, two messages as {\it single\/}, then as {\it full\/};\\ -{\tt f} --- full window, oldest message first;\\ -{\tt r} --- full window reversed, newest message first. +\texttt{s} --- single message (default; only choice prior to 3.4.0);\\ +\texttt{c} --- combination, two messages as \textit{single}, then as \textit{full};\\ +\texttt{f} --- full window, oldest message first;\\ +\texttt{r} --- full window reversed, newest message first. %.ei %.ed For backward compatibility, no value needs to be specified (which -defaults to {\it full\/}), or it can be negated (which defaults -to {\it single\/}). +defaults to \textit{full}), or it can be negated (which defaults +to \textit{single}). %.lp -\item[\ib{name}] +\item[name] Set your character's name (defaults to your user name). You can also set your character's role by appending a dash and one or more letters of the role (that is, by suffixing one of -``{\tt -A -B -C -H -K -M -P -Ra -Ro -S -T -V -W}''). -If ``{\tt -@}'' is used for the role, then a random one will be +``\texttt{-A -B -C -H -K -M -P -Ra -Ro -S -T -V -W}''). +If ``\texttt{-@}'' is used for the role, then a random one will be automatically chosen. %.lp \\ @@ -4716,164 +4782,169 @@ On some systems, the default is the player's user name; on others, there is no default and the player will be prompted. The former can made to behave like the latter by specifying a generic name such as ``player''. -Cannot be set with the `{\tt O}' command. +Cannot be set with the `\texttt{O}' command. %.lp -\item[\ib{news}] -Read the {\it NetHack\/} news file, if present (default on). +\item[news] +Read the \textit{NetHack} news file, if present (default on). Since the news is shown at the beginning of the game, there's no point -in setting this with the `{\tt O}' command. +in setting this with the `\texttt{O}' command. %.lp -\item[\ib{nudist}] +\item[nudist] Start the character with no armor (default false). Persistent. %.lp -\item[\ib{null}] +\item[null] Send padding nulls to the terminal (default on). Persistent. %.lp -\item[\ib{number\verb+_+pad}] +\item[number\textunderscore pad] Use digit keys instead of letters to move (default 0 or off).\\ Valid settings are: %.sd %.si \newlength{\mwidth} -\settowidth{\mwidth}{\tt -0} -\newcommand{\numbox}[1]{\makebox[\mwidth][r]{{\tt #1}}} -\numbox{0} --- move by letters; `{\tt yuhjklbn}'\\ -\numbox{1} --- move by numbers; digit `{\tt 5}' acts as `{\tt G}' movement prefix\\ -\numbox{2} --- like {\tt 1} but `{\tt 5}' works as `{\tt g}' prefix instead of as `{\tt G}'\\ -\numbox{3} --- by numbers using phone key layout; {\tt 123} above, {\tt 789} below\\ -\numbox{4} --- combines {\tt 3} with {\tt 2}; phone layout plus MS-DOS compatibility\\ -\numbox{-1} --- by letters but use `{\tt z}' to go northwest, `{\tt y}' to zap wands +\settowidth{\mwidth}{\texttt{-0}} +\newcommand{\numbox}[1]{\makebox[\mwidth][r]{\texttt{#1}}} +\numbox{0} --- move by letters; `\texttt{yuhjklbn}'\\ +\numbox{1} --- move by numbers; digit `\texttt{5}' acts as `\texttt{G}' movement prefix\\ +\numbox{2} --- like \texttt{1} but `\texttt{5}' works as `\texttt{g}' prefix instead of as `\texttt{G}'\\ +\numbox{3} --- by numbers using phone key layout; \texttt{123} above, \texttt{789} below\\ +\numbox{4} --- combines \texttt{3} with \texttt{2}; phone layout plus MS-DOS compatibility\\ +\numbox{-1} --- by letters but use `\texttt{z}' to go northwest, `\texttt{y}' to zap wands %.ei %.ed -For backward compatibility, omitting a value is the same as specifying {\tt 1} +For backward compatibility, omitting a value is the same as specifying \texttt{1} and negating -{\it number\verb+_+pad\/} -is the same as specifying {\tt 0}. -(Settings {\tt 2} and {\tt 4} are for compatibility with MS-DOS or old PC Hack; -in addition to the different behavior for `{\tt 5}', `{\tt Alt-5}' acts as `{\tt G}' -and `{\tt Alt-0}' acts as `{\tt I}'. -Setting {\tt -1} is to accommodate some QWERTZ keyboards which have the -location of the `{\tt y}' and `{\tt z}' keys swapped.) +\textit{number\textunderscore pad} +is the same as specifying \texttt{0}. +(Settings \texttt{2} and \texttt{4} are for compatibility with MS-DOS or old PC Hack; +in addition to the different behavior for `\texttt{5}', `\texttt{Alt-5}' acts as `\texttt{G}' +and `\texttt{Alt-0}' acts as `\texttt{I}'. +Setting \texttt{-1} is to accommodate some QWERTZ keyboards which have the +location of the `\texttt{y}' and `\texttt{z}' keys swapped.) When moving by numbers, to enter a count prefix for those commands -which accept one (such as ``{\tt 12s}'' to search twelve times), precede it -with the letter `{\tt n}' (``{\tt n12s}''). +which accept one (such as ``\texttt{12s}'' to search twelve times), precede it +with the letter `\texttt{n}' (``\texttt{n12s}''). %.lp -\item[\ib{packorder}] +\item[packorder] Specify the order to list object types in (default -``\verb&")[%?+!=/(*`0_&''). The value of this option should be a string +``\texttt{")[\%?+!=/(*\textasciigrave 0\textunderscore}''). The value of this option should be a string containing the symbols for the various object types. Any omitted types are filled in at the end from the previous order. %.lp -\item[\ib{paranoid\verb+_+confirmation}] +\item[paranoid\textunderscore confirmation] A space separated list of specific situations where alternate prompting is desired. -The default is ``{\it paranoid\verb+_+confirmation:pray swim trap}''. +The default is ``\textit{paranoid\textunderscore confirmation:pray swim trap}''. %.sd %.si \newlength{\pcwidth} -\settowidth{\pcwidth}{\tt Were-change} +\settowidth{\pcwidth}{\texttt{Were-change}} \addtolength{\pcwidth}{\labelsep} -\blist{\leftmargin \pcwidth \topsep 1mm \itemsep 0mm} -\item[{\tt Confirm}] +\begin{description}[leftmargin=\pcwidth, topsep=1mm, itemsep=0mm, labelwidth=*, font=\ttfamily, align=right] +\item[Confirm] for any prompts which are set to require ``yes'' rather than `y', also require ``no'' to reject instead of accepting any non-yes response as no; changes pray and AutoAll to require ``yes'' or ``no'' too; -\item[{\tt quit~~~}] -require ``{\tt yes}'' rather than `{\tt y}' to confirm quitting +\item[quit] +require ``\texttt{yes}'' rather than `\texttt{y}' to confirm quitting the game or switching into non-scoring explore mode; -\item[{\tt die~~~~}] -require ``{\tt yes}'' rather than `{\tt y}' to confirm dying (not +\item[die] +require ``\texttt{yes}'' rather than `\texttt{y}' to confirm dying (not useful in normal play; applies to explore mode); -\item[{\tt bones~~}] -require ``{\tt yes}'' rather than `{\tt y}' to confirm saving +\item[bones] +require ``\texttt{yes}'' rather than `\texttt{y}' to confirm saving bones data when dying in debug mode -\item[{\tt attack~}] -require ``{\tt yes}'' rather than `{\tt y}' to confirm attacking +\item[attack] +require ``\texttt{yes}'' rather than `\texttt{y}' to confirm attacking a peaceful monster; -\item[{\tt wand-break}] -require ``{\tt yes}'' rather than `{\tt y}' to confirm breaking -a wand with the {\it apply} command; -\item[{\tt eating~}] -require ``{\tt yes}'' rather than `{\tt y}' to confirm whether to +\item[wand-break] +require ``\texttt{yes}'' rather than `\texttt{y}' to confirm breaking +a wand with the \textit{apply} command; +\item[eating] +require ``\texttt{yes}'' rather than `\texttt{y}' to confirm whether to continue eating; -\item[{\tt Were-change}] -require ``{\tt yes}'' rather than `{\tt y}' to confirm changing form due +\item[Were-change] +require ``\texttt{yes}'' rather than `\texttt{y}' to confirm changing form due to lycanthropy when hero has polymorph control; -\item[{\tt pray~~~}] -require `{\tt y}' to confirm an attempt to pray rather +\item[pray] +require `\texttt{y}' to confirm an attempt to pray rather than immediately praying; on by default; (to require ``yes'' rather than just `y', set Confirm too); -\item[{\tt trap~~~}] -require `{\tt y}' to confirm an attempt to move into or onto a known trap, +\item[trap] +require `\texttt{y}' to confirm an attempt to move into or onto a known trap, unless doing so is considered to be harmless; when enabled, this confirmation is also used for moving into visible gas cloud regions; (to require ``yes'' rather than just `y', set Confirm too); -confirmation can be skipped by using the `{\tt m}' movement prefix; -\item[{\tt swim~~~}] +confirmation can be skipped by using the `\texttt{m}' movement prefix; +\item[swim] prevent walking into water or lava; on by default; (to deliberately step -onto/into such terrain when this is set, use the `{\tt m}' +onto/into such terrain when this is set, use the `\texttt{m}' movement prefix when adjacent); -\item[{\tt AutoAll}] +\item[AutoAll] require confirmation when the `A' (Autoselect-All) choice is selected -in object class filtering menus for {\it menustyle:Full}; +in object class filtering menus for \textit{menustyle:Full}; (to require ``yes'' rather than just `y', set Confirm too); -\item[{\tt Remove~}] -require selection from inventory for `{\tt R}' and `{\tt T}' +\item[Remove] +require selection from inventory for `\texttt{R}' and `\texttt{T}' commands even when wearing just one applicable item; -\item[{\tt all~~~~}] +\item[all] turn on all of the above. -\elist +\end{description} %.ei %.ed By default, the pray, swim, and trap choices are enabled, the others disabled. To disable them without setting -any of the other choices, use ``{\it paranoid\verb+_+confirmation:none}''. +any of the other choices, use ``\textit{paranoid\textunderscore confirmation:none}''. To keep them enabled while setting any of the others, you can include them in the list, such as -``{\it par\-a\-noid\verb+_+con\-fir\-ma\-tion:attack~pray~swim~Remove\/}'' +``\textit{par\-a\-noid\textunderscore con\-fir\-ma\-tion:attack~pray~swim~Remove}'' or you can precede the first entry in the list with a plus sign, -``{\it paranoid\verb+_+confirmation:\verb|+|attack~Remove\/}''. +``\textit{paranoid\textunderscore confirmation:+attack~Remove}''. To remove an entry that has been previously set without removing others, precede the first entry in the list with a minus sign, -``{\it paranoid\verb+_+confirmation:-swim\/}. +``\textit{paranoid\textunderscore confirmation:-swim}. To both add some new entries and remove some old ones, you can use -multiple {\it paranoid\verb+_+confirmation\/} option settings, or you can -use the `{\tt \verb|+|}' form and list entries to be added by their name -and entries to be removed by `{\tt !}' and name. +multiple \textit{paranoid\textunderscore confirmation} option settings, or you can +use the `\texttt{+}' form and list entries to be added by their name +and entries to be removed by `\texttt{!}' and name. The positive (no `!') and negative (with `!') entries can be intermixed. %.lp -\item[\ib{pauper}] +\item[pauper] Start the character with no possessions (default false). Persistent. +%.lp "" +\\ +Also start with no spells or skills, which are tied to starting equipment. +Does not inhibit acquiring and using items, spells, and skills once play +has started. %.lp -\item[\ib{perm\verb+_+invent}] +\item[perm\textunderscore invent] If true, always display your current inventory in a window (default is false). %.lp "" \\ This only makes sense for windowing system interfaces that implement this feature. For those that do, the -{\tt perminv\verb+_+mode} +\texttt{perminv\textunderscore mode} option can be used to refine what gets displayed -for {\it perm\verb+_+invent\/}. -Setting that to a value other than {\it none\/} -while {\it perm\verb+_+invent\/} is false will change it to true. +for \textit{perm\textunderscore invent}. +Setting that to a value other than \textit{none} +while \textit{perm\textunderscore invent} is false will change it to true. %.lp -\item[\ib{perminv\verb+_+mode}] +\item[perminv\textunderscore mode] Augments the -{\tt perm\verb+_+invent} +\texttt{perm\textunderscore invent} option. Value is one of %.PS "\f(CRin-use\fP" -\settowidth{\pcwidth}{\tt in-use} %reuse the paranoid_confirm width +\settowidth{\pcwidth}{\texttt{in-use}} %reuse the paranoid_confirm width \addtolength{\pcwidth}{\labelsep} -\blist{\leftmargin \pcwidth \topsep 1mm \itemsep 0mm} +\begin{description}[leftmargin=\pcwidth, topsep=1mm, itemsep=0mm, labelwidth=*, font=\mdseries, align=right] %.PL -\item[{\tt none}] -behave as if {\it perm\verb+_+invent\/} is false; +\item[\texttt{none}] +behave as if \textit{perm\textunderscore invent} is false; \item[{all}] show all inventory except for gold; \item[{full}] @@ -4881,81 +4952,81 @@ show full inventory including gold; \item[{in-use}] only show items which are in use (worn, wielded, lit lamp). %.PE -\elist -Default is {\it none\/} but if {\it perm\verb+_+invent\/} gets set to true -while it is {\it none\/} it will be changed to {\it all\/}. +\end{description} +Default is \textit{none} but if \textit{perm\textunderscore invent} gets set to true +while it is \textit{none} it will be changed to \textit{all}. %.lp "" \\ Note: if gold has been equipped in quiver/ammo-pouch then it will be -included for {\it all\/} despite that mode normally omitting gold. +included for \textit{all} despite that mode normally omitting gold. %.lp %.\" petattr is a wincap option but we'll document it here... -\item[\ib{petattr}] +\item[petattr] Specifies one or more text highlighting attributes to use when showing pets on the map. -Effectively a superset of the {\it hilite\verb+_+pet\/} boolean option. +Effectively a superset of the \textit{hilite\textunderscore pet} boolean option. Curses or tty interface only; value is one of none, bold, dim, underline, italic, blink, and inverse. Some of those choices might not work, depending upon terminal hardware or terminal emulation software. %.lp -\item[\ib{pettype}] +\item[pettype] Specify the type of your initial pet, if you are playing a character class that uses multiple types of pets; or choose to have no initial pet at all. -Possible values are ``{\tt cat}'', ``{\tt dog}'', ``{\tt horse}'' -and ``{\tt none}''. +Possible values are ``\texttt{cat}'', ``\texttt{dog}'', ``\texttt{horse}'' +and ``\texttt{none}''. If the choice is not allowed for the role you are currently playing, -it will be silently ignored. For example, ``{\tt horse}'' will only be +it will be silently ignored. For example, ``\texttt{horse}'' will only be honored when playing a knight. -Cannot be set with the `{\tt O}' command. +Cannot be set with the `\texttt{O}' command. %.lp -\item[\ib{pickup\verb+_+burden}] +\item[pickup\textunderscore burden] When you pick up an item that would exceed this encumbrance level (Unencumbered, Burdened, streSsed, straiNed, overTaxed, or overLoaded), you will be asked if you want to continue. (Default `S'). Persistent. %.lp -\item[\ib{pickup\verb+_+stolen}] -If this option is on and ``{\it autopickup\/}'' is also on, try to pick up +\item[pickup\textunderscore stolen] +If this option is on and ``\textit{autopickup}'' is also on, try to pick up things that a monster stole from you, even if they aren't in -``{\it pickup\verb+_+types\/}'' or +``\textit{pickup\textunderscore types}'' or match an autopickup exception. Default is on. Persistent. %.lp -\item[\ib{pickup\verb+_+thrown}] -If this option is on and ``{\it autopickup\/}'' is also on, try to pick up +\item[pickup\textunderscore thrown] +If this option is on and ``\textit{autopickup}'' is also on, try to pick up things that you threw, even if they aren't in -``{\it pickup\verb+_+types\/}'' or +``\textit{pickup\textunderscore types}'' or match an autopickup exception. Default is on. Persistent. %.lp -\item[\ib{pickup\verb+_+types}] -Specify the object types to be picked up when ``{\it autopickup\/}'' +\item[pickup\textunderscore types] +Specify the object types to be picked up when ``\textit{autopickup}'' is on. Default is all types. Persistent. \\ %.lp "" The value is a list of object symbols, such as -{\tt \verb&pickup_types:$?!&} to pick up gold, scrolls, and potions. +\texttt{pickup\textunderscore types:\textdollar ?!\&} to pick up gold, scrolls, and potions. You can use -``{\it autopickup\verb+_+exception\/}'' -configuration file lines to further refine ``{\it autopickup\/}'' behavior. +``\textit{autopickup\textunderscore exception}'' +configuration file lines to further refine ``\textit{autopickup}'' behavior. \\ %.lp "" -There is no way to set {\it pickup\verb+_+types\/} to ``{\it none}''. -(Setting it to an empty value reverts to ``{\it all}''.) +There is no way to set \textit{pickup\textunderscore types} to ``\textit{none}''. +(Setting it to an empty value reverts to ``\textit{all}''.) If you want to avoid automatically picking up any types of items but do -want to have {\it autopickup\/} on in order to have -{\it autopickup\verb+_+exceptions\/} control what you do and don't pick -up, you can set {\it pickup\verb+_+types\/} to `{\tt .}'. -That is the type symbol for {\it venom\/} and you won't come across +want to have \textit{autopickup} on in order to have +\textit{autopickup\textunderscore exceptions} control what you do and don't pick +up, you can set \textit{pickup\textunderscore types} to `\texttt{.}'. +That is the type symbol for \textit{venom} and you won't come across any venom items so won't unintentionally pick such up. %.lp -\item[\ib{pile\verb+_+limit}] +\item[pile\textunderscore limit] When walking across a pile of objects on the floor, threshold at which the message ``there are few/several/many objects here'' is given instead of showing a popup list of those objects. A value of 0 means ``no limit'' @@ -4963,8 +5034,8 @@ of showing a popup list of those objects. A value of 0 means ``no limit'' the objects'' since the pile size will always be at least that big; default value is 5. Persistent. %.lp -\item[\ib{playmode}] -Values are {\it normal\/}, {\it explore\/}, or {\it debug\/}. +\item[playmode] +Values are \textit{normal}, \textit{explore}, or \textit{debug}. Allows selection of explore mode (also known as discovery mode) or debug mode (also known as wizard mode) instead of normal play. Debug mode might only be allowed for someone logged in under a particular @@ -4973,16 +5044,16 @@ name (on single-user systems) or it might be disabled entirely. Requesting it when not allowed or not possible results in explore mode instead. Default is normal play. %.lp -\item[\ib{pushweapon}] -Using the `{\tt w}' (wield) command when already wielding +\item[pushweapon] +Using the `\texttt{w}' (wield) command when already wielding something pushes the old item into your alternate weapon slot (default off). -Likewise for the `{\tt a}' (apply) command if it causes the applied item to +Likewise for the `\texttt{a}' (apply) command if it causes the applied item to become wielded. Persistent. %.lp -\item[\ib{query\verb+_+menu}] +\item[query\textunderscore menu] Use a menu when asked specific yes/no queries, instead of a prompt. %.lp -\item[\ib{quick\verb+_+farsight}] +\item[quick\textunderscore farsight] When set, usually prevents the ``you sense your surroundings'' message where play pauses to allow you to browse the map whenever clairvoyance randomly activates. @@ -4991,36 +5062,36 @@ It does not affect the clairvoyance spell where pausing to examine revealed objects or monsters is less intrusive. Default is off. Persistent. %.lp -\item[\ib{race}] -Choices are {\tt human}, {\tt dwarf}, {\tt elf}, {\tt gnome}, and -{\tt orc} but most roles restrict which of the non-human races are allowed. -See {\it role\/} +\item[race] +Choices are \texttt{human}, \texttt{dwarf}, \texttt{elf}, \texttt{gnome}, and +\texttt{orc} but most roles restrict which of the non-human races are allowed. +See \textit{role} for a description of how to use negation to exclude choices. %.lp "" \\ -If {\tt race} is not specified, there is no default value; +If \texttt{race} is not specified, there is no default value; player will be prompted unless role forces a choice for race. unless role forces a choice for race. -Cannot be set with the `{\tt O}' command. Persistent. +Cannot be set with the `\texttt{O}' command. Persistent. %.lp -\item[\ib{rest\verb+_+on\verb+_+space}] -Make the space bar a synonym for the `{\tt .}' (\#wait) command (default off). +\item[rest\textunderscore on\textunderscore space] +Make the space bar a synonym for the `\texttt{.}' (\#wait) command (default off). Persistent. %.lp -\item[\ib{role}] -Pick your type of character (for example, ``{\tt role:Samurai}''); -synonym for ``{\it character\/}''. -See ``{\it name\/}'' for an alternate method of specifying your role. +\item[role] +Pick your type of character (for example, ``\texttt{role:Samurai}''); +synonym for ``\textit{character}''. +See ``\textit{name}'' for an alternate method of specifying your role. %.\" Normally only the first letter of the -%.\" value is examined; `r' is an exception with ``{\tt Rogue}'', -%.\" ``{\tt Ranger}'', and ``{\tt random}'' values. +%.\" value is examined; `r' is an exception with ``\texttt{Rogue}'', +%.\" ``\texttt{Ranger}'', and ``\texttt{random}'' values. %.lp "" This option can also be used to limit selection when role is chosen randomly. Use a space-separated list of roles and either negate each one or negate the option itself instead. -Negation is accomplished in the same manner as with {\it boolean options\/}, -by prefixing the option or its value(s) with `{\tt \verb+!+}' or ``{\tt no}''. +Negation is accomplished in the same manner as with \textit{boolean options}, +by prefixing the option or its value(s) with `\texttt{!}' or ``\texttt{no}''. %.BR 0 \\ Examples: @@ -5029,123 +5100,123 @@ Examples: OPTIONS=role:!arc !bar !kni OPTIONS=!role:arc bar kni \end{verbatim} -There can be multiple instances of the {\it role\/} +There can be multiple instances of the \textit{role} option if they're all negations. %.\" Only one positive value is allowed, and if present, it overrides any %.\" negations. %.lp "" \\ -If {\tt role} is not specified, there is no default value; +If \texttt{role} is not specified, there is no default value; player will be prompted. -Cannot be set with the `{\tt O}' command. Persistent. +Cannot be set with the `\texttt{O}' command. Persistent. %.lp -\item[\ib{roguesymset}] +\item[roguesymset] This option may be used to select one of the named symbol sets found within -{\tt symbols} to alter the symbols displayed on the screen on the +\texttt{symbols} to alter the symbols displayed on the screen on the rogue level. %.lp -\item[\ib{rlecomp}] +\item[rlecomp] When writing out a save file, perform run length compression of the map. Not all ports support run length compression. It has no effect on reading an existing save file. %.lp -\item[\ib{runmode}] +\item[runmode] Controls the amount of screen updating for the map window when engaged -in multi-turn movement (running via {\tt shift}+direction -or {\tt control}+direction +in multi-turn movement (running via \texttt{shift}+direction +or \texttt{control}+direction and so forth, or via the travel command or mouse click). The possible values are: %.sd %.si -{\tt teleport} --- update the map after movement has finished;\\ -{\tt run} --- update the map after every seven or so steps;\\ -{\tt walk} --- update the map after each step;\\ -{\tt crawl} --- like {\it walk\/}, but pause briefly after each step. +\texttt{teleport} --- update the map after movement has finished;\\ +\texttt{run} --- update the map after every seven or so steps;\\ +\texttt{walk} --- update the map after each step;\\ +\texttt{crawl} --- like \textit{walk}, but pause briefly after each step. %.ei %.ed This option only affects the game's screen display, not the actual -results of moving. The default is {\it run\/}; versions prior to 3.4.1 -used {\it teleport\/} only. Whether or not the effect is noticeable will +results of moving. The default is \textit{run}; versions prior to 3.4.1 +used \textit{teleport} only. Whether or not the effect is noticeable will depend upon the window port used or on the type of terminal. Persistent. %.lp -\item[\ib{safe\verb+_+pet}] +\item[safe\textunderscore pet] Prevent you from (knowingly) attacking your pets (default on). Persistent. %.lp -\item[\ib{safe\verb+_+wait}] +\item[safe\textunderscore wait] Prevents you from waiting or searching when next to a hostile monster (default on). Persistent. %.lp -\item[\ib{sanity\verb+_+check}] +\item[sanity\textunderscore check] Evaluate monsters, objects, and map prior to each turn (default off). Debug mode only. %.lp -\item[\ib{scores}] +\item[scores] Control what parts of the score list you are shown at the end (for example, -``{\tt scores:5top scores/4around my score/own scores}''). Only the first -letter of each category (`{\tt t}', `{\tt a}' or `{\tt o}') is necessary. +``\texttt{scores:5top scores/4around my score/own scores}''). Only the first +letter of each category (`\texttt{t}', `\texttt{a}' or `\texttt{o}') is necessary. Persistent. %.lp -\item[\ib{showdamage}] +\item[showdamage] Whenever your character takes damage, show a message of the damage taken, and the amount of hit points left. %.lp -\item[\ib{showexp}] +\item[showexp] Show your accumulated experience points on bottom line (default off). Persistent. %.lp -\item[\ib{showrace}] +\item[showrace] Display yourself as the glyph for your race, rather than the glyph for your role (default off). Note that this setting affects only the appearance of the display, not the way the game treats you. Persistent. %.lp -\item[\ib{showscore}] +\item[showscore] Show your approximate accumulated score on bottom line (default off). By default, this feature is suppressed when building the program. Persistent. %.lp -\item[\ib{showvers}] +\item[showvers] Include the game's version number on the status lines (default off). Potentially useful if you switch between different versions or variants, or you are making screenshots or streaming video. Using the -{\it statuslines:3\/} +\textit{statuslines:3} option is recommended so that there will be more room available for -status information, unless you're using nethack's {\it Qt\/} interface +status information, unless you're using NetHack's \textit{Qt} interface or your terminal emulator window displays fewer than 25 lines. Persistent. %.lp -\item[\ib{silent}] +\item[silent] Suppress terminal beeps (default on). Persistent. %.lp -\item[\ib{sortdiscoveries}] -Controls the sorting behavior for the output of the `{\tt $\backslash$}' -and `{\tt \`{}}' commands. +\item[sortdiscoveries] +Controls the sorting behavior for the output of the `\texttt{\textbackslash}' +and `\texttt{\`{}}' commands. Persistent. \\ %.lp "" The possible values are: \\ %.PS -{\tt o} --- list object types by class, in discovery order within each class; +\texttt{o} --- list object types by class, in discovery order within each class; default; \\ -{\tt s} --- list object types by {\it sortloot\/} +\texttt{s} --- list object types by \textit{sortloot} classification: by class, by sub-class within class for classes which have substantial groupings (like helmets, boots, gloves, and so forth for armor), with object types partly-discovered via assigned name coming before fully identified types; \\ -{\tt c} --- list by class, alphabetically within each class;\\ -{\tt a} --- list alphabetically across all classes.\\ +\texttt{c} --- list by class, alphabetically within each class;\\ +\texttt{a} --- list alphabetically across all classes.\\ %.PE -Can be interactively set via the `{\tt O}' command or via using -the `{\tt m}' prefix before the `{\tt $\backslash$}' -or `{\tt \`{}}' command. +Can be interactively set via the `\texttt{O}' command or via using +the `\texttt{m}' prefix before the `\texttt{\textbackslash}' +or `\texttt{\textasciigrave}' command. %.lp -\item[\ib{sortloot}] +\item[sortloot] Controls the sorting behavior of pickup lists for inventory and \#loot commands and some others. Persistent. \\ @@ -5153,132 +5224,132 @@ The possible values are: \\ %.sd %.si -{\tt full} --- always sort the lists;\\ -{\tt loot} --- only sort the lists that don't use inventory +\texttt{full} --- always sort the lists;\\ +\texttt{loot} --- only sort the lists that don't use inventory letters, like with the \#loot and pickup commands;\\ -{\tt none} --- show lists the traditional way without sorting; default. +\texttt{none} --- show lists the traditional way without sorting; default. %.ei %.ed %.lp -\item[\ib{sortpack}] +\item[sortpack] Sort the pack contents by type when displaying inventory (default on). Persistent. %.lp -\item[\tb{sortvanquished}] -Controls the sorting behavior for the output of the {\tt \#vanquished} command -and also for the {\tt \#genocided} command. +\item[sortvanquished] +Controls the sorting behavior for the output of the \texttt{\#vanquished} command +and also for the \texttt{\#genocided} command. Persistent. \\ %.lp "" The possible values are: \\ %.PS -{\tt t} --- +\texttt{t} --- traditional: order by monster level; ties are broken by internal monster index; default; \\ -{\tt d} --- +\texttt{d} --- order by monster difficulty rating; ties broken by internal index; \\ -{\tt a} --- +\texttt{a} --- order alphabetically, first any unique monsters then all the others; \\ %note: 'A' and 'C' can be set in RC file or NETHACKOPTIONS but not by 'O' -% {\tt A} --- +% \texttt{A} --- % order alphabetically, unique monsters intermixed with other monsters; % \\ -% {\tt C} --- +% \texttt{C} --- % order by monster class, by high to low level within each class; % \\ -{\tt c} --- +\texttt{c} --- order by monster class, by low to high level within each class; \\ -{\tt n} --- +\texttt{n} --- order by count, high to low; ties are broken by internal monster index; \\ -{\tt z} --- +\texttt{z} --- order by count, low to high; ties broken by internal index. \\ %.PE -Can be interactively set via the `{\tt m O}' command or via using -the `{\tt m}' prefix before either the {\tt \#vanquished} command -or the {\tt \#genocided} command. +Can be interactively set via the `\texttt{m O}' command or via using +the `\texttt{m}' prefix before either the \texttt{\#vanquished} command +or the \texttt{\#genocided} command. %.lp -\item[\ib{sounds}] +\item[sounds] Allow sounds to be emitted from an integrated sound library (default on). %.lp -\item[\ib{sparkle}] +\item[sparkle] Display a sparkly effect when a monster (including yourself) is hit by an attack to which it is resistant (default on). Persistent. %.lp -\item[\ib{spot\verb+_+monsters}] +\item[spot\textunderscore monsters] Show a message when hero notices a monster (default is off). %.lp -\item[\ib{standout}] -Boldface monsters and ``{\tt --More--}'' (default off). Persistent. +\item[standout] +Boldface monsters and ``\texttt{--More--}'' (default off). Persistent. %.lp -\item[\ib{statushilites}] +\item[statushilites] Controls how many turns status hilite behaviors highlight the field. If negated or set to zero, disables status hiliting. -See ``{\it Configuring Status Hilites\/}'' for further information. +See ``\textit{Configuring Status Hilites}'' for further information. %.lp -\item[\ib{status\verb+_+updates}] +\item[status\textunderscore updates] Allow updates to the status lines at the bottom of the screen (default true). %.lp -\item[\ib{suppress\verb+_+alert}] -This option may be set to a {\it NetHack\/} version level to suppress +\item[suppress\textunderscore alert] +This option may be set to a \textit{NetHack} version level to suppress alert notification messages about feature changes for that -and prior versions (for example, ``{\tt suppress\verb+_+alert:3.3.1}'') +and prior versions (for example, ``\texttt{suppress\textunderscore alert:3.3.1}'') %.lp -\item[\ib{symset}] +\item[symset] This option may be used to select one of the named symbol sets found within -{\tt symbols} to alter the symbols displayed on the screen. -Use ``{\tt symset:default}'' to explicitly select the default symbols. +\texttt{symbols} to alter the symbols displayed on the screen. +Use ``\texttt{symset:default}'' to explicitly select the default symbols. %.lp -\item[\ib{time}] +\item[time] Show the elapsed game time in turns on bottom line (default off). Persistent. %.lp -\item[\ib{timed\verb+_+delay}] +\item[timed\textunderscore delay] When pausing momentarily for display effect, such as with explosions and moving objects, use a timer rather than sending extra characters to the screen. (Applies to ``tty'' and ``curses'' interfaces only; ``X11'' interface always uses a timer-based delay. The default is on if configured into the program.) Persistent. %.lp -\item[\ib{tips}] +\item[tips] Show some helpful tips during gameplay (default on). Persistent. %.lp -\item[\ib{tombstone}] +\item[tombstone] Draw a tombstone graphic upon your death (default on). Persistent. %.lp -\item[\ib{toptenwin}] -Put the ending display in a {\it NetHack\/} window instead of on stdout (default off). +\item[toptenwin] +Put the ending display in a \textit{NetHack} window instead of on stdout (default off). Setting this option makes the score list visible when a windowing version -of {\it NetHack\/} is started without a parent window, but it no longer leaves +of \textit{NetHack} is started without a parent window, but it no longer leaves the score list around after game end on a terminal or emulating window. %.lp -\item[\ib{travel}] +\item[travel] Allow the travel command via mouse click (default on). Turning this option off will prevent the game from attempting unintended moves if you make inadvertent mouse clicks on the map window. -Does not affect traveling via the `{\tt \verb+_+}' (``{\tt \#travel}'') +Does not affect traveling via the `\texttt{\textunderscore}' (``\texttt{\#travel}'') command. Persistent. % %.lp -% \item[ib{travel\verb+_+debug}] +% \item[ib{travel\textunderscore debug}] % Display intended path during each step of travel (default off). % Debug mode only. %.lp -\item[\ib{tutorial}] +\item[tutorial] Play a tutorial level at the start of the game. Setting this option on or off in the config file will skip the query. %.lp -\item[\ib{verbose}] +\item[verbose] Provide more commentary during the game (default on). Persistent. %.lp -\item[\ib{whatis\verb+_+coord}] -When using the `{\tt /}' or `{\tt ;}' commands to look around on the map with -``{\tt autodescribe}'' +\item[whatis\textunderscore coord] +When using the `\texttt{/}' or `\texttt{;}' commands to look around on the map with +``\texttt{autodescribe}'' on, display coordinates after the description. Also works in other situations where you are asked to pick a location.\\ @@ -5287,23 +5358,23 @@ The possible settings are: %.sd %.si -{\tt c} --- \verb#compass ('east' or '3s' or '2n,4w')#;\\ -{\tt f} --- \verb#full compass ('east' or '3south' or '2north,4west')#;\\ -{\tt m} --- \verb#map (map column x=0 is not used)#;\\ -{\tt s} --- \verb#screen [row,column] (row is offset to match tty usage)#;\\ -{\tt n} --- \verb#none (no coordinates shown) [default]#. +\texttt{c} --- \texttt{compass ('east' or '3s' or '2n,4w')};\\ +\texttt{f} --- \texttt{full compass ('east' or '3south' or '2north,4west')};\\ +\texttt{m} --- \texttt{map (map column x=0 is not used)};\\ +\texttt{s} --- \texttt{screen [row,column] (row is offset to match tty usage)};\\ +\texttt{n} --- \texttt{none (no coordinates shown) [default]}. %.ei %.ed %.lp "" The -{\it whatis\verb+_+coord\/} +\textit{whatis\textunderscore coord} option is also used with -the `{\tt /m}', `{\tt /M}', `{\tt /o}', and `{\tt /O}' sub-commands -of `{\tt /}', -where the `{\it none\/}' setting is overridden with `{\it map}'. +the `\texttt{/m}', `\texttt{/M}', `\texttt{/o}', and `\texttt{/O}' sub-commands +of `\texttt{/}', +where the `\textit{none}' setting is overridden with `\textit{map}'. %.lp -\item[\ib{whatis\verb+_+filter}] +\item[whatis\textunderscore filter] When getting a location on the map, and using the keys to cycle through next and previous targets, allows filtering the possible targets. (default none)\\ @@ -5312,9 +5383,9 @@ The possible settings are: %.sd %.si -{\tt n} --- \verb#no filtering#;\\ -{\tt v} --- \verb#in view only#;\\ -{\tt a} --- \verb#in same area (room, corridor, etc)#. +\texttt{n} --- \texttt{no filtering};\\ +\texttt{v} --- \texttt{in view only};\\ +\texttt{a} --- \texttt{in same area (room, corridor, etc)}. %.ei %.ed %.lp "" @@ -5326,22 +5397,22 @@ the door you were last moving towards.\\ Filtering can also be changed when getting a location with the ``getpos.filter'' key. %.lp -\item[\ib{whatis\verb+_+menu}] +\item[whatis\textunderscore menu] When getting a location on the map, and using a key to cycle through next and previous targets, use a menu instead to pick a target. (default off) %.lp -\item[\ib{whatis\verb+_+moveskip}] +\item[whatis\textunderscore moveskip] When getting a location on the map, and using shifted movement keys or meta-digit keys to fast-move, instead of moving 8 units at a time, move by skipping the same glyphs. (default off) %.lp -\item[\ib{windowtype}] +\item[windowtype] When the program has been built to support multiple interfaces, -select whichone to use, such as ``{\tt tty}'' or ``{\tt X11}'' -(default depends on build-time settings; use ``{\tt \#version}'' to check). -Cannot be set with the `{\tt O}' command. +select whichone to use, such as ``\texttt{tty}'' or ``\texttt{X11}'' +(default depends on build-time settings; use ``\texttt{\#version}'' to check). +Cannot be set with the `\texttt{O}' command. %.lp "" When used, it should be the first option set since its value might @@ -5349,17 +5420,17 @@ enable or disable the availability of various other options. For multiple lines in a configuration file, that would be the first non-comment line. For a comma-separated list in NETHACKOPTIONS or an OPTIONS line in a -configuration file, that would be the {\it rightmost\/} option in the list. +configuration file, that would be the \textit{rightmost} option in the list. %.lp -\item[\ib{wizweight}] +\item[wizweight] Augment object descriptions with their objects' weight (default off). Debug mode only. %.lp -\item[\ib{zerocomp}] +\item[zerocomp] When writing out a save file, perform zero-comp compression of the contents. Not all ports support zero-comp compression. It has no effect on reading an existing save file. -\elist +\end{description} %.hn 2 \subsection*{Window Port Customization options} @@ -5377,194 +5448,194 @@ can't it will silently ignore it. You can find out if an option is supported by the window port that you are currently using by checking to see if it shows up in the Options list. Some options are dynamic and can be specified during the game -with the `{\tt O}' command. +with the `\texttt{O}' command. -\blist{} +\begin{description}[font=\mdseries\itshape] %.lp -\item[\ib{align\verb+_+message}] +\item[align\textunderscore message] Where to align or place the message window (top, bottom, left, or right) %.lp -\item[\ib{align\verb+_+status}] +\item[align\textunderscore status] Where to align or place the status window (top, bottom, left, or right). %.lp -\item[\ib{ascii\verb+_+map}] +\item[ascii\textunderscore map] %.hw DECgraphics IBMgraphics \% don't hyphenate these \hyphenation{DECgraphics IBMgraphics} -If {\it NetHack\/} can, it should display the map using simple -characters (letters and punctuation) rather than {\it tiles\/} graphics. +If \textit{NetHack} can, it should display the map using simple +characters (letters and punctuation) rather than \textit{tiles} graphics. In some cases, characters can be augmented with line-drawing symbols; -use the {\tt symset} -option to select a symbol set such as {\it DECgraphics\/} -or {\it IBMgraphics\/} if your display supports them. -Setting {\tt ascii\verb+_+map} to {\it True\/} forces -{\tt tiled\verb+_+map} to be {\it False}. +use the \texttt{symset} +option to select a symbol set such as \textit{DECgraphics} +or \textit{IBMgraphics} if your display supports them. +Setting \texttt{ascii\textunderscore map} to \textit{True} forces +\texttt{tiled\textunderscore map} to be \textit{False}. %.lp -\item[\ib{color}] -If {\it NetHack\/} can, it should display color for different monsters, +\item[color] +If \textit{NetHack} can, it should display color for different monsters, objects, and dungeon features (default on). %.lp -\item[\ib{eight\verb+_+bit\verb+_+tty}] -If {\it NetHack\/} can, it should pass eight-bit character values (for example, specified with the -{\it traps \/} option) straight through to your terminal (default off). +\item[eight\textunderscore bit\textunderscore tty] +If \textit{NetHack} can, it should pass eight-bit character values (for example, specified with the +\textit{traps } option) straight through to your terminal (default off). %.lp -\item[\ib{font\verb+_+map}] -If {\it NetHack\/} can, it should use a font by the chosen name for the +\item[font\textunderscore map] +If \textit{NetHack} can, it should use a font by the chosen name for the map window. %.lp -\item[\ib{font\verb+_+menu}] -If {\it NetHack\/} can, it should use a font by the chosen name for menu +\item[font\textunderscore menu] +If \textit{NetHack} can, it should use a font by the chosen name for menu windows. %.lp -\item[\ib{font\verb+_+message}] -If {\it NetHack\/} can, it should use a font by the chosen name for the message window. +\item[font\textunderscore message] +If \textit{NetHack} can, it should use a font by the chosen name for the message window. %.lp -\item[\ib{font\verb+_+status}] -If {\it NetHack\/} can, it should use a font by the chosen name for the status window. +\item[font\textunderscore status] +If \textit{NetHack} can, it should use a font by the chosen name for the status window. %.lp -\item[\ib{font\verb+_+text}] -If {\it NetHack\/} can, it should use a font by the chosen name for text windows. +\item[font\textunderscore text] +If \textit{NetHack} can, it should use a font by the chosen name for text windows. %.lp -\item[\ib{font\verb+_+size\verb+_+map}] -If {\it NetHack\/} can, it should use this size font for the map window. +\item[font\textunderscore size\textunderscore map] +If \textit{NetHack} can, it should use this size font for the map window. %.lp -\item[\ib{font\verb+_+size\verb+_+menu}] -If {\it NetHack\/} can, it should use this size font for menu windows. +\item[font\textunderscore size\textunderscore menu] +If \textit{NetHack} can, it should use this size font for menu windows. %.lp -\item[\ib{font\verb+_+size\verb+_+message}] -If {\it NetHack\/} can, it should use this size font for the message window. +\item[font\textunderscore size\textunderscore message] +If \textit{NetHack} can, it should use this size font for the message window. %.lp -\item[\ib{font\verb+_+size\verb+_+status}] -If {\it NetHack\/} can, it should use this size font for the status window. +\item[font\textunderscore size\textunderscore status] +If \textit{NetHack} can, it should use this size font for the status window. %.lp -\item[\ib{font\verb+_+size\verb+_+text}] -If {\it NetHack\/} can, it should use this size font for text windows. +\item[font\textunderscore size\textunderscore text] +If \textit{NetHack} can, it should use this size font for text windows. %.lp -\item[\ib{fullscreen}] -If {\it NetHack\/} can, it should try to display on the entire screen rather than in a window. +\item[fullscreen] +If \textit{NetHack} can, it should try to display on the entire screen rather than in a window. %.lp -\item[\ib{guicolor}] +\item[guicolor] Use color text and/or highlighting attributes when displaying some non-map data (such as menu selector letters). Curses interface only; default is on. %.lp -\item[\ib{large\verb+_+font}] -If {\it NetHack\/} can, it should use a large font. +\item[large\textunderscore font] +If \textit{NetHack} can, it should use a large font. %.lp -\item[\ib{map\verb+_+mode}] -If {\it NetHack\/} can, it should display the map in the manner specified. +\item[map\textunderscore mode] +If \textit{NetHack} can, it should display the map in the manner specified. %.lp -\item[\ib{player\verb+_+selection}] -If {\it NetHack\/} can, it should pop up dialog boxes or use prompts for character selection. +\item[player\textunderscore selection] +If \textit{NetHack} can, it should pop up dialog boxes or use prompts for character selection. %.lp -\item[\ib{popup\verb+_+dialog}] -If {\it NetHack\/} can, it should pop up dialog boxes for input. +\item[popup\textunderscore dialog] +If \textit{NetHack} can, it should pop up dialog boxes for input. %.lp -\item[\ib{preload\verb+_+tiles}] -If {\it NetHack\/} can, it should preload tiles into memory. +\item[preload\textunderscore tiles] +If \textit{NetHack} can, it should preload tiles into memory. For example, in the protected mode MS-DOS version, control whether tiles get pre-loaded into RAM at the start of the game. Doing so enhances performance of the tile graphics, but uses more memory. (default on). -Cannot be set with the `{\tt O}' command. +Cannot be set with the `\texttt{O}' command. %.lp -\item[\ib{scroll\verb+_+amount}] -If {\it NetHack\/} can, it should scroll the display by this number of cells -when the hero reaches the scroll\verb+_+margin. +\item[scroll\textunderscore amount] +If \textit{NetHack} can, it should scroll the display by this number of cells +when the hero reaches the scroll\textunderscore margin. %.lp -\item[\ib{scroll\verb+_+margin}] -If {\it NetHack\/} can, it should scroll the display when the hero or cursor +\item[scroll\textunderscore margin] +If \textit{NetHack} can, it should scroll the display when the hero or cursor is this number of cells away from the edge of the window. %.lp -\item[\ib{selectsaved}] -If {\it NetHack\/} can, it should display a menu of existing saved games for the player to +\item[selectsaved] +If \textit{NetHack} can, it should display a menu of existing saved games for the player to choose from at game startup, if it can. Not all ports support this option. %.lp -\item[\ib{softkeyboard}] -If {\it NetHack\/} can, it should display an onscreen keyboard. +\item[softkeyboard] +If \textit{NetHack} can, it should display an onscreen keyboard. Handhelds are most likely to support this option. %.lp -\item[\ib{splash\verb+_+screen}] -If {\it NetHack\/} can, it should display an opening splash screen when +\item[splash\textunderscore screen] +If \textit{NetHack} can, it should display an opening splash screen when it starts up (default yes). %.lp -\item[\ib{statuslines}] +\item[statuslines] Number of lines for traditional below-the-map status display. -Acceptable values are {\tt 2} and {\tt 3} (default is {\tt 2}). +Acceptable values are \texttt{2} and \texttt{3} (default is \texttt{2}). %.lp "" -When set to {\tt 3}, the {\tt tty} interface moves some fields around and +When set to \texttt{3}, the \texttt{tty} interface moves some fields around and mainly shows status conditions on their own line. A display capable of showing at least 25 lines is recommended. -The value can be toggled back and forth during the game with the `{\tt O}' +The value can be toggled back and forth during the game with the `\texttt{O}' command. %.lp "" -The {\tt curses} interface does likewise if the -{\it align\verb+_+status\/} -option is set to {\it top\/} or {\it bottom\/} but ignores -{\it statuslines\/} -when set to {\it left\/} or {\it right}. +The \texttt{curses} interface does likewise if the +\textit{align\textunderscore status} +option is set to \textit{top} or \textit{bottom} but ignores +\textit{statuslines} +when set to \textit{left} or \textit{right}. %.lp "" -The {\tt Qt} interface already displays more than 3 lines for status +The \texttt{Qt} interface already displays more than 3 lines for status so uses the -{\it statuslines\/} +\textit{statuslines} value differently. -A value of {\tt 3} renders status in the {\tt Qt} interface's +A value of \texttt{3} renders status in the \texttt{Qt} interface's original format, with the status window spread out vertically. -A value of {\tt 2} makes status be slightly condensed, moving some +A value of \texttt{2} makes status be slightly condensed, moving some fields to different lines to eliminate one whole line, reducing the height needed. -(If NetHack has been built using a version of {\tt Qt} -older than {\tt qt-5.9}, -{\it statuslines\/} +(If NetHack has been built using a version of \texttt{Qt} +older than \texttt{qt-5.9}, +\textit{statuslines} can only be set in the run-time configuration file or via NETHACKOPTIONS, -not during play with the `{\tt O}' command.) +not during play with the `\texttt{O}' command.) %.lp -\item[\ib{term\verb+_+cols} {\normalfont and}] +\item[term\textunderscore cols \textrm{and}] %.lp -\item[\ib{term\verb+_+rows}] +\item[term\textunderscore rows] Curses interface only. Number of columns and rows to use for the display. Curses will attempt to resize to the values specified but will settle for smaller sizes if they are too big. Default is the current window size. %.lp -\item[\ib{tile\verb+_+file}] +\item[tile\textunderscore file] Specify the name of an alternative tile file to override the default. \\ %.lp "" Note: the X11 interface uses X resources rather than NetHack's options to select an alternate tile file. -See {\tt NetHack.ad}, the sample X ``application defaults'' file. +See \texttt{NetHack.ad}, the sample X ``application defaults'' file. %.lp -\item[\ib{tile\verb+_+height}] +\item[tile\textunderscore height] Specify the preferred height of each tile in a tile capable port. %.lp -\item[\ib{tile\verb+_+width}] +\item[tile\textunderscore width] Specify the preferred width of each tile in a tile capable port %.lp -\item[\ib{tiled\verb+_+map}] -If {\it NetHack\/} can, it should display the map using {\it tiles} graphics +\item[tiled\textunderscore map] +If \textit{NetHack} can, it should display the map using \textit{tiles} graphics rather than simple characters (letters and punctuation, possibly augmented by line-drawing symbols). -Setting {\tt tiled\verb+_+map} to {\it True\/} forces -{\tt ascii\verb+_+map} to be {\it False}. +Setting \texttt{tiled\textunderscore map} to \textit{True} forces +\texttt{ascii\textunderscore map} to be \textit{False}. %.lp -\item[\ib{use\verb+_+darkgray}] +\item[use\textunderscore darkgray] Use bold black instead of blue for black glyphs (TTY only). %.lp -\item[\ib{use\verb+_+inverse}] -If {\it NetHack\/} can, it should display inverse when the game specifies it. +\item[use\textunderscore inverse] +If \textit{NetHack} can, it should display inverse when the game specifies it. %.lp -\item[\ib{use\verb+_+menu\verb+_+glyphs}] -If {\it NetHack\/} can, it should display glyphs next to objects in the +\item[use\textunderscore menu\textunderscore glyphs] +If \textit{NetHack} can, it should display glyphs next to objects in the inventory. %.lp -\item[\ib{vary\verb+_+msgcount}] -If {\it NetHack\/} can, it should display this number of messages at a time +\item[vary\textunderscore msgcount] +If \textit{NetHack} can, it should display this number of messages at a time in the message window. %.lp -\item[\ib{windowborders}] +\item[windowborders] Whether to draw boxes around the map, status area, message area, and persistent inventory window if enabled. Curses interface only. @@ -5572,21 +5643,21 @@ Acceptable values are %.sd %.si -{\tt 0} --- off, never show borders\\ -{\tt 1} --- on, always show borders\\ -{\tt 2} --- auto, on display is at least -(\verb&24+2&)x(\verb&80+2&) [default]\\ -{\tt 3} --- on, except forced off for perm\verb+_+invent\\ -{\tt 4} --- auto, except forced off for perm\verb+_+invent\\ +\texttt{0} --- off, never show borders\\ +\texttt{1} --- on, always show borders\\ +\texttt{2} --- auto, on display is at least +$(24+2)\times(80+2)$ [default]\\ +\texttt{3} --- on, except forced off for perm\textunderscore invent\\ +\texttt{4} --- auto, except forced off for perm\textunderscore invent\\ %.ei %.ed %.lp "" (The 26x82 size threshold for `2' refers to number of rows and columns of the display. -A width of at least 110 columns (\verb&80+2+26+2&) is needed for -{\it align_status\/} -set to {\tt left} or {\tt right}.) +A width of at least 110 columns ($80+2+26+2$) is needed for +\textit{align\textunderscore status} +set to \texttt{left} or \texttt{right}.) %.lp "" The persistent inventory window, when enabled, can grow until it is @@ -5596,35 +5667,35 @@ setting the value to 3 or 4 instead will keep borders for the map, message, and status windows but have room for two additional lines of inventory plus widen each inventory line by two columns. %.lp -\item[\ib{windowcolors}] -If {\it NetHack\/} can, it should display all windows of a particular style +\item[windowcolors] +If \textit{NetHack} can, it should display all windows of a particular style with the specified foreground and background colors. Windows GUI and curses windowport only. The format is\\ -{\tt ~~~~OPTION=windowcolors:}{\it style foreground\/}{\tt /}{\it background}\\ -where {\it style} is one of {\tt menu}, {\tt message}, {\tt status}, -or {\tt text}, and -{\it foreground} and {\it background} are colors, either numeric (hash -sign followed by three pairs of hexadecimal digits, {\it \#rrggbb\/}), -one of the named colors ({\it black}, {\it red}, {\it green}, {\it brown}, -{\it blue}, {\it magenta}, {\it cyan}, {\it orange}, -{\it bright-green}, {\it yellow}, {\it bright-blue}, {\it bright-magenta}, -{\it bright-cyan}, {\it white}, {\it gray}, {\it purple}, -{\it silver}, {\it maroon}, {\it fuchsia}, {\it lime}, {\it olive}, -{\it navy}, {\it teal}, {\it aqua}), -or (for Windows only) one of Windows UI colors ({\it trueblack}, -{\it activeborder}, {\it activecaption}, {\it appworkspace}, {\it background}, -{\it btnface}, {\it btnshadow}, {\it btntext}, {\it captiontext}, -{\it graytext}, {\it greytext}, {\it highlight}, -{\it highlighttext}, {\it inactiveborder}, {\it inactivecaption}, {\it menu}, -{\it menutext}, {\it scrollbar}, {\it window}, {\it windowframe}, -{\it windowtext}). +\texttt{~~~~OPTION=windowcolors:}\textit{style foreground}\texttt{/}\textit{background}\\ +where \textit{style} is one of \texttt{menu}, \texttt{message}, \texttt{status}, +or \texttt{text}, and +\textit{foreground} and \textit{background} are colors, either numeric (hash +sign followed by three pairs of hexadecimal digits, \textit{\#rrggbb}), +one of the named colors (\textit{black}, \textit{red}, \textit{green}, \textit{brown}, +\textit{blue}, \textit{magenta}, \textit{cyan}, \textit{orange}, +\textit{bright-green}, \textit{yellow}, \textit{bright-blue}, \textit{bright-magenta}, +\textit{bright-cyan}, \textit{white}, \textit{gray}, \textit{purple}, +\textit{silver}, \textit{maroon}, \textit{fuchsia}, \textit{lime}, \textit{olive}, +\textit{navy}, \textit{teal}, \textit{aqua}), +or (for Windows only) one of Windows UI colors (\textit{trueblack}, +\textit{activeborder}, \textit{activecaption}, \textit{appworkspace}, \textit{background}, +\textit{btnface}, \textit{btnshadow}, \textit{btntext}, \textit{captiontext}, +\textit{graytext}, \textit{greytext}, \textit{highlight}, +\textit{highlighttext}, \textit{inactiveborder}, \textit{inactivecaption}, \textit{menu}, +\textit{menutext}, \textit{scrollbar}, \textit{window}, \textit{windowframe}, +\textit{windowtext}). %.lp -\item[\ib{wraptext}] -If {\it NetHack\/} can, it should wrap long lines of text if they don't fit +\item[wraptext] +If \textit{NetHack} can, it should wrap long lines of text if they don't fit in the visible area of the window. -\elist +\end{description} %.hn 2 \subsection*{Crash Report Options} @@ -5633,21 +5704,21 @@ in the visible area of the window. Please note that NetHack does not send {\textbf any} information off your computer unless you manually click submit on a form. %.si -\blist{} +\begin{description} %.lp -\item[OPTION=crash_email:{\it email_address}] +\item[OPTION=crash\textunderscore email:\textit{email\textunderscore address}] %.lp -\item[OPTION=crash_name:{\it your_name}] +\item[OPTION=crash\textunderscore name:\textit{your\textunderscore name}] %.ei -\elist +\end{description} These options are used only to save you some typing on the crash report and \#bugreport forms. %.si -\blist{} +\begin{description} %.lp -\item[OPTION=crash_urlmax:{\it bytes}] +\item[OPTION=crash\textunderscore urlmax:\textit{bytes}] %.ei -\elist +\end{description} This option is used to limit the length of the URLs generated and is only needed if your browser cannot handle arbitrarily long URLs. @@ -5658,100 +5729,100 @@ needed if your browser cannot handle arbitrarily long URLs. Here are explanations of options that are used by specific platforms or ports to customize and change the port behavior. -\blist{} +\begin{description}[font=\mdseries\itshape] %.lp -\item[\ib{altkeyhandling}] -Select an alternate way to handle keystrokes ({\it Win32 tty\/ NetHack\/} only). -The name of the handling type is one of {\it default}, {\it ray}, {\it 340} -%.\" \item[\ib{altmeta}] +\item[altkeyhandling] +Select an alternate way to handle keystrokes (\textit{Win32 tty NetHack} only). +The name of the handling type is one of \textit{default}, \textit{ray}, \textit{340} +%.\" \item[altmeta] %.\" On Amiga, this option controls whether typing ``Alt'' plus another key %.\" functions as a meta-shift for that key (default on). %.lp -\item[\ib{altmeta}] +\item[altmeta] %.\" On other (non-Amiga) systems where this option is available, it can be On systems where this option is available, it can be -set to tell {\it NetHack\/} to convert a two character sequence beginning with +set to tell \textit{NetHack} to convert a two character sequence beginning with ESC into a meta-shifted version of the second character (default off). %.lp "" This conversion is only done for commands, not for other input prompts. Note that typing one or more digits as a count prefix prior to a -command---preceded by {\tt n} if the {\it number\verb+_+pad\/} +command---preceded by \texttt{n} if the \textit{number\textunderscore pad} option is set---is also subject to this conversion, so attempting to -abort the count by typing ESC will leave {\it NetHack\/} waiting for another +abort the count by typing ESC will leave \textit{NetHack} waiting for another character to complete the two character sequence. Type a second ESC to finish cancelling such a count. At other prompts a single ESC suffices. %.lp -\item[\ib{BIOS}] +\item[BIOS] Use BIOS calls to update the screen display quickly and to read the keyboard (allowing the use of arrow keys to move) on machines with an IBM PC -compatible BIOS ROM (default off, {\it OS/2, PC\/ {\rm and} ST NetHack\/} only). +compatible BIOS ROM (default off, \textit{OS/2, PC {\rm and} ST NetHack} only). %.lp -\item[\ib{rawio}] +\item[rawio] Force raw (non-cbreak) mode for faster output and more -bulletproof input (MS-DOS sometimes treats `{\tt \^{}P}' as a printer toggle -without it) (default off, {\it OS/2, PC\/ {\rm and} ST NetHack\/} only). +bulletproof input (MS-DOS sometimes treats `\texttt{\textasciicircum P}' as a printer toggle +without it) (default off, \textit{OS/2, PC {\rm and} ST NetHack} only). Note: DEC Rainbows hang if this is turned on. -Cannot be set with the `{\tt O}' command. +Cannot be set with the `\texttt{O}' command. %.lp -\item[\ib{subkeyvalue}] -({\it Win32 tty NetHack \/} only). +\item[subkeyvalue] +(\textit{Win32 tty NetHack } only). May be used to alter the value of keystrokes that the operating system -returns to {\it NetHack\/} to help compensate for international keyboard +returns to \textit{NetHack} to help compensate for international keyboard issues. OPTIONS=subkeyvalue:171/92 -will return 92 to {\it NetHack\/}, if 171 was originally going to be returned. +will return 92 to \textit{NetHack}, if 171 was originally going to be returned. You can use multiple subkeyvalue assignments in the configuration file if needed. -Cannot be set with the `{\tt O}' command. +Cannot be set with the `\texttt{O}' command. %.lp -\item[\ib{video}] -Set the video mode used ({\it PC\/ NetHack\/} only). -Values are {\it autodetect\/}, {\it default\/}, {\it vga\/}, or {\it vesa\/}. -Setting {\it vesa\/} will cause the game to display tiles, using the full +\item[video] +Set the video mode used (\textit{PC NetHack} only). +Values are \textit{autodetect}, \textit{default}, \textit{vga}, or \textit{vesa}. +Setting \textit{vesa} will cause the game to display tiles, using the full capability of the VGA hardware. -Setting {\it vga\/} will cause the game to display tiles, fixed at 640x480 +Setting \textit{vga} will cause the game to display tiles, fixed at 640x480 in 16 colors, a mode that is compatible with all VGA hardware. Third party tilesets will probably not work. -Setting {\it autodetect\/} attempts {\it vesa\/}, then {\it vga\/}, and -finally sets {\it default\/} if neither of those modes works. -Cannot be set with the `{\tt O}' command. +Setting \textit{autodetect} attempts \textit{vesa}, then \textit{vga}, and +finally sets \textit{default} if neither of those modes works. +Cannot be set with the `\texttt{O}' command. %.lp -\item[\ib{video\verb+_+height}] +\item[video\textunderscore height] Set the VGA mode resolution height (MS-DOS only, with video:vesa) %.lp -\item[\ib{video\verb+_+width}] +\item[video\textunderscore width] Set the VGA mode resolution width (MS-DOS only, with video:vesa) %.lp -\item[\ib{videocolors}] +\item[videocolors] \begin{sloppypar} -Set the color palette for PC systems using NO\verb+_+TERMS -(default 4-2-6-1-5-3-15-12-10-14-9-13-11, {\it PC\/ NetHack\/} only). +Set the color palette for PC systems using NO\textunderscore TERMS +(default 4-2-6-1-5-3-15-12-10-14-9-13-11, \textit{PC NetHack} only). The order of colors is red, green, brown, blue, magenta, cyan, bright.white, bright.red, bright.green, yellow, bright.blue, bright.magenta, and bright.cyan. -Cannot be set with the `{\tt O}' command. +Cannot be set with the `\texttt{O}' command. \end{sloppypar} %.lp -\item[\ib{videoshades}] +\item[videoshades] Set the intensity level of the three gray scales available -(default dark normal light, {\it PC\/ NetHack\/} only). +(default dark normal light, \textit{PC NetHack} only). If the game display is difficult to read, try adjusting these scales; -if this does not correct the problem, try {\tt !color}. -Cannot be set with the `{\tt O}' command. -\elist +if this does not correct the problem, try \texttt{!color}. +Cannot be set with the `\texttt{O}' command. +\end{description} %.hn 2 \subsection*{Regular Expressions} %.pg Regular expressions are normally POSIX extended regular expressions. It is -possible to compile {\it NetHack\/} without regular expression support on +possible to compile \textit{NetHack} without regular expression support on a platform where there is no regular expression library. While this is not true of any modern -platform, if your {\it NetHack\/} was built this way, patterns are instead glob +platform, if your \textit{NetHack} was built this way, patterns are instead glob patterns; regardless, this document refers to both as ``regular expressions.'' This applies to Autopickup exceptions, Message types, Menu colors, and User sounds. @@ -5760,19 +5831,19 @@ and User sounds. \subsection*{Configuring Autopickup Exceptions} %.pg -You can further refine the behavior of the ``{\tt autopickup}'' option -beyond what is available through the ``{\tt pickup\verb+_+types}'' option. +You can further refine the behavior of the ``\texttt{autopickup}'' option +beyond what is available through the ``\texttt{pickup\textunderscore types}'' option. %.pg -By placing ``{\tt autopickup\verb+_+exception}'' lines in your configuration +By placing ``\texttt{autopickup\textunderscore exception}'' lines in your configuration file, you can define patterns to be checked when the game is about to autopickup something. -\blist{} +\begin{description} %.lp -\item[\ib{autopickup\verb+_+exception}] -Sets an exception to the ``{\it pickup\verb+_+types}'' option. -The {\it autopickup\verb+_+exception\/} option should be followed by a regular +\item[autopickup\textunderscore exception] +Sets an exception to the ``\textit{pickup\textunderscore types}'' option. +The \textit{autopickup\textunderscore exception} option should be followed by a regular expression to be used as a pattern to match against the singular form of the description of an object at your location. @@ -5781,21 +5852,21 @@ character in the pattern, specifically: %.sd %.si -{\tt <} --- always pickup an object that matches rest of pattern;\\ -{\tt >} --- never pickup an object that matches rest of pattern. +\texttt{<} --- always pickup an object that matches rest of pattern;\\ +\texttt{>} --- never pickup an object that matches rest of pattern. %.ei %.ed -The {\it autopickup\verb+_+exception\/} rules are processed in the order +The \textit{autopickup\textunderscore exception} rules are processed in the order in which they appear in your configuration file, thus allowing a later rule to override an earlier rule. %.lp "" -Exceptions can be set with the `{\tt O}' command, but because they are not +Exceptions can be set with the `\texttt{O}' command, but because they are not included in your configuration file, they won't be in effect if you save and then restore your game. -{\it autopickup\verb+_+exception\/} rules are not saved with the game. -\elist +\textit{autopickup\textunderscore exception} rules are not saved with the game. +\end{description} %.lp "Here are some examples:" Here are some examples: @@ -5820,8 +5891,8 @@ autopickup. It is possible to change the default key bindings of some special commands, menu accelerator keys, extended commands, by using BIND stanzas in the configuration file. Format is key, followed by the command to bind to, -separated by a colon. The key can be a single character (``{\tt x}''), -a control key (``{\tt \^{}X}'', ``{\tt C-x}''), a meta key (``{\tt M-x}''), +separated by a colon. The key can be a single character (``\texttt{x}''), +a control key (``\texttt{\textasciicircum X}'', ``\texttt{C-x}''), a meta key (``\texttt{M-x}''), a mouse button, or a three-digit decimal ASCII code. %.pg @@ -5833,160 +5904,160 @@ For example: BIND=v:loot \end{verbatim} -\blist{} +\begin{description}[font=\mdseries\ttfamily] %.lp "Extended command keys" -\item[\tb{Extended command keys}] +\item[Extended command keys] You can bind multiple keys to the same extended command. Unbind a key by -using ``{\tt nothing}'' as the extended command to bind to. You can also bind -the ``{\tt }'', ``{\tt }'', and ``{\tt }'' keys. +using ``\texttt{nothing}'' as the extended command to bind to. You can also bind +the ``\texttt{}'', ``\texttt{}'', and ``\texttt{}'' keys. %.lp "Menu accelerator keys" -\item[\tb{Menu accelerator keys}] +\item[Menu accelerator keys] The menu control or accelerator keys can also be rebound via OPTIONS lines in the configuration file. You cannot bind object symbols or selection letters into menu accelerators. Some interfaces only support some of the menu accelerators. %.lp "Mouse buttons" -\item[\tb{Mouse buttons}] -You can bind ``mouse1'' or ``mouse2'' to ``{\tt nothing}'', -``{\tt therecmdmenu}'', ``{\tt clicklook}'', or ``{\tt mouseaction}''. +\item[Mouse buttons] +You can bind ``mouse1'' or ``mouse2'' to ``\texttt{nothing}'', +``\texttt{therecmdmenu}'', ``\texttt{clicklook}'', or ``\texttt{mouseaction}''. %.lp "Special command keys" -\item[\tb{Special command keys}] +\item[Special command keys] Below are the special commands you can rebind. Some of them can be bound to same keys with no problems, others are in the same ``context'', and if bound to same keys, only one of those commands will be available. Special command can only be bound to a single key. -\elist +\end{description} %.pg -\blist{\itemindent 10mm \labelwidth 15mm \rightmargin 15mm} +\begin{description}[itemindent=10mm, labelwidth=15mm, rightmargin=15mm] %.lp -\item[{\bb{count}}] +\item[count] Prefix key to start a count, to repeat a command this many times. -With {\it number\verb+_+pad\/} only. Default is~`{\tt n}'. +With \textit{number\textunderscore pad} only. Default is~`\texttt{n}'. %.lp -\item[{\bb{getdir.help}}] -When asked for a direction, the key to show the help. Default is~`{\tt ?}'. +\item[getdir.help] +When asked for a direction, the key to show the help. Default is~`\texttt{?}'. %.lp -\item[{\bb{getdir.mouse}}] +\item[getdir.mouse] When asked for a direction, the key to initiate a simulated mouse click. You will be asked to pick a location. Use movement keystrokes to move the cursor around the map, then type -the getpos.pick.once key (default `{\tt ,}') -or the getpos.pick key (default `{\tt .}') +the getpos.pick.once key (default `\texttt{,}') +or the getpos.pick key (default `\texttt{.}') to finish as if performing a left or right click. -Only useful when using the {\tt \#therecmdmenu} command. -Default is~`{\tt \verb+_+}'. +Only useful when using the \texttt{\#therecmdmenu} command. +Default is~`\texttt{\textunderscore}'. %.lp -\item[{\bb{getdir.self}}] -When asked for a direction, the key to target yourself. Default is~`{\tt .}'. +\item[getdir.self] +When asked for a direction, the key to target yourself. Default is~`\texttt{.}'. %.lp -\item[{\bb{getdir.self2}}] +\item[getdir.self2] When asked for a direction, an alternate key to target yourself. -Default is~`{\tt s}'. +Default is~`\texttt{s}'. %.lp -\item[{\bb{getpos.autodescribe}}] -When asked for a location, the key to toggle {\it autodescribe\/}. -Default is~`{\tt \#}'. +\item[getpos.autodescribe] +When asked for a location, the key to toggle \textit{autodescribe}. +Default is~`\texttt{\#}'. %.lp -\item[{\bb{getpos.all.next}}] +\item[getpos.all.next] When asked for a location, the key to go to next closest interesting thing. -Default is~`{\tt a}'. +Default is~`\texttt{a}'. %.lp -\item[{\bb{getpos.all.prev}}] +\item[getpos.all.prev] When asked for a location, the key to go to previous closest interesting thing. -Default is~`{\tt A}'. +Default is~`\texttt{A}'. %.lp -\item[{\bb{getpos.door.next}}] +\item[getpos.door.next] When asked for a location, the key to go to next closest door or doorway. -Default is~`{\tt d}'. +Default is~`\texttt{d}'. %.lp -\item[{\bb{getpos.door.prev}}] +\item[getpos.door.prev] When asked for a location, the key to go to previous closest door or doorway. -Default is~`{\tt D}'. +Default is~`\texttt{D}'. %.lp -\item[{\bb{getpos.help}}] -When asked for a location, the key to show help. Default is~`{\tt ?}'. +\item[getpos.help] +When asked for a location, the key to show help. Default is~`\texttt{?}'. %.lp -\item[{\bb{getpos.mon.next}}] +\item[getpos.mon.next] When asked for a location, the key to go to next closest monster. -Default is~`{\tt m}'. +Default is~`\texttt{m}'. %.lp -\item[{\bb{getpos.mon.prev}}] +\item[getpos.mon.prev] When asked for a location, the key to go to previous closest monster. -Default is~`{\tt M}'. +Default is~`\texttt{M}'. %.lp -\item[{\bb{getpos.obj.next}}] +\item[getpos.obj.next] When asked for a location, the key to go to next closest object. -Default is~`{\tt o}'. +Default is~`\texttt{o}'. %.lp -\item[{\bb{getpos.obj.prev}}] +\item[getpos.obj.prev] When asked for a location, the key to go to previous closest object. -Default is~`{\tt O}'. +Default is~`\texttt{O}'. %.lp -\item[{\bb{getpos.menu}}] +\item[getpos.menu] When asked for a location, and using one of the next or previous keys to -cycle through targets, toggle showing a menu instead. Default is~`{\tt !}'. +cycle through targets, toggle showing a menu instead. Default is~`\texttt{!}'. %.lp -\item[{\bb{getpos.moveskip}}] +\item[getpos.moveskip] When asked for a location, and using the shifted movement keys or meta-digit keys to fast-move around, move by skipping the same glyphs instead of by 8 units. -Default is~`{\tt *}'. +Default is~`\texttt{*}'. %.lp -\item[{\bb{getpos.filter}}] +\item[getpos.filter] When asked for a location, change the filtering mode when using one of the next or previous keys to cycle through targets. Toggles between no -filtering, in view only, and in the same area only. Default is~`{\tt "}'. +filtering, in view only, and in the same area only. Default is~`\texttt{"}'. %.lp -\item[{\bb{getpos.pick}}] +\item[getpos.pick] When asked for a location, the key to choose the location, and possibly ask for more info. When simulating a mouse click after being asked for a direction (see getdir.mouse above), the key to use to respond as right click. -Default is~`{\tt .}'. +Default is~`\texttt{.}'. %.lp -\item[{\bb{getpos.pick.once}}] +\item[getpos.pick.once] When asked for a location, the key to choose the location, and skip asking for more info. When simulating a mouse click after being asked for a direction, the key to respond as left click. -Default is~`{\tt ,}'. +Default is~`\texttt{,}'. %.lp -\item[{\bb{getpos.pick.quick}}] +\item[getpos.pick.quick] When asked for a location, the key to choose the location, skip asking -for more info, and exit the location asking loop. Default is~`{\tt ;}'. +for more info, and exit the location asking loop. Default is~`\texttt{;}'. %.lp -\item[{\bb{getpos.pick.verbose}}] +\item[getpos.pick.verbose] When asked for a location, the key to choose the location, and show more -info without asking. Default is~`{\tt :}'. +info without asking. Default is~`\texttt{:}'. %.lp -\item[{\bb{getpos.self}}] +\item[getpos.self] When asked for a location, the key to go to your location. -Default is~`{\tt @}'. +Default is~`\texttt{@}'. %.lp -\item[{\bb{getpos.unexplored.next}}] +\item[getpos.unexplored.next] When asked for a location, the key to go to next closest unexplored location. -Default is~`{\tt x}'. +Default is~`\texttt{x}'. %.lp -\item[{\bb{getpos.unexplored.prev}}] +\item[getpos.unexplored.prev] When asked for a location, the key to go to previous closest unexplored -location. Default is~`{\tt X}'. +location. Default is~`\texttt{X}'. %.lp -\item[{\bb{getpos.valid}}] +\item[getpos.valid] When asked for a location, the key to go to show valid target locations. -Default is~`{\tt \$}'. +Default is~`\texttt{\$}'. %.lp -\item[{\bb{getpos.valid.next}}] +\item[getpos.valid.next] When asked for a location, the key to go to next closest valid location. -Default is~`{\tt z}'. +Default is~`\texttt{z}'. %.lp -\item[{\bb{getpos.valid.prev}}] +\item[getpos.valid.prev] When asked for a location, the key to go to previous closest valid location. -Default is~`{\tt Z}'. -\elist +Default is~`\texttt{Z}'. +\end{description} %.hn 2 @@ -6002,27 +6073,27 @@ look like this: \begin{verbatim} MSGTYPE=type "pattern" \end{verbatim} -\blist{} +\begin{description}[font=\mdseries\itshape] %.lp -\item[\ib{type}] +\item[type] how the message should be shown: %.sd %.si \\ -{\tt show} --- show message normally.\\ -{\tt hide} --- never show the message.\\ -{\tt stop} --- wait for user with more-prompt.\\ -{\tt norep} --- show the message once, but not again if no other message is +\texttt{show} --- show message normally.\\ +\texttt{hide} --- never show the message.\\ +\texttt{stop} --- wait for user with more-prompt.\\ +\texttt{norep} --- show the message once, but not again if no other message is shown in between. %.ei %.ed %.lp -\item[\ib{pattern}] +\item[pattern] the pattern to match. The pattern should be a regular expression. -\elist +\end{description} %.lp "" -Here's an example of message types using {\it NetHack's\/} internal +Here's an example of message types using \textit{NetHack's} internal pattern matching facility: \begin{verbatim} @@ -6032,7 +6103,7 @@ pattern matching facility: specifies that whenever a message ``You feel hungry'' is shown, the user is prompted with more-prompt, and a message matching -``You displaced \verb+<+something\verb+>+'' is not shown at all. +``You displaced '' is not shown at all. %.lp The order of the defined MSGTYPE lines is important; the last matching @@ -6057,40 +6128,40 @@ look like this: MENUCOLOR="pattern"=color&attribute \end{verbatim} -\blist{} +\begin{description}[font=\mdseries\itshape] %.lp -\item[\ib{pattern}] +\item[pattern] the pattern to match; %.lp -\item[\ib{color}] +\item[color] the color to use for lines matching the pattern; %.lp -\item[\ib{attribute}] +\item[attribute] the attribute to use for lines matching the pattern. The attribute is optional, and if left out, you must also leave out the preceding ampersand. If no attribute is defined, no attribute is used. -\elist +\end{description} %.lp "" The pattern should be a regular expression. %.lp "" -Allowed colors are {\it black}, {\it red}, {\it green}, {\it brown}, -{\it blue}, {\it magenta}, {\it cyan}, {\it gray}, {\it orange}, -{\it light-green}, {\it yellow}, {\it light-blue}, {\it light-magenta}, -{\it light-cyan}, and {\it white}. -And {\it no-color}, the default foreground color, which isn't necessarily +Allowed colors are \textit{black}, \textit{red}, \textit{green}, \textit{brown}, +\textit{blue}, \textit{magenta}, \textit{cyan}, \textit{gray}, \textit{orange}, +\textit{light-green}, \textit{yellow}, \textit{light-blue}, \textit{light-magenta}, +\textit{light-cyan}, and \textit{white}. +And \textit{no-color}, the default foreground color, which isn't necessarily the same as any of the other colors. %.lp "" -Allowed attributes are {\it none}, {\it bold}, {\it dim}, {\it italic}, -{\it underline},{\it blink}, and {\it inverse}. -{\it Normal\/} is a synonym for {\it none}. +Allowed attributes are \textit{none}, \textit{bold}, \textit{dim}, \textit{italic}, +\textit{underline},\textit{blink}, and \textit{inverse}. +\textit{Normal} is a synonym for \textit{none}. Note that the platform used may interpret the attributes any way it wants. %.lp "" -Here's an example of menu colors using {\it NetHack's\/} internal +Here's an example of menu colors using \textit{NetHack's} internal pattern matching facility: \begin{verbatim} @@ -6110,7 +6181,7 @@ a menu line will be used for the line. %.pg Note that if you intend to have one or more color specifications match ``~uncursed~'', you will probably want to turn the -{\it implicit\verb+_+uncursed\/} +\textit{implicit\textunderscore uncursed} option off so that all items known to be uncursed are actually displayed with the ``uncursed'' description. @@ -6128,26 +6199,26 @@ use of user sounds. The following configuration file entries are relevant to mapping user sounds to messages: -\blist{} +\begin{description}[font=\mdseries\itshape] %.lp -\item[\ib{SOUNDDIR}] +\item[SOUNDDIR] The directory that houses the sound files to be played. %.lp -\item[\ib{SOUND}] +\item[SOUND] An entry that maps a sound file to a user-specified message pattern. Each SOUND entry is broken down into the following parts: %.sd %.si -{\tt MESG } --- message window mapping (the only one supported in 3.7.0);\\ -{\tt msgtype } --- optional; message type to use, see ``Configuring User Sounds''\\ -{\tt pattern } --- the pattern to match;\\ -{\tt sound file } --- the sound file to play;\\ -{\tt volume } --- the volume to be set while playing the sound file;\\ -{\tt sound index} --- optional; the index corresponding to a sound file. +\texttt{MESG} --- message window mapping (the only one supported in 3.7.0);\\ +\texttt{msgtype} --- optional; message type to use, see ``Configuring User Sounds''\\ +\texttt{pattern} --- the pattern to match;\\ +\texttt{sound file} --- the sound file to play;\\ +\texttt{volume} --- the volume to be set while playing the sound file;\\ +\texttt{sound index} --- optional; the index corresponding to a sound file. %.ei %.ed -\elist +\end{description} %.lp "" The pattern should be a regular expression. @@ -6166,8 +6237,8 @@ For example: \subsection*{Configuring Status Hilites} %.pg -Your copy of {\it NetHack\/} may have been compiled with support -for {\it Status Hilites}. +Your copy of \textit{NetHack} may have been compiled with support +for \textit{Status Hilites}. If so, you can customize your game display by setting thresholds to change the color or appearance of fields in the status display. @@ -6182,8 +6253,8 @@ drop to or below a threshold of 30%:\\ \begin{verbatim} OPTION=hilite_status:hitpoints/<=30%/red/normal \end{verbatim} -(That example is actually specifying {\tt red\&normal} for {\tt <=30\%} -and {\tt no-color\&normal} for {\tt >30\%}.)\\ +(That example is actually specifying \texttt{red\& normal} for \texttt{<=30\%} +and \texttt{no-color\& normal} for \texttt{>30\%}.)\\ For another example, the following line in your configuration file will cause wisdom to be displayed red if it drops and green if it rises:\\ @@ -6193,7 +6264,7 @@ OPTION=hilite_status:wisdom/down/red/up/green Allowed colors are black, red, green, brown, blue, magenta, cyan, gray, orange, light-green, yellow, light-blue, light-magenta, light-cyan, and white. -And {\it no-color}, the default foreground color on the display, which +And \textit{no-color}, the default foreground color on the display, which is not necessarily the same as black or white or any of the other colors. Allowed attributes are none, bold, dim, underline, italic, blink, and inverse. @@ -6204,7 +6275,7 @@ To specify both a color and an attribute, use `\&' to combine them. To specify multiple attributes, use `+' to combine those. %.lp "" -For example: {\tt magenta\&inverse+dim}. +For example: \texttt{magenta\& inverse+dim}. Note that the display may substitute or ignore particular attributes depending upon its capabilities, and in general may interpret the @@ -6214,7 +6285,7 @@ blink or vice versa. On others, issuing an attribute request while another is already set up will replace the earlier attribute rather than combine with it. Since nethack issues attribute requests sequentially (at least with -the {\it tty} interface) rather than all at once, the only way a +the \textit{tty} interface) rather than all at once, the only way a situation like that can be controlled is to specify just one attribute. You can adjust the display of the following status fields: @@ -6243,51 +6314,51 @@ depending upon your other option settings. %.lp "" Instead of a behavior, `condition' takes the following condition flags: -{\it stone}, {\it slime}, {\it strngl}, {\it foodpois}, {\it termill}, -{\it blind}, {\it deaf}, {\it stun}, {\it conf}, {\it hallu}, -{\it lev}, {\it fly}, and {\it ride}. -You can use `major\_troubles' as an alias -for stone through termill, `minor\_troubles' for blind through hallu, +\textit{stone}, \textit{slime}, \textit{strngl}, \textit{foodpois}, \textit{termill}, +\textit{blind}, \textit{deaf}, \textit{stun}, \textit{conf}, \textit{hallu}, +\textit{lev}, \textit{fly}, and \textit{ride}. +You can use `major\textunderscore troubles' as an alias +for stone through termill, `minor\textunderscore troubles' for blind through hallu, `movement' for lev, fly, and ride, and `all' for every condition. %.lp "" Allowed behaviors are ``always'', ``up'', ``down'', ``changed'', a percentage or absolute number threshold, or text to match against. -For the {\it hitpoints\/} field, the additional behavior ``criticalhp'' +For the \textit{hitpoints} field, the additional behavior ``criticalhp'' is available. It overrides other behavior rules if -hit points are at or below the {\it major problem\/} threshold +hit points are at or below the \textit{major problem} threshold (which varies depending upon maximum hit points and experience level). -\blist{} +\begin{description}[font=\mdseries\ttfamily] %.lp "*" -\item[{\tt always}] will set the default attributes for that field. +\item[always] will set the default attributes for that field. %.lp "*" -\item[{\tt up}{\normalfont, }{\tt down}] set the field attributes +\item[up \textrm{\textmd{,}} down] set the field attributes for when the field value changes upwards or downwards. This attribute -times out after {\tt statushilites} turns. +times out after \texttt{statushilites} turns. %.lp "*" -\item[{\tt changed}] sets the field attribute for when the field value -changes. This attribute times out after {\tt statushilites} turns. +\item[changed] sets the field attribute for when the field value +changes. This attribute times out after \texttt{statushilites} turns. (If a field has both a ``changed'' rule and an ``up'' or ``down'' rule which matches a change in the field's value, the ``up'' or ``down'' one takes precedence.) %.lp "*" -\item[{\tt percentage}] sets the field attribute when the field value +\item[percentage] sets the field attribute when the field value matches the percentage. -It is specified as a number between 0 and 100, followed by `{\tt \%}' +It is specified as a number between 0 and 100, followed by `\texttt{\%}' (percent sign). -If the percentage is prefixed with `{\tt <=}' or `{\tt >=}', +If the percentage is prefixed with `\texttt{<=}' or `\texttt{>=}', it also matches when value is below or above the percentage. -Use prefix `{\tt <}' or `{\tt >}' to match when strictly below or above. -(The numeric limit is relaxed slightly for those: {\tt >-1\%} -and {\tt <101\%} are allowed.) +Use prefix `\texttt{<}' or `\texttt{>}' to match when strictly below or above. +(The numeric limit is relaxed slightly for those: \texttt{>-1\%} +and \texttt{<101\%} are allowed.) Only four fields support percentage rules. -Percentages for ``{\it hitpoints\/}'' and ``{\it power\/}'' are +Percentages for ``\textit{hitpoints}'' and ``\textit{power}'' are straightforward; they're based on the corresponding maximum field. -Percentage highlight rules are also allowed for ``{\it experience level\/}'' -and ``{\it experience points\/}'' (valid when the -{\it showexp\/} +Percentage highlight rules are also allowed for ``\textit{experience level}'' +and ``\textit{experience points}'' (valid when the +\textit{showexp} option is enabled). For those, the percentage is based on the progress from the start of the current experience level to the start of the next level. @@ -6295,41 +6366,41 @@ So if level 2 starts at 20 points and level 3 starts at 40 points, having 30 points is 50\% and 35 points is 75\%. 100\% is unattainable for experience because you'll gain a level and the calculations will be reset for that new level, but a rule for -{\tt =100\%} is allowed and matches the special case of being +\texttt{=100\%} is allowed and matches the special case of being exactly 1 experience point short of the next level. % (If you manage to reach level 30, there is no next level and the % percentage will remain at 0\% no matter have many additional experience % points you earn.) %.lp "*" -\item[{\tt absolute}] value sets the attribute when the field value +\item[absolute] value sets the attribute when the field value matches that number. -The number must be 0 or higher, except for ``{\it armor-class\/} which -allows negative values, and may optionally be preceded by `{\tt =}'. -If the number is preceded by `{\tt <=}' or `{\tt >=}' instead, +The number must be 0 or higher, except for ``\textit{armor-class} which +allows negative values, and may optionally be preceded by `\texttt{=}'. +If the number is preceded by `\texttt{<=}' or `\texttt{>=}' instead, it also matches when value is below or above. -If the prefix is `{\tt <}' or `{\tt >}', only match when strictly +If the prefix is `\texttt{<}' or `\texttt{>}', only match when strictly above or below. %.lp "*" -\item[{\tt criticalhp}] only applies to the hitpoints field and only +\item[criticalhp] only applies to the hitpoints field and only when current hit points are below a threshold (which varies by maximum hit points and experience level). When the threshold is met, a criticalhp rule takes precedence over all other hitpoints rules. %.lp "*" -\item[{\tt text}] match sets the attribute when the field value +\item[text] match sets the attribute when the field value matches the text. -Text matches can only be used for ``{\it alignment\/}'', -``{\it carrying-capacity\/}'', ``{\it hunger\/}'', ``{\it dungeon-level\/}'', -and ``{\it title\/}''. +Text matches can only be used for ``\textit{alignment}'', +``\textit{carrying-capacity}'', ``\textit{hunger}'', ``\textit{dungeon-level}'', +and ``\textit{title}''. For title, only the role's rank title is tested; the character's name is ignored. %.ei -\elist +\end{description} The in-game options menu can help you determine the correct syntax for a configuration file. -The whole feature can be disable by setting option {\it statushilites} to 0. +The whole feature can be disable by setting option \textit{statushilites} to 0. Example hilites: \begin{verbatim} @@ -6347,233 +6418,231 @@ Example hilites: %.lp %.hn 2 -\subsection*{Modifying {\it NetHack\/} Symbols} +\subsection*{Modifying \textit{NetHack} Symbols} %.pg -{\it NetHack\/} can load entire symbol sets from the symbol file. +\textit{NetHack} can load entire symbol sets from the symbol file. %.pg The options that are used to select a particular symbol set from the symbol file are: -\blist{} +\begin{description}[font=\mdseries\itshape] %.lp -\item[\ib{symset}] +\item[symset] Set the name of the symbol set that you want to load. -{\it symbols\/}. +\textit{symbols}. %.lp -\item[\ib{roguesymset}] +\item[roguesymset] Set the name of the symbol set that you want to load for display on the rogue level. -\elist +\end{description} -You can also override one or more symbols using the {\it SYMBOLS\/} and -{\it ROGUESYMBOLS\/} configuration file options. -Symbols are specified as {\it name:value\/} pairs. -Note that {\it NetHack\/} escape-processes -the {\it value\/} string in conventional C fashion. -This means that `\verb+\+' is a prefix to take the following character +You can also override one or more symbols using the \textit{SYMBOLS} and +\textit{ROGUESYMBOLS} configuration file options. +Symbols are specified as \textit{name:value} pairs. +Note that \textit{NetHack} escape-processes +the \textit{value} string in conventional C fashion. +This means that `\texttt{\textbackslash}' is a prefix to take the following character literally. -Thus `\verb+\+' needs to be represented as `\verb+\\+'. +Thus `\texttt{\textbackslash}' needs to be represented as `\texttt{\textbackslash\textbackslash}'. The special prefix -`\verb+\m+' switches on the meta bit in the symbol value, and the -`{\tt \^{}}' prefix causes the following character to be treated as a control +`\texttt{\textbackslash m}' switches on the meta bit in the symbol value, and the +`\texttt{\textasciicircum}' prefix causes the following character to be treated as a control character. -{ \small \begin{longtable}{lll} \caption[]{NetHack Symbols}\\ Default & Symbol Name & Description\\ \hline \hline \endhead -\verb@ @ & S\verb+_+air & (air)\\ -\_ & S\verb+_+altar & (altar)\\ -\verb@"@ & S\verb+_+amulet & (amulet)\\ -\verb@A@ & S\verb+_+angel & (angelic being)\\ -\verb@a@ & S\verb+_+ant & (ant or other insect)\\ -\verb@^@ & S\verb+_+anti\verb+_+magic\verb+_+trap & (anti-magic field)\\ -\verb@[@ & S\verb+_+armor & (suit or piece of armor)\\ -\verb@[@ & S\verb+_+armour & (suit or piece of armor)\\ -\verb@^@ & S\verb+_+arrow\verb+_+trap & (arrow trap)\\ -\verb@0@ & S\verb+_+ball & (iron ball)\\ -\# & S\verb+_+bars & (iron bars)\\ -\verb@B@ & S\verb+_+bat & (bat or bird)\\ -\verb@^@ & S\verb+_+bear\verb+_+trap & (bear trap)\\ -\verb@-@ & S\verb+_+blcorn & (bottom left corner)\\ -\verb@b@ & S\verb+_+blob & (blob)\\ -\verb@+@ & S\verb+_+book & (spellbook)\\ -\verb@)@ & S\verb+_+boomleft & (boomerang open left)\\ -\verb@(@ & S\verb+_+boomright & (boomerang open right)\\ -\verb@`@ & S\verb+_+boulder & (boulder)\\ -\verb@-@ & S\verb+_+brcorn & (bottom right corner)\\ -\verb@>@ & S\verb+_+brdnladder & (branch ladder down)\\ -\verb@>@ & S\verb+_+brdnstair & (branch staircase down)\\ -\verb@<@ & S\verb+_+brupladder & (branch ladder up)\\ -\verb@<@ & S\verb+_+brupstair & (branch staircase up)\\ -\verb@C@ & S\verb+_+centaur & (centaur)\\ -\verb@_@ & S\verb+_+chain & (iron chain)\\ -\# & S\verb+_+cloud & (cloud)\\ -\verb@c@ & S\verb+_+cockatrice & (cockatrice)\\ -\$ & S\verb+_+coin & (pile of coins)\\ -\# & S\verb+_+corr & (corridor)\\ -\verb@-@ & S\verb+_+crwall & (wall)\\ -\verb@-@ & S\verb+_+darkroom & (dark room)\\ -\verb@^@ & S\verb+_+dart\verb+_+trap & (dart trap)\\ -\verb@&@ & S\verb+_+demon & (major demon)\\ -\verb@*@ & S\verb+_+digbeam & (dig beam)\\ -\verb@>@ & S\verb+_+dnladder & (ladder down)\\ -\verb@>@ & S\verb+_+dnstair & (staircase down)\\ -\verb@d@ & S\verb+_+dog & (dog or other canine)\\ -\verb@D@ & S\verb+_+dragon & (dragon)\\ -\verb@;@ & S\verb+_+eel & (sea monster)\\ -\verb@E@ & S\verb+_+elemental & (elemental)\\ -\# & S\verb+_+engrcorr & (engraving in a corridor)\\ -\verb@`@ & S\verb+_+engroom & (engraving in a room)\\ -\verb@/@ & S\verb+_+expl\verb+_+tl & (explosion top left)\\ -\verb@-@ & S\verb+_+expl\verb+_+tc & (explosion top center)\\ -\verb@\@ & S\verb+_+expl\verb+_+tr & (explosion top right)\\ -\verb@|@ & S\verb+_+expl\verb+_+ml & (explosion middle left)\\ -\verb@ @ & S\verb+_+expl\verb+_+mc & (explosion middle center)\\ -\verb@|@ & S\verb+_+expl\verb+_+mr & (explosion middle right)\\ -\verb@\@ & S\verb+_+expl\verb+_+bl & (explosion bottom left)\\ -\verb@-@ & S\verb+_+expl\verb+_+bc & (explosion bottom center)\\ -\verb@/@ & S\verb+_+expl\verb+_+br & (explosion bottom right)\\ -\verb@e@ & S\verb+_+eye & (eye or sphere)\\ -\verb@^@ & S\verb+_+falling\verb+_+rock\verb+_+trap & (falling rock trap)\\ -\verb@f@ & S\verb+_+feline & (cat or other feline)\\ -\verb@^@ & S\verb+_+fire\verb+_+trap & (fire trap)\\ -\verb@!@ & S\verb+_+flashbeam & (flash beam)\\ -\% & S\verb+_+food & (piece of food)\\ -\{ & S\verb+_+fountain & (fountain)\\ -\verb@F@ & S\verb+_+fungus & (fungus or mold)\\ -\verb@*@ & S\verb+_+gem & (gem or rock)\\ -\verb@ @ & S\verb+_+ghost & (ghost)\\ -\verb@H@ & S\verb+_+giant & (giant humanoid)\\ -\verb@G@ & S\verb+_+gnome & (gnome)\\ -\verb@'@ & S\verb+_+golem & (golem)\\ -\verb@|@ & S\verb+_+grave & (grave)\\ -\verb@g@ & S\verb+_+gremlin & (gremlin)\\ -\verb@-@ & S\verb+_+hbeam & (wall)\\ -\# & S\verb+_+hcdbridge & (horizontal raised drawbridge)\\ -\verb@+@ & S\verb+_+hcdoor & (closed door)\\ -\verb@.@ & S\verb+_+hodbridge & (horizontal lowered drawbridge)\\ -\verb@|@ & S\verb+_+hodoor & (open door)\\ -\verb\^\ & S\verb+_+hole & (hole)\\ -\verb~@~ & S\verb+_+human & (human or elf)\\ -\verb@h@ & S\verb+_+humanoid & (humanoid)\\ -\verb@-@ & S\verb+_+hwall & (horizontal wall)\\ -\verb@.@ & S\verb+_+ice & (ice)\\ -\verb@i@ & S\verb+_+imp & (imp or minor demon)\\ -\verb@I@ & S\verb+_+invisible & (invisible monster)\\ -\verb@J@ & S\verb+_+jabberwock & (jabberwock)\\ -\verb@j@ & S\verb+_+jelly & (jelly)\\ -\verb@k@ & S\verb+_+kobold & (kobold)\\ -\verb@K@ & S\verb+_+kop & (Keystone Kop)\\ -\verb@^@ & S\verb+_+land\verb+_+mine & (land mine)\\ -\verb@}@ & S\verb+_+lava & (molten lava)\\ -\verb@}@ & S\verb+_+lavawall & (wall of lava)\\ -\verb@l@ & S\verb+_+leprechaun & (leprechaun)\\ -\verb@^@ & S\verb+_+level\verb+_+teleporter & (level teleporter)\\ -\verb@L@ & S\verb+_+lich & (lich)\\ -\verb@y@ & S\verb+_+light & (light)\\ -\# & S\verb+_+litcorr & (lit corridor)\\ -\verb@:@ & S\verb+_+lizard & (lizard)\\ -\verb@\@ & S\verb+_+lslant & (wall)\\ -\verb@^@ & S\verb+_+magic\verb+_+portal & (magic portal)\\ -\verb@^@ & S\verb+_+magic\verb+_+trap & (magic trap)\\ -\verb@m@ & S\verb+_+mimic & (mimic)\\ -\verb@]@ & S\verb+_+mimic\verb+_+def & (mimic)\\ -\verb@M@ & S\verb+_+mummy & (mummy)\\ -\verb@N@ & S\verb+_+naga & (naga)\\ -\verb@.@ & S\verb+_+ndoor & (doorway)\\ -\verb@n@ & S\verb+_+nymph & (nymph)\\ -\verb@O@ & S\verb+_+ogre & (ogre)\\ -\verb@o@ & S\verb+_+orc & (orc)\\ -\verb@p@ & S\verb+_+piercer & (piercer)\\ -\verb@^@ & S\verb+_+pit & (pit)\\ -\# & S\verb+_+poisoncloud & (poison cloud)\\ -\verb@^@ & S\verb+_+polymorph\verb+_+trap & (polymorph trap)\\ -\verb@}@ & S\verb+_+pool & (water)\\ -\verb@!@ & S\verb+_+potion & (potion)\\ -\verb@P@ & S\verb+_+pudding & (pudding or ooze)\\ -\verb@q@ & S\verb+_+quadruped & (quadruped)\\ -\verb@Q@ & S\verb+_+quantmech & (quantum mechanic)\\ -\verb@=@ & S\verb+_+ring & (ring)\\ -\verb@`@ & S\verb+_+rock & (boulder or statue)\\ -\verb@r@ & S\verb+_+rodent & (rodent)\\ -\verb@^@ & S\verb+_+rolling\verb+_+boulder\verb+_+trap & (rolling boulder trap)\\ -\verb@.@ & S\verb+_+room & (floor of a room)\\ -\verb@/@ & S\verb+_+rslant & (wall)\\ -\verb@^@ & S\verb+_+rust\verb+_+trap & (rust trap)\\ -\verb@R@ & S\verb+_+rustmonst & (rust monster or disenchanter)\\ -\verb@?@ & S\verb+_+scroll & (scroll)\\ -\# & S\verb+_+sink & (sink)\\ -\verb@^@ & S\verb+_+sleeping\verb+_+gas\verb+_+trap & (sleeping gas trap)\\ -\verb@S@ & S\verb+_+snake & (snake)\\ -\verb@s@ & S\verb+_+spider & (arachnid or centipede)\\ -\verb@^@ & S\verb+_+spiked\verb+_+pit & (spiked pit)\\ -\verb@^@ & S\verb+_+squeaky\verb+_+board & (squeaky board)\\ -\verb@0@ & S\verb+_+ss1 & (magic shield 1 of 4)\\ -\# & S\verb+_+ss2 & (magic shield 2 of 4)\\ -\verb+@+ & S\verb+_+ss3 & (magic shield 3 of 4)\\ -\verb@*@ & S\verb+_+ss4 & (magic shield 4 of 4)\\ -\verb@^@ & S\verb+_+statue\verb+_+trap & (statue trap)\\ -\verb@ @ & S\verb+_+stone & (solid rock)\\ -\verb@]@ & S\verb+_+strange\verb+_+obj & (strange object)\\ -\verb@-@ & S\verb+_+sw\verb+_+bc & (swallow bottom center)\\ -\verb@\@ & S\verb+_+sw\verb+_+bl & (swallow bottom left)\\ -\verb@/@ & S\verb+_+sw\verb+_+br & (swallow bottom right )\\ -\verb@|@ & S\verb+_+sw\verb+_+ml & (swallow middle left)\\ -\verb@|@ & S\verb+_+sw\verb+_+mr & (swallow middle right)\\ -\verb@-@ & S\verb+_+sw\verb+_+tc & (swallow top center)\\ -\verb@/@ & S\verb+_+sw\verb+_+tl & (swallow top left)\\ -\verb@\@ & S\verb+_+sw\verb+_+tr & (swallow top right)\\ -\verb@-@ & S\verb+_+tdwall & (wall)\\ -\verb@^@ & S\verb+_+teleportation\verb+_+trap & (teleportation trap)\\ -\verb@\@ & S\verb+_+throne & (opulent throne)\\ -\verb@-@ & S\verb+_+tlcorn & (top left corner)\\ -\verb@|@ & S\verb+_+tlwall & (wall)\\ -\verb@(@ & S\verb+_+tool & (useful item (pick-axe, key, lamp...))\\ -\verb@^@ & S\verb+_+trap\verb+_+door & (trap door)\\ -\verb@t@ & S\verb+_+trapper & (trapper or lurker above)\\ -\verb@-@ & S\verb+_+trcorn & (top right corner)\\ -\# & S\verb+_+tree & (tree)\\ -\verb@T@ & S\verb+_+troll & (troll)\\ -\verb@|@ & S\verb+_+trwall & (wall)\\ -\verb@-@ & S\verb+_+tuwall & (wall)\\ -\verb@U@ & S\verb+_+umber & (umber hulk)\\ -\verb@ @ & S\verb+_+unexplored & (unexplored terrain)\\ -\verb@u@ & S\verb+_+unicorn & (unicorn or horse)\\ -\verb@<@ & S\verb+_+upladder & (ladder up)\\ -\verb@<@ & S\verb+_+upstair & (staircase up)\\ -\verb@V@ & S\verb+_+vampire & (vampire)\\ -\verb@|@ & S\verb+_+vbeam & (wall)\\ -\# & S\verb+_+vcdbridge & (vertical raised drawbridge)\\ -\verb@+@ & S\verb+_+vcdoor & (closed door)\\ -\verb@.@ & S\verb+_+venom & (splash of venom)\\ -\verb@^@ & S\verb+_+vibrating\verb+_+square & (vibrating square)\\ -\verb@.@ & S\verb+_+vodbridge & (vertical lowered drawbridge)\\ -\verb@-@ & S\verb+_+vodoor & (open door)\\ -\verb@v@ & S\verb+_+vortex & (vortex)\\ -\verb@|@ & S\verb+_+vwall & (vertical wall)\\ -\verb@/@ & S\verb+_+wand & (wand)\\ -\verb@}@ & S\verb+_+water & (water)\\ -\verb@)@ & S\verb+_+weapon & (weapon)\\ -\verb@"@ & S\verb+_+web & (web)\\ -\verb@w@ & S\verb+_+worm & (worm)\\ -\verb@~@ & S\verb+_+worm\verb+_+tail & (long worm tail)\\ -\verb@W@ & S\verb+_+wraith & (wraith)\\ -\verb@x@ & S\verb+_+xan & (xan or other extraordinary insect)\\ -\verb@X@ & S\verb+_+xorn & (xorn)\\ -\verb@Y@ & S\verb+_+yeti & (apelike creature)\\ -\verb@Z@ & S\verb+_+zombie & (zombie)\\ -\verb@z@ & S\verb+_+zruty & (zruty)\\ -\verb@ @ & S\verb+_+pet\verb+_+override & (any pet if ACCESSIBILITY=1 is set)\\ -\verb@ @ & S\verb+_+hero\verb+_+override & (hero if ACCESSIBILITY=1 is set) -\end{longtable}% -} +\space @ & S\textunderscore air & (air)\\ +\textunderscore & S\textunderscore altar & (altar)\\ +" & S\textunderscore amulet & (amulet)\\ +A & S\textunderscore angel & (angelic being)\\ +a & S\textunderscore ant & (ant or other insect)\\ +\textasciicircum & S\textunderscore anti\textunderscore magic\textunderscore trap & (anti-magic field)\\ +{[} & S\textunderscore armor & (suit or piece of armor)\\ +{[} & S\textunderscore armour & (suit or piece of armor)\\ +\textasciicircum & S\textunderscore arrow\textunderscore trap & (arrow trap)\\ +0 & S\textunderscore ball & (iron ball)\\ +\# & S\textunderscore bars & (iron bars)\\ +B & S\textunderscore bat & (bat or bird)\\ +\textasciicircum & S\textunderscore bear\textunderscore trap & (bear trap)\\ +- & S\textunderscore blcorn & (bottom left corner)\\ +b & S\textunderscore blob & (blob)\\ ++ & S\textunderscore book & (spellbook)\\ +) & S\textunderscore boomleft & (boomerang open left)\\ +( & S\textunderscore boomright & (boomerang open right)\\ +\textasciigrave & S\textunderscore boulder & (boulder)\\ +- & S\textunderscore brcorn & (bottom right corner)\\ +> & S\textunderscore brdnladder & (branch ladder down)\\ +> & S\textunderscore brdnstair & (branch staircase down)\\ +< & S\textunderscore brupladder & (branch ladder up)\\ +< & S\textunderscore brupstair & (branch staircase up)\\ +C & S\textunderscore centaur & (centaur)\\ +\textunderscore & S\textunderscore chain & (iron chain)\\ +\# & S\textunderscore cloud & (cloud)\\ +c & S\textunderscore cockatrice & (cockatrice)\\ +\textdollar & S\textunderscore coin & (pile of coins)\\ +\# & S\textunderscore corr & (corridor)\\ +- & S\textunderscore crwall & (wall)\\ +- & S\textunderscore darkroom & (dark room)\\ +\textasciicircum & S\textunderscore dart\textunderscore trap & (dart trap)\\ +\& & S\textunderscore demon & (major demon)\\ +* & S\textunderscore digbeam & (dig beam)\\ +> & S\textunderscore dnladder & (ladder down)\\ +> & S\textunderscore dnstair & (staircase down)\\ +d & S\textunderscore dog & (dog or other canine)\\ +D & S\textunderscore dragon & (dragon)\\ +; & S\textunderscore eel & (sea monster)\\ +E & S\textunderscore elemental & (elemental)\\ +\# & S\textunderscore engrcorr & (engraving in a corridor)\\ +\textasciigrave & S\textunderscore engroom & (engraving in a room)\\ +/ & S\textunderscore expl\textunderscore tl & (explosion top left)\\ +- & S\textunderscore expl\textunderscore tc & (explosion top center)\\ +\textbackslash & S\textunderscore expl\textunderscore tr & (explosion top right)\\ +| & S\textunderscore expl\textunderscore ml & (explosion middle left)\\ +~ & S\textunderscore expl\textunderscore mc & (explosion middle center)\\ +| & S\textunderscore expl\textunderscore mr & (explosion middle right)\\ +\textbackslash & S\textunderscore expl\textunderscore bl & (explosion bottom left)\\ +- & S\textunderscore expl\textunderscore bc & (explosion bottom center)\\ +/ & S\textunderscore expl\textunderscore br & (explosion bottom right)\\ +e & S\textunderscore eye & (eye or sphere)\\ +\textasciicircum & S\textunderscore falling\textunderscore rock\textunderscore trap & (falling rock trap)\\ +f & S\textunderscore feline & (cat or other feline)\\ +\textasciicircum & S\textunderscore fire\textunderscore trap & (fire trap)\\ +! & S\textunderscore flashbeam & (flash beam)\\ +\% & S\textunderscore food & (piece of food)\\ +\textbraceleft & S\textunderscore fountain & (fountain)\\ +F & S\textunderscore fungus & (fungus or mold)\\ +* & S\textunderscore gem & (gem or rock)\\ +~ & S\textunderscore ghost & (ghost)\\ +H & S\textunderscore giant & (giant humanoid)\\ +G & S\textunderscore gnome & (gnome)\\ +\textquotesingle & S\textunderscore golem & (golem)\\ +| & S\textunderscore grave & (grave)\\ +g & S\textunderscore gremlin & (gremlin)\\ +- & S\textunderscore hbeam & (wall)\\ +\# & S\textunderscore hcdbridge & (horizontal raised drawbridge)\\ ++ & S\textunderscore hcdoor & (closed door)\\ +. & S\textunderscore hodbridge & (horizontal lowered drawbridge)\\ +| & S\textunderscore hodoor & (open door)\\ +\textasciicircum & S\textunderscore hole & (hole)\\ +@ & S\textunderscore human & (human or elf)\\ +h & S\textunderscore humanoid & (humanoid)\\ +- & S\textunderscore hwall & (horizontal wall)\\ +. & S\textunderscore ice & (ice)\\ +i & S\textunderscore imp & (imp or minor demon)\\ +I & S\textunderscore invisible & (invisible monster)\\ +J & S\textunderscore jabberwock & (jabberwock)\\ +j & S\textunderscore jelly & (jelly)\\ +k & S\textunderscore kobold & (kobold)\\ +K & S\textunderscore kop & (Keystone Kop)\\ +\textasciicircum & S\textunderscore land\textunderscore mine & (land mine)\\ +\textbraceright & S\textunderscore lava & (molten lava)\\ +\textbraceright & S\textunderscore lavawall & (wall of lava)\\ +1 & S\textunderscore leprechaun & (leprechaun)\\ +\textasciicircum & S\textunderscore level\textunderscore teleporter & (level teleporter)\\ +L & S\textunderscore lich & (lich)\\ +y & S\textunderscore light & (light)\\ +\# & S\textunderscore litcorr & (lit corridor)\\ +: & S\textunderscore lizard & (lizard)\\ +\textbackslash & S\textunderscore lslant & (wall)\\ +\textasciicircum & S\textunderscore magic\textunderscore portal & (magic portal)\\ +\textasciicircum & S\textunderscore magic\textunderscore trap & (magic trap)\\ +m & S\textunderscore mimic & (mimic)\\ +{]} & S\textunderscore mimic\textunderscore def & (mimic)\\ +M & S\textunderscore mummy & (mummy)\\ +N & S\textunderscore naga & (naga)\\ +. & S\textunderscore ndoor & (doorway)\\ +n & S\textunderscore nymph & (nymph)\\ +O & S\textunderscore ogre & (ogre)\\ +o & S\textunderscore orc & (orc)\\ +p & S\textunderscore piercer & (piercer)\\ +\textasciicircum & S\textunderscore pit & (pit)\\ +\# & S\textunderscore poisoncloud & (poison cloud)\\ +\textasciicircum & S\textunderscore polymorph\textunderscore trap & (polymorph trap)\\ +\textbraceright & S\textunderscore pool & (water)\\ +! & S\textunderscore potion & (potion)\\ +P & S\textunderscore pudding & (pudding or ooze)\\ +q & S\textunderscore quadruped & (quadruped)\\ +Q & S\textunderscore quantmech & (quantum mechanic)\\ += & S\textunderscore ring & (ring)\\ +\textasciigrave & S\textunderscore rock & (boulder or statue)\\ +r & S\textunderscore rodent & (rodent)\\ +\textasciicircum & S\textunderscore rolling\textunderscore boulder\textunderscore trap & (rolling boulder trap)\\ +. & S\textunderscore room & (floor of a room)\\ +/ & S\textunderscore rslant & (wall)\\ +\textasciicircum & S\textunderscore rust\textunderscore trap & (rust trap)\\ +R & S\textunderscore rustmonst & (rust monster or disenchanter)\\ +? & S\textunderscore scroll & (scroll)\\ +\# & S\textunderscore sink & (sink)\\ +\textasciicircum & S\textunderscore sleeping\textunderscore gas\textunderscore trap & (sleeping gas trap)\\ +S & S\textunderscore snake & (snake)\\ +s & S\textunderscore spider & (arachnid or centipede)\\ +\textasciicircum & S\textunderscore spiked\textunderscore pit & (spiked pit)\\ +\textasciicircum & S\textunderscore squeaky\textunderscore board & (squeaky board)\\ +0 & S\textunderscore ss1 & (magic shield 1 of 4)\\ +\# & S\textunderscore ss2 & (magic shield 2 of 4)\\ +@ & S\textunderscore ss3 & (magic shield 3 of 4)\\ +* & S\textunderscore ss4 & (magic shield 4 of 4)\\ +\textasciicircum & S\textunderscore statue\textunderscore trap & (statue trap)\\ +~ & S\textunderscore stone & (solid rock)\\ +{]} & S\textunderscore strange\textunderscore obj & (strange object)\\ +- & S\textunderscore sw\textunderscore bc & (swallow bottom center)\\ +\textbackslash & S\textunderscore sw\textunderscore bl & (swallow bottom left)\\ +/ & S\textunderscore sw\textunderscore br & (swallow bottom right )\\ +| & S\textunderscore sw\textunderscore ml & (swallow middle left)\\ +| & S\textunderscore sw\textunderscore mr & (swallow middle right)\\ +- & S\textunderscore sw\textunderscore tc & (swallow top center)\\ +/ & S\textunderscore sw\textunderscore tl & (swallow top left)\\ +\textbackslash & S\textunderscore sw\textunderscore tr & (swallow top right)\\ +- & S\textunderscore tdwall & (wall)\\ +\textasciicircum & S\textunderscore teleportation\textunderscore trap & (teleportation trap)\\ +\textbackslash & S\textunderscore throne & (opulent throne)\\ +- & S\textunderscore tlcorn & (top left corner)\\ +| & S\textunderscore tlwall & (wall)\\ +( & S\textunderscore tool & (useful item (pick-axe, key, lamp...))\\ +\textasciicircum & S\textunderscore trap\textunderscore door & (trap door)\\ +t & S\textunderscore trapper & (trapper or lurker above)\\ +- & S\textunderscore trcorn & (top right corner)\\ +\# & S\textunderscore tree & (tree)\\ +T & S\textunderscore troll & (troll)\\ +| & S\textunderscore trwall & (wall)\\ +- & S\textunderscore tuwall & (wall)\\ +U & S\textunderscore umber & (umber hulk)\\ +~ & S\textunderscore unexplored & (unexplored terrain)\\ +u & S\textunderscore unicorn & (unicorn or horse)\\ +< & S\textunderscore upladder & (ladder up)\\ +< & S\textunderscore upstair & (staircase up)\\ +V & S\textunderscore vampire & (vampire)\\ +| & S\textunderscore vbeam & (wall)\\ +\# & S\textunderscore vcdbridge & (vertical raised drawbridge)\\ ++ & S\textunderscore vcdoor & (closed door)\\ +. & S\textunderscore venom & (splash of venom)\\ +\textasciicircum & S\textunderscore vibrating\textunderscore square & (vibrating square)\\ +. & S\textunderscore vodbridge & (vertical lowered drawbridge)\\ +- & S\textunderscore vodoor & (open door)\\ +v & S\textunderscore vortex & (vortex)\\ +| & S\textunderscore vwall & (vertical wall)\\ +/ & S\textunderscore wand & (wand)\\ +\textbraceright & S\textunderscore water & (water)\\ +\textbraceright & S\textunderscore weapon & (weapon)\\ +" & S\textunderscore web & (web)\\ +w & S\textunderscore worm & (worm)\\ +\textasciitilde & S\textunderscore worm\textunderscore tail & (long worm tail)\\ +W & S\textunderscore wraith & (wraith)\\ +x & S\textunderscore xan & (xan or other extraordinary insect)\\ +X & S\textunderscore xorn & (xorn)\\ +Y & S\textunderscore yeti & (apelike creature)\\ +Z & S\textunderscore zombie & (zombie)\\ +z & S\textunderscore zruty & (zruty)\\ +~ & S\textunderscore pet\textunderscore override & (any pet if ACCESSIBILITY=1 is set)\\ +~ @ & S\textunderscore hero\textunderscore override & (hero if ACCESSIBILITY=1 is set) +\end{longtable} \hyphenation{sysconf} %no syllable breaks => don't hyphenate file name %.lp @@ -6581,16 +6650,16 @@ Notes: %.lp "*" Several symbols in this table appear to be blank. -They are the space character, except for S\verb+_+pet\verb+_+override -and S\verb+_+hero\verb+_+override which don't have any default value +They are the space character, except for S\textunderscore pet\textunderscore override +and S\textunderscore hero\textunderscore override which don't have any default value and can only be used if enabled in the ``sysconf'' file. %.lp "*" -S\verb+_+rock is misleadingly named; rocks and stones use S\verb+_+gem. +S\textunderscore rock is misleadingly named; rocks and stones use S\textunderscore gem. Statues and boulders are the rock being referred to, but since version 3.6.0, statues are displayed as the monster they depict. -So S\verb+_+rock is only used for boulders and not used at all if -overridden by the more specific S\verb+_+boulder. +So S\textunderscore rock is only used for boulders and not used at all if +overridden by the more specific S\textunderscore boulder. %.lp %.hn 2 @@ -6614,7 +6683,7 @@ character sequences and explicit 24-bit red-green-blue colors in order for the g representation to be visible as specified. For example, the following line in your configuration file will cause -the glyph representation for glyphid G\verb+_+pool to use Unicode codepoint +the glyph representation for glyphid G\textunderscore pool to use Unicode codepoint U+224B and the color represented by R-G-B value 0-0-160:\\ \begin{verbatim} OPTIONS=glyph:G_pool/U+224B/0-0-160 @@ -6624,8 +6693,8 @@ The list of acceptable glyphid's can be produced by \begin{verbatim} nethack --glyphids \end{verbatim} -Individual NetHack glyphs can be specified using the G\verb+_+ prefix, -or you can use an S\verb+_+ symbol for a glyphid and store the custom +Individual NetHack glyphs can be specified using the G\textunderscore prefix, +or you can use an S\textunderscore symbol for a glyphid and store the custom representation for all NetHack glyphs that would map to that particular symbol. @@ -6634,11 +6703,11 @@ display of the customizations, such as the Enhanced symset. %.pg %.hn 2 -\subsection*{Configuring {\it NetHack\/} for Play by the Blind} +\subsection*{Configuring \textit{NetHack} for Play by the Blind} %.pg -{\it NetHack\/} can be set up to use only standard ASCII characters for making -maps of the dungeons. This makes even the MS-DOS versions of {\it NetHack} +\textit{NetHack} can be set up to use only standard ASCII characters for making +maps of the dungeons. This makes even the MS-DOS versions of \textit{NetHack} (which use special line-drawing characters by default) completely accessible to the blind who use speech and/or Braille access technologies. Players will require a good working knowledge of their screen-reader's @@ -6654,196 +6723,196 @@ gives you the row and column of your review cursor and the PC cursor. These co-ordinates are often useful in giving players a better sense of the overall location of items on the screen. %.pg -{\it NetHack\/} can also be compiled with support for sending the game +\textit{NetHack} can also be compiled with support for sending the game messages to an external program, such as a text-to-speech synthesizer. If -the ``{\tt \#version}'' extended command shows ``external program as a -message handler'', your {\it NetHack\/} -has been compiled with the capability. When compiling {\it NetHack\/} +the ``\texttt{\#version}'' extended command shows ``external program as a +message handler'', your \textit{NetHack} +has been compiled with the capability. When compiling \textit{NetHack} from source -on Linux and other POSIX systems, define {\tt MSGHANDLER\/} to enable it. +on Linux and other POSIX systems, define \texttt{MSGHANDLER} to enable it. To use -the capability, set the environment variable {\tt NETHACK\_MSGHANDLER\/} to +the capability, set the environment variable \texttt{NETHACK\textunderscore MSGHANDLER} to an executable, which will be executed with the game message as the program's only parameter. %.pg The most crucial settings to make the game more accessible are: %.pg -\blist{} +\begin{description}[font=\mdseries\itshape] %.lp -\item[\ib{symset:plain}] +\item[symset:plain] Load a symbol set appropriate for use by blind players. %.lp -\item[\ib{menustyle:traditional}] +\item[menustyle:traditional] This will assist in the interface to speech synthesizers. %.lp -\item[\ib{nomenu\verb+_+overlay}] +\item[nomenu\textunderscore overlay] Show menus on a cleared screen and aligned to the left edge. %.lp -\item[\ib{number\verb+_+pad}] +\item[number\textunderscore pad] A lot of speech access programs use the number-pad to review the screen. -If this is the case, disable the number\verb+_+pad option and use the +If this is the case, disable the number\textunderscore pad option and use the traditional Rogue-like commands. %.lp -\item[\ib{paranoid\verb+_+confirmation:swim}] +\item[paranoid\textunderscore confirmation:swim] Prevent walking into water or lava. %.lp -\item[\ib{accessiblemsg}] +\item[accessiblemsg] Adds direction or location information to messages. %.lp -\item[\ib{spot\verb+_+monsters}] +\item[spot\textunderscore monsters] Shows a message when hero notices a monster; combine with accessiblemsg. %.lp -\item[\ib{mon\verb+_+movement}] +\item[mon\textunderscore movement] Shows a message when hero notices a monster movement; -combine with spot\verb+_+monsters and accessiblemsg. +combine with spot\textunderscore monsters and accessiblemsg. %.lp -\item[\ib{autodescribe}] +\item[autodescribe] Automatically describe the terrain under the cursor when targeting. %.lp -\item[\ib{mention\verb+_+map}] +\item[mention\textunderscore map] Give feedback messages when interesting map locations change. %.lp -\item[\ib{mention\verb+_+walls}] +\item[mention\textunderscore walls] Give feedback messages when walking towards a wall or when travel command was interrupted. %.lp -\item[\ib{whatis\verb+_+coord:compass}] +\item[whatis\textunderscore coord:compass] When targeting with cursor, describe the cursor position with coordinates relative to your character. %.lp -\item[\ib{whatis\verb+_+filter:area}] +\item[whatis\textunderscore filter:area] When targeting with cursor, filter possible locations so only those in the same area (eg. same room, or same corridor) are considered. %.lp -\item[\ib{whatis\verb+_+moveskip}] +\item[whatis\textunderscore moveskip] When targeting with cursor and using fast-move, skip the same glyphs instead of moving 8 units at a time. %.lp -\item[\ib{nostatus\verb+_+updates}] +\item[nostatus\textunderscore updates] Prevent updates to the status lines at the bottom of the screen, if your screen-reader reads those lines. The same information can be -seen via the {\tt \#attributes} command. +seen via the \texttt{\#attributes} command. %.lp -\item[\ib{showdamage}] +\item[showdamage] Give a message of damage taken and how many hit points are left. -\elist +\end{description} %.hn2 \subsection*{Global Configuration for System Administrators} %.pg -If {\it NetHack\/} is compiled with the SYSCF option, a system administrator +If \textit{NetHack} is compiled with the SYSCF option, a system administrator should set up a global configuration; this is a file in the same format as the traditional per-user configuration file (see above). This file should be named sysconf and placed in the same directory as -the other {\it NetHack\/} support files. +the other \textit{NetHack} support files. The options recognized in this file are listed below. Any option not set uses a compiled-in default (which may not be appropriate for your system). %.pg -\blist{} +\begin{description}[font=\mdseries\itshape] %.lp -\item[\ib{WIZARDS}] +\item[WIZARDS] A space-separated list of user name who are allowed to play in debug mode (commonly referred to as wizard mode). A value of a single asterisk (*) allows anyone to start a game in debug mode. %.lp -\item[\ib{SHELLERS}] -A list of users who are allowed to use the shell escape command (`{\tt !}'). +\item[SHELLERS] +A list of users who are allowed to use the shell escape command (`\texttt{!}'). The syntax is the same as WIZARDS. %.lp -\item[\ib{EXPLORERS}] +\item[EXPLORERS] A list of users who are allowed to use the explore mode. The syntax is the same as WIZARDS. %.lp -\item[\ib{MSGHANDLER}] +\item[MSGHANDLER] A path and filename of executable. Whenever a message-window message is shown, NetHack runs this program. The program will get the message as the only parameter. %.lp -\item[\ib{MAXPLAYERS}] +\item[MAXPLAYERS] Limit the maximum number of games that can be running at the same time. %.lp -\item[\ib{SUPPORT}] +\item[SUPPORT] A string explaining how to get local support (no default value). %.lp -\item[\ib{RECOVER}] +\item[RECOVER] A string explaining how to recover a game on this system (no default value). %.lp -\item[\ib{SEDUCE}] +\item[SEDUCE] 0 or 1 to disable or enable, respectively, the SEDUCE option. When disabled, incubi and succubi behave like nymphs. %.lp -\item[\ib{CHECK\verb+_+PLNAME}] +\item[CHECK\textunderscore PLNAME] Setting this to 1 will make the EXPLORERS, WIZARDS, and SHELLERS check for the player name instead of the user's login name. %.lp -\item[\ib{CHECK\verb+_+SAVE\verb+_+UID}] +\item[CHECK\textunderscore SAVE\textunderscore UID] 0 or 1 to disable or enable, respectively, the UID (used identification number) checking for save files (to verify that the user who is restoring is the same one who saved). -\elist +\end{description} %.pg The following four options affect the score file: -\blist {} +\begin{description}[font=\mdseries\itshape] %.pg %.lp -\item[\ib{PERSMAX}] +\item[PERSMAX] Maximum number of entries for one person. %.lp -\item[\ib{ENTRYMAX}] +\item[ENTRYMAX] Maximum number of entries in the score file. %.lp -\item[\ib{POINTSMIN}] +\item[POINTSMIN] Minimum number of points to get an entry in the score file. %.lp -\item[\ib{PERS\verb+_+IS\verb+_+UID}] +\item[PERS\textunderscore IS\textunderscore UID] 0 or 1 to use user names or numeric userids, respectively, to identify unique people for the score file. %.lp -\item[\ib{HIDEUSAGE}] +\item[HIDEUSAGE] 0 or 1 to control whether the help menu entry for command line usage is shown or suppressed. %.lp -\item[\ib{MAX\verb+_+STATUENAME\verb+_+RANK}] +\item[MAX\textunderscore STATUENAME\textunderscore RANK] Maximum number of score file entries to use for random statue names (default is 10). %.lp -\item[\ib{ACCESSIBILITY}] +\item[ACCESSIBILITY] 0 or 1 to disable or enable, respectively, the ability for players -to set S\verb+_+pet\verb+_+override and S\verb+_+hero\verb+_+override +to set S\textunderscore pet\textunderscore override and S\textunderscore hero\textunderscore override symbols in their configuration file. %.lp -\item[\ib{PORTABLE\verb+_+DEVICE\verb+_+PATHS}] +\item[PORTABLE\textunderscore DEVICE\textunderscore PATHS] 0 or 1 Windows OS only, the game will look for all of its external files, and write to all of its output files in one place rather than at the standard locations. %.lp -\item[\ib{DUMPLOGFILE}] +\item[DUMPLOGFILE] A filename where the end-of-game dumplog is saved. Not defining this will prevent dumplog from being created. Only available if your game is compiled with DUMPLOG. Allows the following placeholders: % FIXME: this should be changed to a nested list or else be forcibly indented -{\tt \%\%} --- literal `{\tt \%}'\\ -{\tt \%v} --- version (eg. ``{\tt 3.7.0-0}'')\\ -{\tt \%u} --- game UID\\ -{\tt \%t} --- game start time, UNIX timestamp format\\ -{\tt \%T} --- current time, UNIX timestamp format\\ -{\tt \%d} --- game start time, YYYYMMDDhhmmss format\\ -{\tt \%D} --- current time, YYYYMMDDhhmmss format\\ -{\tt \%n} --- player name\\ -{\tt \%N} --- first character of player name +\texttt{\%\%} --- literal `\texttt{\%}'\\ +\texttt{\%v} --- version (eg. ``\texttt{3.7.0-0}'')\\ +\texttt{\%u} --- game UID\\ +\texttt{\%t} --- game start time, UNIX timestamp format\\ +\texttt{\%T} --- current time, UNIX timestamp format\\ +\texttt{\%d} --- game start time, YYYYMMDDhhmmss format\\ +\texttt{\%D} --- current time, YYYYMMDDhhmmss format\\ +\texttt{\%n} --- player name\\ +\texttt{\%N} --- first character of player name %.lp -\item[\ib{LIVELOG}] +\item[LIVELOG] A bit-mask of types of events that should be written to -the {\it livelog\/} file if one is present. -The sample {\it sysconf\/} file accompanying the program contains a +the \textit{livelog} file if one is present. +The sample \textit{sysconf} file accompanying the program contains a comment which lists the meaning of the various bits used. Intended for server systems supporting simultaneous play by multiple players (to be clear, each one running a separate single player game), @@ -6853,26 +6922,26 @@ When available, it should be left commented out on single player installations because over time the file could grow to be extremely large unless it is actively maintained. %.lp -\item[\ib{CRASHREPORTURL}] +\item[CRASHREPORTURL] If set to -{\tt https://www.nethack.org/links/cr-37BETA.html} +\url{https://www.nethack.org/links/cr-37BETA.html} and support is compiled in, brings up a browser window populated with the information needed to report a problem if the game panics or ends up in an internally inconsistent state, or if the \#bugreport command is invoked. -\elist +\end{description} %.hn 1 \section{Scoring} %.pg -{\it NetHack\/} maintains a list of the top scores or scorers on your machine, +\textit{NetHack} maintains a list of the top scores or scorers on your machine, depending on how it is set up. In the latter case, each account on the machine can post only one non-winning score on this list. If you score higher than someone else on this list, or better your previous score, you will be inserted in the proper place under your current name. How many scores are kept can also be set up when -{\it NetHack\/} is compiled. +\textit{NetHack} is compiled. %.pg Your score is chiefly based upon how much experience you gained, how @@ -6897,7 +6966,7 @@ on most versions. \section{Explore mode} %.pg -{\it NetHack\/} is an intricate and difficult game. Novices might falter +\textit{NetHack} is an intricate and difficult game. Novices might falter in fear, aware of their ignorance of the means to survive. Well, fear not. Your dungeon comes equipped with an ``explore'' or ``discovery'' mode that enables you to keep old save files and cheat death, at the @@ -6905,10 +6974,10 @@ paltry cost of not getting on the high score list. %.pg There are two ways of enabling explore mode. One is to start the game -with the {\tt -X} +with the \texttt{-X} command-line switch or with the -{\it playmode:explore\/} -option. The other is to issue the `{\tt \#exploremode}' extended command while +\textit{playmode:explore} +option. The other is to issue the `\texttt{\#exploremode}' extended command while already playing the game. Starting a new game in explore mode provides your character with a wand of wishing in initial inventory; switching during play does not. The other benefits of explore mode are left for @@ -6927,16 +6996,16 @@ program rather than to provide god-like powers to your character, and players who attempt debugging are expected to figure out how to use it themselves. It is initiated by starting the game with the -{\tt -D} +\texttt{-D} command-line switch or with the -{\it playmode:debug\/} +\textit{playmode:debug} option. %.pg For some systems, the player must be logged in under a particular user name to be allowed to use debug mode; for others, the hero must be given a particular character name (but may be any role; -there's no connection between ``wizard mode'' and the {\it Wizard\/} role). +there's no connection between ``wizard mode'' and the \textit{Wizard} role). Attempting to start a game in debug mode when not allowed or not available will result in falling back to explore mode instead. @@ -6944,85 +7013,85 @@ or not available will result in falling back to explore mode instead. \section{Credits} %.pg The original % -{\it hack\/} game was modeled on the Berkeley +\textit{hack} game was modeled on the Berkeley %.ux UNIX -{\it rogue\/} game. Large portions of this document were shamelessly +\textit{rogue} game. Large portions of this document were shamelessly cribbed from % -{\it A Guide to the Dungeons of Doom}, by Michael C. Toy +\textit{A Guide to the Dungeons of Doom}, by Michael C. Toy and Kenneth C. R. C. Arnold. Small portions were adapted from -{\it Further Exploration of the Dungeons of Doom}, by Ken Arromdee. +\textit{Further Exploration of the Dungeons of Doom}, by Ken Arromdee. %.pg -{\it NetHack\/} is the product of literally scores of people's work. +\textit{NetHack} is the product of literally scores of people's work. Main events in the course of the game development are described below: %.pg \bigskip -\nd {\it Jay Fenlason\/} wrote the original {\it Hack}, with help from {\it -Kenny Woodland}, {\it Mike Thome}, and {\it Jon Payne}. +\noindent \textit{Jay Fenlason} wrote the original \textit{Hack}, with help from {\it +Kenny Woodland}, \textit{Mike Thome}, and \textit{Jon Payne}. %.pg \medskip -\nd {\it Andries Brouwer\/} did a major re-write while at +\noindent \textit{Andries Brouwer} did a major re-write while at Stichting Mathematisch Centrum (now Centrum Wiskunde \& Informatica), transforming Hack into a very different game. He published the Hack source code for use on UNIX systems by posting that to Usenet -newsgroup {\it net.sources\/} (later renamed {\it comp.sources}) +newsgroup \textit{net.sources} (later renamed \textit{comp.sources}) releasing version 1.0 in December of 1984, then versions 1.0.1, 1.0.2, and finally 1.0.3 in July of 1985. -Usenet newsgroup {\it net.games.hack\/} (later -renamed {\it rec.games.hack}, eventually replaced -by {\it rec.games.roguelike.nethack}) +Usenet newsgroup \textit{net.games.hack} (later +renamed \textit{rec.games.hack}, eventually replaced +by \textit{rec.games.roguelike.nethack}) was created for discussing it. %.pg \medskip -\nd {\it Don G. Kneller\/} ported {\it Hack\/} 1.0.3 to Microsoft C and MS-DOS, -producing {\it PC Hack\/} 1.01e, added support for DEC Rainbow graphics in +\noindent \textit{Don G. Kneller} ported \textit{Hack} 1.0.3 to Microsoft C and MS-DOS, +producing \textit{PC Hack} 1.01e, added support for DEC Rainbow graphics in version 1.03g, and went on to produce at least four more versions (3.0, 3.2, 3.51, and 3.6; -note that these are old {\it Hack\/} version numbers, not contemporary -{\it NetHack\/} ones). +note that these are old \textit{Hack} version numbers, not contemporary +\textit{NetHack} ones). %.pg \medskip -\nd {\it R. Black\/} ported {\it PC Hack\/} 3.51 to Lattice C and the Atari -520/1040ST, producing {\it ST Hack\/} 1.03. +\noindent \textit{R. Black} ported \textit{PC Hack} 3.51 to Lattice C and the Atari +520/1040ST, producing \textit{ST Hack} 1.03. %.pg \medskip -\nd {\it Mike Stephenson\/} merged these various versions back together, -incorporating many of the added features, and produced {\it NetHack\/} version +\noindent \textit{Mike Stephenson} merged these various versions back together, +incorporating many of the added features, and produced \textit{NetHack} version 1.4 in 1987. He then coordinated a cast of thousands in enhancing and debugging -{\it NetHack\/} 1.4 and released {\it NetHack\/} versions 2.2 and 2.3. +\textit{NetHack} 1.4 and released \textit{NetHack} versions 2.2 and 2.3. Like Hack, they were released by posting their source code to Usenet where they remained available in various archives accessible -via {\it ftp\/} and {\it uucp\/} after expiring from the newsgroup. +via \textit{ftp} and \textit{uucp} after expiring from the newsgroup. %.pg \medskip -\nd Later, Mike coordinated a major re-write of the game, heading a team which -included {\it Ken Arromdee}, {\it Jean-Christophe Collet}, {\it Steve Creps}, -{\it Eric Hendrickson}, {\it Izchak Miller}, {\it Eric S. Raymond}, {\it John -Rupley}, {\it Mike Threepoint}, and {\it Janet Walz}, to produce -{\it NetHack\/} 3.0c. +\noindent Later, Mike coordinated a major re-write of the game, heading a team which +included \textit{Ken Arromdee}, \textit{Jean-Christophe Collet}, \textit{Steve Creps}, +\textit{Eric Hendrickson}, \textit{Izchak Miller}, \textit{Eric S. Raymond}, \textit{John +Rupley}, \textit{Mike Threepoint}, and \textit{Janet Walz}, to produce +\textit{NetHack} 3.0c. %.pg \medskip -\nd {\it NetHack\/} 3.0 was ported to the Atari by {\it Eric R. Smith}, to OS/2 by -{\it Timo Hakulinen}, and to VMS by {\it David Gentzel}. The three of them -and {\it Kevin Darcy\/} later joined the main {\it NetHack Development Team} to produce +\noindent \textit{NetHack} 3.0 was ported to the Atari by \textit{Eric R. Smith}, to OS/2 by +\textit{Timo Hakulinen}, and to VMS by \textit{David Gentzel}. The three of them +and \textit{Kevin Darcy} later joined the main \textit{NetHack Development Team} to produce subsequent revisions of 3.0. %.pg \medskip -\nd {\it Olaf Seibert\/} ported {\it NetHack\/} 2.3 and 3.0 to the Amiga. {\it -Norm Meluch}, {\it Stephen Spackman\/} and {\it Pierre Martineau\/} designed -overlay code for {\it PC NetHack\/} 3.0. {\it Johnny Lee\/} ported {\it -NetHack\/} 3.0 to the Macintosh. Along with various other Dungeoneers, they +\noindent \textit{Olaf Seibert} ported \textit{NetHack} 2.3 and 3.0 to the Amiga. {\it +Norm Meluch}, \textit{Stephen Spackman} and \textit{Pierre Martineau} designed +overlay code for \textit{PC NetHack} 3.0. \textit{Johnny Lee} ported {\it +NetHack} 3.0 to the Macintosh. Along with various other Dungeoneers, they continued to enhance the PC, Macintosh, and Amiga ports through the later revisions of 3.0. @@ -7038,92 +7107,92 @@ the three component numbering scheme began to be used with 3.1.0. %.pg \medskip -\nd Headed by {\it Mike Stephenson\/} and coordinated by {\it Izchak Miller\/} -and {\it Janet Walz}, the {\it NetHack Development Team} which now included -{\it Ken Arromdee}, -{\it David Cohrs}, {\it Jean-Christophe Collet}, {\it Kevin Darcy}, -{\it Matt Day}, {\it Timo Hakulinen}, {\it Steve Linhart}, {\it Dean Luick}, -{\it Pat Rankin}, {\it Eric Raymond}, and {\it Eric Smith\/} undertook a +\noindent Headed by \textit{Mike Stephenson} and coordinated by \textit{Izchak Miller} +and \textit{Janet Walz}, the \textit{NetHack Development Team} which now included +\textit{Ken Arromdee}, +\textit{David Cohrs}, \textit{Jean-Christophe Collet}, \textit{Kevin Darcy}, +\textit{Matt Day}, \textit{Timo Hakulinen}, \textit{Steve Linhart}, \textit{Dean Luick}, +\textit{Pat Rankin}, \textit{Eric Raymond}, and \textit{Eric Smith} undertook a radical revision of 3.0. They re-structured the game's design, and re-wrote major parts of the code. They added multiple dungeons, a new display, special individual character quests, a new endgame and many other new features, and -produced {\it NetHack\/} 3.1. +produced \textit{NetHack} 3.1. Version 3.1.0 was released in January of 1993. %.pg \medskip -\nd {\it Ken Lorber}, {\it Gregg Wonderly\/} and {\it Greg Olson}, with help -from {\it Richard Addison}, {\it Mike Passaretti}, and {\it Olaf Seibert}, -developed {\it NetHack\/} 3.1 for the Amiga. +\noindent \textit{Ken Lorber}, \textit{Gregg Wonderly} and \textit{Greg Olson}, with help +from \textit{Richard Addison}, \textit{Mike Passaretti}, and \textit{Olaf Seibert}, +developed \textit{NetHack} 3.1 for the Amiga. %.pg \medskip -\nd {\it Norm Meluch\/} and {\it Kevin Smolkowski}, with help from -{\it Carl Schelin}, {\it Stephen Spackman}, {\it Steve VanDevender}, -and {\it Paul Winner}, ported {\it NetHack\/} 3.1 to the PC. +\noindent \textit{Norm Meluch} and \textit{Kevin Smolkowski}, with help from +\textit{Carl Schelin}, \textit{Stephen Spackman}, \textit{Steve VanDevender}, +and \textit{Paul Winner}, ported \textit{NetHack} 3.1 to the PC. %.pg \medskip -\nd {\it Jon W\{tte} and {\it Hao-yang Wang}, -with help from {\it Ross Brown}, {\it Mike Engber}, {\it David Hairston}, -{\it Michael Hamel}, {\it Jonathan Handler}, {\it Johnny Lee}, -{\it Tim Lennan}, {\it Rob Menke}, and {\it Andy Swanson}, -developed {\it NetHack\/} 3.1 for the Macintosh, porting it for MPW. -Building on their development, {\it Bart House} added a Think C port. +\noindent \textit{Jon W\textbraceleft tte} and \textit{Hao-yang Wang}, +with help from \textit{Ross Brown}, \textit{Mike Engber}, \textit{David Hairston}, +\textit{Michael Hamel}, \textit{Jonathan Handler}, \textit{Johnny Lee}, +\textit{Tim Lennan}, \textit{Rob Menke}, and \textit{Andy Swanson}, +developed \textit{NetHack} 3.1 for the Macintosh, porting it for MPW. +Building on their development, \textit{Bart House} added a Think C port. %.pg \medskip -\nd {\it Timo Hakulinen\/} ported {\it NetHack\/} 3.1 to OS/2. -{\it Eric Smith\/} ported {\it NetHack\/} 3.1 to the Atari. -{\it Pat Rankin}, with help from {\it Joshua Delahunty}, -was responsible for the VMS version of {\it NetHack\/} 3.1. -{\it Michael Allison} ported {\it NetHack\/} 3.1 to Windows NT. +\noindent \textit{Timo Hakulinen} ported \textit{NetHack} 3.1 to OS/2. +\textit{Eric Smith} ported \textit{NetHack} 3.1 to the Atari. +\textit{Pat Rankin}, with help from \textit{Joshua Delahunty}, +was responsible for the VMS version of \textit{NetHack} 3.1. +\textit{Michael Allison} ported \textit{NetHack} 3.1 to Windows NT. %.pg \medskip -\nd {\it Dean Luick}, with help from {\it David Cohrs}, developed -{\it NetHack\/} 3.1 for X11. +\noindent \textit{Dean Luick}, with help from \textit{David Cohrs}, developed +\textit{NetHack} 3.1 for X11. It drew the map as text rather than graphically but -included {\tt nh10.bdf}, an optionally used custom X11 font which has +included \texttt{nh10.bdf}, an optionally used custom X11 font which has tiny images in place of letters and punctuation, a precursor of tiles. Those images don't extend to individual monster and object types, just replacements for monster and object classes (so one custom image for all -``{\tt a}'' insects and another for all ``{\tt [}'' armor and so +``\texttt{a}'' insects and another for all ``\texttt{[}'' armor and so forth, not separate images for beetles and ants or for cloaks and boots). %.pg \medskip -\nd {\it Warwick Allison\/} wrote a graphically displayed version -of {\it NetHack\/} +\noindent \textit{Warwick Allison} wrote a graphically displayed version +of \textit{NetHack} for the Atari where the tiny pictures were described as ``icons'' and were distinct for specific types of monsters and objects rather than just their classes. -He contributed them to the {\it NetHack Development Team\/} which +He contributed them to the \textit{NetHack Development Team} which rechristened them ``tiles'', original usage which has subsequently been picked up by various other games. -{\it NetHack's\/} tiles support was then implemented on other platforms +\textit{NetHack's} tiles support was then implemented on other platforms (initially MS-DOS but eventually Windows, Qt, and X11 too). %.pg \medskip -\nd The 3.2 {\it NetHack Development Team}, comprised of {\it Michael Allison}, {\it Ken -Arromdee}, {\it David Cohrs}, {\it Jessie Collet}, {\it Steve Creps}, {\it -Kevin Darcy}, {\it Timo Hakulinen}, {\it Steve Linhart}, {\it Dean Luick}, -{\it Pat Rankin}, {\it Eric Smith}, {\it Mike Stephenson}, {\it Janet Walz}, -and {\it Paul Winner}, released version 3.2.0 in April of 1996. +\noindent The 3.2 \textit{NetHack Development Team}, comprised of \textit{Michael Allison}, \textit{Ken +Arromdee}, \textit{David Cohrs}, \textit{Jessie Collet}, \textit{Steve Creps}, {\it +Kevin Darcy}, \textit{Timo Hakulinen}, \textit{Steve Linhart}, \textit{Dean Luick}, +\textit{Pat Rankin}, \textit{Eric Smith}, \textit{Mike Stephenson}, \textit{Janet Walz}, +and \textit{Paul Winner}, released version 3.2.0 in April of 1996. %.pg \medskip -\nd Version 3.2 marked the tenth anniversary of the formation of the +\noindent Version 3.2 marked the tenth anniversary of the formation of the development team. In a testament to their dedication to the game, all thirteen members -of the original {\it NetHack Development Team} remained on the team at the +of the original \textit{NetHack Development Team} remained on the team at the start of work on that release. During the interval between the release of 3.1.3 and 3.2.0, -one of the founding members of the {\it NetHack Development Team}, -{\it Dr. Izchak Miller}, was diagnosed with cancer and passed away. +one of the founding members of the \textit{NetHack Development Team}, +\textit{Dr. Izchak Miller}, was diagnosed with cancer and passed away. That release of the game was dedicated to him by the development and porting teams. @@ -7134,31 +7203,31 @@ better game play. %.pg \medskip -During the lifespan of {\it NetHack\/} 3.1 and 3.2, several enthusiasts +During the lifespan of \textit{NetHack} 3.1 and 3.2, several enthusiasts of the game added their own modifications to the game and made these ``variants'' publicly available: %.pg \medskip -{\it Tom Proudfoot} and {\it Yuval Oren} created {\it NetHack++}, -which was quickly renamed {\it NetHack$--$\/} +\textit{Tom Proudfoot} and \textit{Yuval Oren} created \textit{NetHack++}, +which was quickly renamed \textit{NetHack$--$} when some people incorrectly assumed that it was a conversion of the -{\it C\/} source code to {\it C++}. -Working independently, {\it Stephen White} wrote {\it NetHack Plus}. -{\it Tom Proudfoot} later merged {\it NetHack Plus} -and his own {\it NetHack$--$} to produce {\it SLASH}. -{\it Larry Stewart-Zerba} and {\it Warwick Allison} improved the spell +\textit{C} source code to \textit{C++}. +Working independently, \textit{Stephen White} wrote \textit{NetHack Plus}. +\textit{Tom Proudfoot} later merged \textit{NetHack Plus} +and his own \textit{NetHack$--$} to produce \textit{SLASH}. +\textit{Larry Stewart-Zerba} and \textit{Warwick Allison} improved the spell casting system with the Wizard Patch. -{\it Warwick Allison} also ported {\it NetHack\/} to use the Qt interface. +\textit{Warwick Allison} also ported \textit{NetHack} to use the Qt interface. %.pg \medskip -{\it Warren Cheung} combined {\it SLASH} with the Wizard Patch -to produce {\it Slash'EM\/}, and -with the help of {\it Kevin Hugo}, added more features. -Kevin later joined the {\it NetHack Development Team} and incorporated -the best of these ideas into {\it NetHack\/} 3.3. +\textit{Warren Cheung} combined \textit{SLASH} with the Wizard Patch +to produce \textit{Slash'EM}, and +with the help of \textit{Kevin Hugo}, added more features. +Kevin later joined the \textit{NetHack Development Team} and incorporated +the best of these ideas into \textit{NetHack} 3.3. %.pg \medskip @@ -7169,7 +7238,7 @@ without any ready-to-play distribution for systems that usually had such. %.pg (To anyone considering resurrecting an old version: all versions before -3.2.3 had a {\it Y2K\/} bug. +3.2.3 had a \textit{Y2K} bug. The high scores file and the log file contained dates which were formatted using a two-digit year, and 1999's year 99 was followed by 2000's year 100. @@ -7181,11 +7250,11 @@ random ghost and statue names in the current game.) %.pg \medskip -The 3.3 {\it NetHack Development Team}, consisting of {\it Michael Allison}, {\it Ken Arromdee}, -{\it David Cohrs}, {\it Jessie Collet}, {\it Steve Creps}, {\it Kevin Darcy}, -{\it Timo Hakulinen}, {\it Kevin Hugo}, {\it Steve Linhart}, {\it Ken Lorber}, -{\it Dean Luick}, {\it Pat Rankin}, {\it Eric Smith}, {\it Mike Stephenson}, -{\it Janet Walz}, and {\it Paul Winner}, released 3.3.0 in +The 3.3 \textit{NetHack Development Team}, consisting of \textit{Michael Allison}, \textit{Ken Arromdee}, +\textit{David Cohrs}, \textit{Jessie Collet}, \textit{Steve Creps}, \textit{Kevin Darcy}, +\textit{Timo Hakulinen}, \textit{Kevin Hugo}, \textit{Steve Linhart}, \textit{Ken Lorber}, +\textit{Dean Luick}, \textit{Pat Rankin}, \textit{Eric Smith}, \textit{Mike Stephenson}, +\textit{Janet Walz}, and \textit{Paul Winner}, released 3.3.0 in December 1999 and 3.3.1 in August of 2000. %.pg @@ -7203,73 +7272,73 @@ more than a year and a half. %.pg \medskip -The 3.4 {\it NetHack Development Team} initially consisted of -{\it Michael Allison}, {\it Ken Arromdee}, -{\it David Cohrs}, {\it Jessie Collet}, {\it Kevin Hugo}, {\it Ken Lorber}, -{\it Dean Luick}, {\it Pat Rankin}, {\it Mike Stephenson}, -{\it Janet Walz}, and {\it Paul Winner}, with {\it Warwick Allison} joining -just before the release of {\it NetHack\/} 3.4.0 in March 2002. +The 3.4 \textit{NetHack Development Team} initially consisted of +\textit{Michael Allison}, \textit{Ken Arromdee}, +\textit{David Cohrs}, \textit{Jessie Collet}, \textit{Kevin Hugo}, \textit{Ken Lorber}, +\textit{Dean Luick}, \textit{Pat Rankin}, \textit{Mike Stephenson}, +\textit{Janet Walz}, and \textit{Paul Winner}, with \textit{ Warwick Allison} joining +just before the release of \textit{NetHack} 3.4.0 in March 2002. %.pg \medskip As with version 3.3, various people contributed to the game as a whole as -well as supporting ports on the different platforms that {\it NetHack\/} +well as supporting ports on the different platforms that \textit{NetHack} runs on: %.pg \medskip -\nd{\it Pat Rankin} maintained 3.4 for VMS. +\noindent\textit{Pat Rankin} maintained 3.4 for VMS. %.pg \medskip -\nd {\it Michael Allison} maintained {\it NetHack\/} 3.4 for the MS-DOS +\noindent \textit{Michael Allison} maintained \textit{NetHack} 3.4 for the MS-DOS platform. -{\it Paul Winner} and {\it Yitzhak Sapir} provided encouragement. +\textit{Paul Winner} and \textit{Yitzhak Sapir} provided encouragement. %.pg \medskip -\nd {\it Dean Luick}, {\it Mark Modrall}, and {\it Kevin Hugo} maintained and +\noindent \textit{Dean Luick}, \textit{Mark Modrall}, and \textit{Kevin Hugo} maintained and enhanced the Macintosh port of 3.4. %.pg \medskip -\nd {\it Michael Allison}, {\it David Cohrs}, {\it Alex Kompel}, -{\it Dion Nicolaas}, and -{\it Yitzhak Sapir} maintained and enhanced 3.4 for the Microsoft Windows +\noindent \textit{Michael Allison}, \textit{David Cohrs}, \textit{Alex Kompel}, +\textit{Dion Nicolaas}, and +\textit{Yitzhak Sapir} maintained and enhanced 3.4 for the Microsoft Windows platform. -{\it Alex Kompel} contributed a new graphical interface for the Windows port. -{\it Alex Kompel} also contributed a Windows CE port for 3.4.1. +\textit{Alex Kompel} contributed a new graphical interface for the Windows port. +\textit{Alex Kompel} also contributed a Windows CE port for 3.4.1. %.pg \medskip -\nd {\it Ron Van Iwaarden} was the sole maintainer of {\it NetHack\/} for +\noindent \textit{Ron Van Iwaarden} was the sole maintainer of \textit{NetHack} for OS/2 the past several releases. Unfortunately Ron's last OS/2 machine stopped working in -early 2006. A great many thanks to Ron for keeping {\it NetHack\/} alive on +early 2006. A great many thanks to Ron for keeping \textit{NetHack} alive on OS/2 all these years. %.pg \medskip -\nd {\it Janne Salmij\"{a}rvi} and {\it Teemu Suikki} maintained -and enhanced the Amiga port of 3.4 after {\it Janne Salmij\"{a}rvi} resurrected +\noindent \textit{Janne Salmij\"{a}rvi} and \textit{Teemu Suikki} maintained +and enhanced the Amiga port of 3.4 after \textit{Janne Salmij\"{a}rvi} resurrected it for 3.3.1. %.pg \medskip -\nd {\it Christian ``Marvin'' Bressler} maintained 3.4 for the Atari after he +\noindent \textit{Christian ``Marvin'' Bressler} maintained 3.4 for the Atari after he resurrected it for 3.3.1. %.pg \medskip -The release of {\it NetHack\/} 3.4.3 in December 2003 marked the beginning of +The release of \textit{NetHack} 3.4.3 in December 2003 marked the beginning of a long release hiatus. 3.4.3 proved to be a remarkably stable version that provided continued enjoyment by the community for more than a decade. The -{\it NetHack Development Team} slowly and quietly continued to work on the game behind the scenes +\textit{NetHack Development Team} slowly and quietly continued to work on the game behind the scenes during the tenure of 3.4.3. It was during that same period that several new -variants emerged within the {\it NetHack\/} community. Notably sporkhack by -Derek S. Ray, {\it unnethack\/} by Patric Mueller, {\it nitrohack\/} and its +variants emerged within the \textit{NetHack} community. Notably sporkhack by +Derek S. Ray, \textit{unnethack} by Patric Mueller, \textit{nitrohack} and its successors originally by Daniel Thaler and then by Alex Smith, and -{\it Dynahack\/} by Tung Nguyen. +\textit{Dynahack} by Tung Nguyen. Some of those variants continue to be developed, maintained, and enjoyed by the community to this day. @@ -7280,9 +7349,9 @@ released publicly by other parties. Since that code was a work-in-progress and had not gone through the process of debugging it as a suitable release, it was decided that the version numbers present on that code snapshot would -be retired and never used in an official {\it NetHack\/} release. -An announcement was posted on the {\it NetHack Development Team}'s official -{\it nethack.org\/} website +be retired and never used in an official \textit{NetHack} release. +An announcement was posted on the \textit{NetHack Development Team}'s official +\textit{nethack.org} website to that effect, stating that there would never be a 3.4.4, 3.5, or 3.5.0 official release version. @@ -7293,20 +7362,20 @@ In January 2015, preparation began for the release of NetHack 3.6. %.pg \medskip At the beginning of development for what would eventually get released as -3.6.0, the {\it NetHack Development Team} consisted of {\it Warwick Allison}, -{\it Michael Allison}, {\it Ken Arromdee}, -{\it David Cohrs}, {\it Jessie Collet}, -{\it Ken Lorber}, {\it Dean Luick}, {\it Pat Rankin}, -{\it Mike Stephenson}, {\it Janet Walz}, and {\it Paul Winner}. +3.6.0, the \textit{NetHack Development Team} consisted of \textit{Warwick Allison}, +\textit{Michael Allison}, \textit{Ken Arromdee}, +\textit{David Cohrs}, \textit{Jessie Collet}, +\textit{Ken Lorber}, \textit{Dean Luick}, \textit{Pat Rankin}, +\textit{Mike Stephenson}, \textit{Janet Walz}, and \textit{Paul Winner}. In early 2015, ahead of the release of 3.6.0, new members -{\it Sean Hunt}, {\it Pasi Kallinen}, and {\it Derek S. Ray} -joined the {\it NetHack\/} development team. +\textit{Sean Hunt}, \textit{Pasi Kallinen}, and \textit{Derek S. Ray} +joined the \textit{NetHack} development team. %.pg \medskip Near the end of the development of 3.6.0, one of the significant inspirations for many of the humorous and fun features found in the game, -author Terry Pratchett, passed away. {\it NetHack\/} 3.6.0 introduced +author Terry Pratchett, passed away. \textit{NetHack} 3.6.0 introduced a tribute to him. %.pg @@ -7317,46 +7386,46 @@ patches. Many bugs were fixed and some code was restructured. %.pg \medskip -The {\it NetHack Development Team}, as well as {\it Steve VanDevender} and -{\it Kevin Smolkowski}, ensured that {\it NetHack\/} 3.6 continued to +The \textit{NetHack Development Team}, as well as \textit{Steve VanDevender} and +\textit{Kevin Smolkowski}, ensured that \textit{NetHack} 3.6 continued to operate on various UNIX flavors and maintained the X11 interface. %.pg \medskip -{\it Ken Lorber}, {\it Haoyang Wang}, {\it Pat Rankin}, and {\it Dean Luick} -maintained the port of {\it NetHack\/} 3.6 for MacOS. +\textit{Ken Lorber}, \textit{Haoyang Wang}, \textit{Pat Rankin}, and \textit{Dean Luick} +maintained the port of \textit{NetHack} 3.6 for MacOS. %.pg \medskip -{\it Michael Allison}, {\it David Cohrs}, {\it Bart House}, -{\it Pasi Kallinen}, {\it Alex Kompel}, {\it Dion Nicolaas}, -{\it Derek S. Ray} and {\it Yitzhak Sapir} -maintained the port of {\it NetHack\/} 3.6 for Microsoft Windows. +\textit{Michael Allison}, \textit{David Cohrs}, \textit{Bart House}, +\textit{Pasi Kallinen}, \textit{Alex Kompel}, \textit{Dion Nicolaas}, +\textit{Derek S. Ray} and \textit{Yitzhak Sapir} +maintained the port of \textit{NetHack} 3.6 for Microsoft Windows. %.pg \medskip -{\it Pat Rankin} attempted to keep the VMS port running for NetHack 3.6, -hindered by limited access. {\it Kevin Smolkowski} has updated and tested it +\textit{Pat Rankin} attempted to keep the VMS port running for NetHack 3.6, +hindered by limited access. \textit{Kevin Smolkowski} has updated and tested it for the most recent version of OpenVMS (V8.4 as of this writing) on Alpha and Integrity (aka Itanium aka IA64) but not VAX. %.pg \medskip -{\it Ray Chason} resurrected the MS-DOS port for 3.6 and contributed the +\textit{Ray Chason} resurrected the MS-DOS port for 3.6 and contributed the necessary updates to the community at large. %.pg \medskip In late April 2018, several hundred bug fixes for 3.6.0 and some new features were assembled and released as NetHack 3.6.1. -The {\it NetHack Development Team} at the +The \textit{NetHack Development Team} at the time of release of 3.6.1 consisted of -{\it Warwick Allison}, {\it Michael Allison}, {\it Ken Arromdee}, -{\it David Cohrs}, {\it Jessie Collet}, -{\it Pasi Kallinen}, {\it Ken Lorber}, {\it Dean Luick}, -{\it Patric Mueller}, {\it Pat Rankin}, {\it Derek S. Ray}, -{\it Alex Smith}, {\it Mike Stephenson}, {\it Janet Walz}, and -{\it Paul Winner}. +\textit{Warwick Allison}, \textit{Michael Allison}, \textit{Ken Arromdee}, +\textit{David Cohrs}, \textit{Jessie Collet}, +\textit{Pasi Kallinen}, \textit{Ken Lorber}, \textit{Dean Luick}, +\textit{Patric Mueller}, \textit{Pat Rankin}, \textit{Derek S. Ray}, +\textit{Alex Smith}, \textit{Mike Stephenson}, \textit{Janet Walz}, and +\textit{Paul Winner}. %.pg \medskip @@ -7365,8 +7434,8 @@ the adopted curses window port, were released as 3.6.2. %.pg \medskip -{\it Bart House}, who had contributed to the game as a porting team participant -for decades, joined the {\it NetHack Development Team} in late May 2019. +\textit{Bart House}, who had contributed to the game as a porting team participant +for decades, joined the \textit{NetHack Development Team} in late May 2019. %.pg \medskip @@ -7395,33 +7464,29 @@ some bug fixes. %.pg \medskip -\nd The official {\it NetHack\/} web site is maintained by {\it Ken Lorber} at -{\catcode`\#=11 -\special{html:}} -https:{\tt /}{\tt /}www.nethack.org{\tt /}. -{\catcode`\#=11 -\special{html:}} +\noindent The official \textit{NetHack} web site is maintained by \textit{Ken Lorber} at +\url{https://www.nethack.org}. %.pg %.hn 2 \subsection*{Special Thanks} -\nd On behalf of the {\it NetHack\/} community, thank you very much once -again to {\it M. Drew Streib} and {\it Pasi Kallinen} for providing a -public NetHack server at nethack.alt.org. Thanks to {\it Keith Simpson} -and {\it Andy Thomson} for hardfought.org. Thanks to all those +\noindent On behalf of the \textit{NetHack} community, thank you very much once +again to \textit{M. Drew Streib} and \textit{Pasi Kallinen} for providing a +public NetHack server at nethack.alt.org. Thanks to \textit{Keith Simpson} +and \textit{Andy Thomson} for hardfought.org. Thanks to all those unnamed dungeoneers who invest their time and effort into annual -{\it NetHack\/} tournaments such as {\it Junethack}, -{\it The November NetHack Tournament}, and in days past, -{\it devnull.net\/} (gone for now, but not forgotten). +\textit{NetHack} tournaments such as \textit{Junethack}, +\textit{The November NetHack Tournament}, and in days past, +\textit{devnull.net} (gone for now, but not forgotten). \clearpage %.hn 2 \subsection*{Dungeoneers} %.pg -\nd From time to time, some depraved individual out there in netland sends a +\noindent From time to time, some depraved individual out there in netland sends a particularly intriguing modification to help out with the game. The -{\it NetHack Development Team} sometimes makes note of the names of the worst +\textit{NetHack Development Team} sometimes makes note of the names of the worst of these miscreants in this, the list of Dungeoneers: \medskip %.sd @@ -7438,7 +7503,7 @@ Andy Thomson & John Kallen & Patric Mueller\\ Ari Huttunen & John Rupley & Paul Winner\\ Bart House & John S. Bien & Pierre Martineau\\ Benson I. Margulies & Johnny Lee & Ralf Brown\\ -Bill Dyer & Jon W\{tte & Ray Chason\\ +Bill Dyer & Jon W\textbraceleft tte & Ray Chason\\ Boudewijn Waijers & Jonathan Handler & Richard Addison\\ Bruce Cox & Joshua Delahunty & Richard Beigel\\ Bruce Holloway & Karl Garrison & Richard P. Hughey\\ diff --git a/doc/Guidebook.txt b/doc/Guidebook.txt index e36569992..b652a455e 100644 --- a/doc/Guidebook.txt +++ b/doc/Guidebook.txt @@ -15,7 +15,7 @@ Original version - Eric S. Raymond (Edited and expanded for NetHack 3.7.0 by Mike Stephenson and others) - May 1, 2025 + March 25, 2026 @@ -86,23 +86,24 @@ Archeologists understand dungeons pretty well; this enables them to move quickly and sneak up on the local nasties. They start - equipped with the tools for a proper scientific expedition. + equipped with the tools for a proper scientific expedition, and are + able to read ancient languages. - Barbarians are warriors out of the hinterland, hardened to bat- - tle. They begin their quests with naught but uncommon strength, a + Barbarians are warriors out of the hinterland, hardened to bat- + tle. They begin their quests with naught but uncommon strength, a trusty hauberk, and a great two-handed sword. Cavemen and Cavewomen start with exceptional strength but, unfor- tunately, with neolithic weapons. Healers are wise in medicine and apothecary. They know the herbs - and simples that can restore vitality, ease pain, anesthetize, and - neutralize poisons; and with their instruments, they can divine a - being's state of health or sickness. Their medical practice earns + and simples that can restore vitality, ease pain, anesthetize, and + neutralize poisons; and with their instruments, they can divine a + being's state of health or sickness. Their medical practice earns them quite reasonable amounts of money, with which they enter the dun- geon. - Knights are distinguished from the common skirmisher by their + Knights are distinguished from the common skirmisher by their devotion to the ideals of chivalry and by the surpassing excellence of their armor. @@ -112,21 +113,20 @@ mobility. Priests and Priestesses are clerics militant, crusaders advancing - the cause of righteousness with arms, armor, and arts thaumaturgic. - Their ability to commune with deities via prayer occasionally extri- + the cause of righteousness with arms, armor, and arts thaumaturgic. + Their ability to commune with deities via prayer occasionally extri- cates them from peril, but can also put them in it. - Rangers are most at home in the woods, and some say slightly out - of place in a dungeon. They are, however, experts in archery as well + Rangers are most at home in the woods, and some say slightly out + of place in a dungeon. They are, however, experts in archery as well as tracking and stealthy movement. - Rogues are agile and stealthy thieves, with knowledge of locks, - traps, and poisons. Their advantage lies in surprise, which they + Rogues are agile and stealthy thieves, with knowledge of locks, + traps, and poisons. Their advantage lies in surprise, which they employ to great advantage. - - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -136,63 +136,63 @@ - Samurai are the elite warriors of feudal Nippon. They are - lightly armored and quick, and wear the dai-sho, two swords of the + Samurai are the elite warriors of feudal Nippon. They are + lightly armored and quick, and wear the dai-sho, two swords of the deadliest keenness. - Tourists start out with lots of gold (suitable for shopping - with), a credit card, lots of food, some maps, and an expensive cam- + Tourists start out with lots of gold (suitable for shopping + with), a credit card, lots of food, some maps, and an expensive cam- era. Most monsters don't like being photographed. Valkyries are hardy warrior women. Their upbringing in the harsh - Northlands makes them strong, inures them to extremes of cold, and + Northlands makes them strong, inures them to extremes of cold, and instills in them stealth and cunning. Wizards start out with a knowledge of magic, a selection of magi- cal items, and a particular affinity for dweomercraft. Although seem- - ingly weak and easy to overcome at first sight, an experienced Wizard + ingly weak and easy to overcome at first sight, an experienced Wizard is a deadly foe. - You may also choose the race of your character (within limits; + You may also choose the race of your character (within limits; most roles have restrictions on which races are eligible for them): - Dwarves are smaller than humans or elves, but are stocky and - solid individuals. Dwarves' most notable trait is their great exper- - tise in mining and metalwork. Dwarvish armor is said to be second in + Dwarves are smaller than humans or elves, but are stocky and + solid individuals. Dwarves' most notable trait is their great exper- + tise in mining and metalwork. Dwarvish armor is said to be second in quality not even to the mithril armor of the Elves. - Elves are agile, quick, and perceptive; very little of what goes + Elves are agile, quick, and perceptive; very little of what goes on will escape an Elf. The quality of Elven craftsmanship often gives them an advantage in arms and armor. Gnomes are smaller than but generally similar to dwarves. Gnomes - are known to be expert miners, and it is known that a secret under- + are known to be expert miners, and it is known that a secret under- ground mine complex built by this race exists within the Mazes of Men- ace, filled with both riches and danger. - Humans are by far the most common race of the surface world, and - are thus the norm to which other races are often compared. Although + Humans are by far the most common race of the surface world, and + are thus the norm to which other races are often compared. Although they have no special abilities, they can succeed in any role. - Orcs are a cruel and barbaric race that hate every living thing + Orcs are a cruel and barbaric race that hate every living thing (including other orcs). Above all others, Orcs hate Elves with a pas- - sion unequalled, and will go out of their way to kill one at any - opportunity. The armor and weapons fashioned by the Orcs are typi- + sion unequalled, and will go out of their way to kill one at any + opportunity. The armor and weapons fashioned by the Orcs are typi- cally of inferior quality. 3. What do all those things on the screen mean? - On the screen is kept a map of where you have been and what you - have seen on the current dungeon level; as you explore more of the + On the screen is kept a map of where you have been and what you + have seen on the current dungeon level; as you explore more of the level, it appears on the screen in front of you. When NetHack's ancestor rogue first appeared, its screen orienta- - tion was almost unique among computer fantasy games. Since then, - screen orientation has become the norm rather than the exception; - NetHack continues this fine tradition. Unlike text adventure games + tion was almost unique among computer fantasy games. Since then, + screen orientation has become the norm rather than the exception; + NetHack continues this fine tradition. Unlike text adventure games - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -202,38 +202,38 @@ - that accept commands in pseudo-English sentences and explain the - results in words, NetHack commands are all one or two keystrokes and + that accept commands in pseudo-English sentences and explain the + results in words, NetHack commands are all one or two keystrokes and the results are displayed graphically on the screen. A minimum screen - size of 24 lines by 80 columns is recommended; if the screen is + size of 24 lines by 80 columns is recommended; if the screen is larger, only a 21x80 section will be used for the map. - NetHack can even be played by blind players, with the assistance + NetHack can even be played by blind players, with the assistance of Braille readers or speech synthesisers. Instructions for configur- ing NetHack for the blind are included later in this document. - NetHack generates a new dungeon every time you play it; even the + NetHack generates a new dungeon every time you play it; even the authors still find it an entertaining and exciting game despite having won several times. - NetHack offers a variety of display options. The options avail- + NetHack offers a variety of display options. The options avail- able to you will vary from port to port, depending on the capabilities - of your hardware and software, and whether various compile-time + of your hardware and software, and whether various compile-time options were enabled when your executable was created. The three pos- - sible display options are: a monochrome character interface, a color - character interface, and a graphical interface using small pictures - called tiles. The two character interfaces allow fonts with other + sible display options are: a monochrome character interface, a color + character interface, and a graphical interface using small pictures + called tiles. The two character interfaces allow fonts with other characters to be substituted, but the default assignments use standard - ASCII characters to represent everything. There is no difference - between the various display options with respect to game play. - Because we cannot reproduce the tiles or colors in the Guidebook, and + ASCII characters to represent everything. There is no difference + between the various display options with respect to game play. + Because we cannot reproduce the tiles or colors in the Guidebook, and because it is common to all ports, we will use the default ASCII char- - acters from the monochrome character display when referring to things + acters from the monochrome character display when referring to things you might see on the screen during your game. - In order to understand what is going on in NetHack, first you - must understand what NetHack is doing with the screen. The NetHack - screen replaces the "You see ..." descriptions of text adventure + In order to understand what is going on in NetHack, first you + must understand what NetHack is doing with the screen. The NetHack + screen replaces the "You see ..." descriptions of text adventure games. Figure 1 is a sample of what a NetHack screen might look like. The way the screen looks for you depends on your platform. @@ -258,7 +258,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -324,7 +324,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -342,15 +342,15 @@ 3.1. The status lines (bottom) - The bottom two (or three) lines of the screen contain several - cryptic pieces of information describing your current status. Figure - 1 shows the traditional two-line status area below the map. Figure 2 + The bottom two (or three) lines of the screen contain several + cryptic pieces of information describing your current status. Figure + 1 shows the traditional two-line status area below the map. Figure 2 shows just the status area, when the statuslines:3 option has been set - (not all interfaces support this option). If any status line becomes - wider than the screen, you might not see all of it due to truncation. - When the numbers grow bigger and multiple conditions are present, the - two-line format will run out of room on the second line, but sta- - tuslines:2 is the default because a basic 24-line terminal isn't tall + (not all interfaces support this option). If any status line becomes + wider than the screen, you might not see all of it due to truncation. + When the numbers grow bigger and multiple conditions are present, the + two-line format will run out of room on the second line, but sta- + tuslines:2 is the default because a basic 24-line terminal isn't tall enough for the third line. Here are explanations of what the various status items mean: @@ -360,37 +360,37 @@ experience level, see below). Strength - A measure of your character's strength; one of your six basic - attributes. A human character's attributes can range from 3 to - 18 inclusive; non-humans may exceed these limits (occasionally + A measure of your character's strength; one of your six basic + attributes. A human character's attributes can range from 3 to + 18 inclusive; non-humans may exceed these limits (occasionally you may get super-strengths of the form 18/xx, and magic can also - cause attributes to exceed the normal limits). The higher your - strength, the stronger you are. Strength affects how success- - fully you perform physical tasks, how much damage you do in com- + cause attributes to exceed the normal limits). The higher your + strength, the stronger you are. Strength affects how success- + fully you perform physical tasks, how much damage you do in com- bat, and how much loot you can carry. Dexterity - Dexterity affects your chances to hit in combat, to avoid traps, + Dexterity affects your chances to hit in combat, to avoid traps, and do other tasks requiring agility or manipulation of objects. Constitution - Constitution affects your ability to recover from injuries and - other strains on your stamina. When strength is low or modest, - constitution also affects how much you can carry. With suffi- + Constitution affects your ability to recover from injuries and + other strains on your stamina. When strength is low or modest, + constitution also affects how much you can carry. With suffi- ciently high strength, the contribution to carrying capacity from your constitution no longer matters. Intelligence - Intelligence affects your ability to cast spells and read spell- + Intelligence affects your ability to cast spells and read spell- books. Wisdom - Wisdom comes from your practical experience (especially when + Wisdom comes from your practical experience (especially when dealing with magic). It affects your magical energy. - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -405,58 +405,58 @@ ticular, it can affect the prices shopkeepers offer you. Alignment - Lawful, Neutral, or Chaotic. Often, Lawful is taken as good and - Chaotic as evil, but legal and ethical do not always coincide. - Your alignment influences how other monsters react toward you. - Monsters of a like alignment are more likely to be non-aggres- - sive, while those of an opposing alignment are more likely to be + Lawful, Neutral, or Chaotic. Often, Lawful is taken as good and + Chaotic as evil, but legal and ethical do not always coincide. + Your alignment influences how other monsters react toward you. + Monsters of a like alignment are more likely to be non-aggres- + sive, while those of an opposing alignment are more likely to be seriously offended at your presence. Dungeon Level - How deep you are in the dungeon. You start at level one and the - number increases as you go deeper into the dungeon. Some levels - are special, and are identified by a name and not a number. The + How deep you are in the dungeon. You start at level one and the + number increases as you go deeper into the dungeon. Some levels + are special, and are identified by a name and not a number. The Amulet of Yendor is reputed to be somewhere beneath the twentieth level. Gold - The number of gold pieces you are openly carrying. Gold which + The number of gold pieces you are openly carrying. Gold which you have concealed in containers is not counted. Hit Points - Your current and maximum hit points. Hit points indicate how + Your current and maximum hit points. Hit points indicate how much damage you can take before you die. The more you get hit in - a fight, the lower they get. You can regain hit points by rest- - ing, or by using certain magical items or spells. The number in + a fight, the lower they get. You can regain hit points by rest- + ing, or by using certain magical items or spells. The number in parentheses is the maximum number your hit points can reach. Power - Spell points. This tells you how much mystic energy (mana) you + Spell points. This tells you how much mystic energy (mana) you have available for spell casting. Again, resting will regenerate the amount available. Armor Class A measure of how effectively your armor stops blows from - unfriendly creatures. The lower this number is, the more effec- - tive the armor; it is quite possible to have negative armor + unfriendly creatures. The lower this number is, the more effec- + tive the armor; it is quite possible to have negative armor class. See the Armor subsection of Objects for more information. Experience - Your current experience level. If the showexp option is set, it + Your current experience level. If the showexp option is set, it will be followed by a slash and experience points. As you adven- - ture, you gain experience points. At certain experience point - totals, you gain an experience level. The more experienced you + ture, you gain experience points. At certain experience point + totals, you gain an experience level. The more experienced you are, the better you fight and withstand magical attacks. (By the - time your level reaches double digits, the usefulness of showing - the points with it has dropped significantly. You can use the - `O' command to turn showexp off to avoid using up the limited + time your level reaches double digits, the usefulness of showing + the points with it has dropped significantly. You can use the + `O' command to turn showexp off to avoid using up the limited status line space.) Time - The number of turns elapsed so far, displayed if you have the + The number of turns elapsed so far, displayed if you have the - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -469,60 +469,60 @@ time option set. Status - Hunger: your current hunger status. Values are Satiated, Not - Hungry (or Normal), Hungry, Weak, and Fainting. Not shown when + Hunger: your current hunger status. Values are Satiated, Not + Hungry (or Normal), Hungry, Weak, and Fainting. Not shown when Normal. - Encumbrance: an indication of how what you are carrying affects - your ability to move. Values are Unencumbered, Burdened, - Stressed, Strained, Overtaxed, and Overloaded. Not shown when + Encumbrance: an indication of how what you are carrying affects + your ability to move. Values are Unencumbered, Burdened, + Stressed, Strained, Overtaxed, and Overloaded. Not shown when Unencumbered. Fatal conditions: Stone (aka Petrifying, turning to stone), Slime - (turning into green slime), Strngl (being strangled), FoodPois - (suffering from acute food poisoning), TermIll (suffering from a + (turning into green slime), Strngl (being strangled), FoodPois + (suffering from acute food poisoning), TermIll (suffering from a terminal illness). - Non-fatal conditions: Blind (can't see), Deaf (can't hear), Stun + Non-fatal conditions: Blind (can't see), Deaf (can't hear), Stun (stunned), Conf (confused), Hallu (hallucinating). - Movement modifiers: Lev (levitating), Fly (flying), Ride (rid- + Movement modifiers: Lev (levitating), Fly (flying), Ride (rid- ing). Other conditions and modifiers exist, but there isn't enough room to display them with the other status fields. - The #attributes command (default key ^X) will show all current status - information in unabbreviated format. It also shows other information + The #attributes command (default key ^X) will show all current status + information in unabbreviated format. It also shows other information which might be included on the status lines if those had more room. 3.2. The message line (top) The top line of the screen is reserved for messages that describe - things that are impossible to represent visually. If you see a - "--More--" on the top line, this means that NetHack has another mes- - sage to display on the screen, but it wants to make certain that - you've read the one that is there first. To read the next message, + things that are impossible to represent visually. If you see a + "--More--" on the top line, this means that NetHack has another mes- + sage to display on the screen, but it wants to make certain that + you've read the one that is there first. To read the next message, just press the space bar. - To change how and what messages are shown on the message line, + To change how and what messages are shown on the message line, see "Configuring Message Types" and the verbose option. 3.3. The map (rest of the screen) - The rest of the screen is the map of the level as you have - explored it so far. Each symbol on the screen represents something. + The rest of the screen is the map of the level as you have + explored it so far. Each symbol on the screen represents something. You can set various graphics options to change some of the symbols the - game uses; otherwise, the game will use default symbols. Here is a + game uses; otherwise, the game will use default symbols. Here is a list of what the default symbols mean: - - The horizontal or corner walls of a room, or an open east/west + - The horizontal or corner walls of a room, or an open east/west door. - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -532,13 +532,13 @@ - | The vertical walls of a room, or an open north/south door, or a + | The vertical walls of a room, or an open north/south door, or a grave. - . The floor of a room, or ice, or a doorless doorway, or the span + . The floor of a room, or ice, or a doorless doorway, or the span of an open drawbridge. - # A corridor, or iron bars, or a tree, or the portcullis of a + # A corridor, or iron bars, or a tree, or the portcullis of a closed drawbridge. Note: engravings in corridors also appear as # but are shown in a @@ -548,7 +548,7 @@ < Stairs up: a way to the previous level. - + A closed door, or a spellbook containing a spell you may be able + + A closed door, or a spellbook containing a spell you may be able to learn. @ Your character or a human or an elf. @@ -579,7 +579,7 @@ ` A boulder or statue or an engraving on the floor of a room. - Note: statues are displayed as if they were the monsters they + Note: statues are displayed as if they were the monsters they depict so won't appear as a grave accent (aka back-tick). 0 An iron ball. @@ -588,7 +588,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -610,51 +610,51 @@ A-HJ-Z and @&':; - Letters and certain other symbols represent the various inhabi- - tants of the Mazes of Menace. Watch out, they can be nasty and + Letters and certain other symbols represent the various inhabi- + tants of the Mazes of Menace. Watch out, they can be nasty and vicious. Sometimes, however, they can be helpful. I Rather than a specific type of monster, this marks the last known - location of an invisible or otherwise unseen monster. Note that + location of an invisible or otherwise unseen monster. Note that the monster could have moved. The `s', `F', and `m' commands may be useful here. - 1-5 The digits 1 through 5 may be displayed, marking unseen monsters - sensed via the Warning attribute. Less dangerous monsters are + 1-5 The digits 1 through 5 may be displayed, marking unseen monsters + sensed via the Warning attribute. Less dangerous monsters are indicated by lower values, more dangerous by higher values. - You need not memorize all these symbols; you can ask the game - what any symbol represents with the `/' command (see the next section + You need not memorize all these symbols; you can ask the game + what any symbol represents with the `/' command (see the next section for more info). 4. Commands - Commands can be initiated by typing one or two characters to - which the command is bound to, or typing the command name in the + Commands can be initiated by typing one or two characters to + which the command is bound to, or typing the command name in the extended commands entry. Some commands, like "search", do not require - that any more information be collected by NetHack. Other commands - might require additional information, for example a direction, or an - object to be used. For those commands that require additional infor- + that any more information be collected by NetHack. Other commands + might require additional information, for example a direction, or an + object to be used. For those commands that require additional infor- mation, NetHack will present you with either a menu of choices or with a command line prompt requesting information. Which you are presented with will depend chiefly on how you have set the menustyle option. - For example, a common question, in the form "What do you want to - use? [a-zA-Z ?*]", asks you to choose an object you are carrying. - Here, "a-zA-Z" are the inventory letters of your possible choices. - Typing `?' gives you an inventory list of these items, so you can see - what each letter refers to. In this example, there is also a `*' - indicating that you may choose an object not on the list, if you - wanted to use something unexpected. Typing a `*' lists your entire + For example, a common question, in the form "What do you want to + use? [a-zA-Z ?*]", asks you to choose an object you are carrying. + Here, "a-zA-Z" are the inventory letters of your possible choices. + Typing `?' gives you an inventory list of these items, so you can see + what each letter refers to. In this example, there is also a `*' + indicating that you may choose an object not on the list, if you + wanted to use something unexpected. Typing a `*' lists your entire inventory, so you can see the inventory letters of every object you're - carrying. Finally, if you change your mind and decide you don't want - to do this command after all, you can press the ESC key to abort the + carrying. Finally, if you change your mind and decide you don't want + to do this command after all, you can press the ESC key to abort the command. - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -664,63 +664,63 @@ - You can put a number before some commands to repeat them that + You can put a number before some commands to repeat them that many times; for example, "10s" will search ten times. If you have the - number_pad option set, you must type `n' to prefix a count, so the - example above would be typed "n10s" instead. Commands for which - counts make no sense ignore them. In addition, movement commands can - be prefixed for greater control (see below). To cancel a count or a + number_pad option set, you must type `n' to prefix a count, so the + example above would be typed "n10s" instead. Commands for which + counts make no sense ignore them. In addition, movement commands can + be prefixed for greater control (see below). To cancel a count or a prefix, press the ESC key. - The list of commands is rather long, but it can be read at any + The list of commands is rather long, but it can be read at any time during the game through the `?' command, which accesses a menu of helpful texts. Here are the default key bindings for your reference: ? Help menu: display one of several help texts available. - / The "whatis" command, to tell what a symbol represents. You may - choose to specify a location or type a symbol (or even a whole - word) to explain. Specifying a location is done by moving the - cursor to a particular spot on the map and then pressing one of + / The "whatis" command, to tell what a symbol represents. You may + choose to specify a location or type a symbol (or even a whole + word) to explain. Specifying a location is done by moving the + cursor to a particular spot on the map and then pressing one of `.', `,', `;', or `:'. `.' will explain the symbol at the chosen - location, conditionally check for "More info?" depending upon + location, conditionally check for "More info?" depending upon whether the help option is on, and then you will be asked to pick - another location; `,' will explain the symbol but skip any addi- - tional information, then let you pick another location; `;' will - skip additional info and also not bother asking you to choose - another location to examine; `:' will show additional info, if - any, without asking for confirmation. When picking a location, + another location; `,' will explain the symbol but skip any addi- + tional information, then let you pick another location; `;' will + skip additional info and also not bother asking you to choose + another location to examine; `:' will show additional info, if + any, without asking for confirmation. When picking a location, pressing the ESC key will terminate this command, or pressing `?' will give a brief reminder about how it works. If the autodescribe option is on, a short description of what you see at each location is shown as you move the cursor. Typing `#' - while picking a location will toggle that option on or off. The - whatis_coord option controls whether the short description + while picking a location will toggle that option on or off. The + whatis_coord option controls whether the short description includes map coordinates. - Specifying a name rather than a location always gives any addi- + Specifying a name rather than a location always gives any addi- tional information available about that name. - You may also request a description of nearby monsters, all mon- - sters currently displayed, nearby objects, or all objects. The - whatis_coord option controls which format of map coordinate is + You may also request a description of nearby monsters, all mon- + sters currently displayed, nearby objects, or all objects. The + whatis_coord option controls which format of map coordinate is included with their descriptions. & Tell what a command does. - < Go up to the previous level (if you are on a staircase or lad- + < Go up to the previous level (if you are on a staircase or lad- der). > Go down to the next level (if you are on a staircase or ladder). [yuhjklbn] - Go one step in the direction indicated (see Figure 3). If you - sense or remember a monster there, you will fight the monster - instead. Only these one-step movement commands cause you to + Go one step in the direction indicated (see Figure 3). If you + sense or remember a monster there, you will fight the monster + instead. Only these one-step movement commands cause you to - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -748,27 +748,27 @@ remember a monster there). A few non-movement commands use the `m' prefix to request operat- - ing via menu (to temporarily override the menustyle:traditional - option). Primarily useful for `,' (pickup) when there is only - one class of objects present (where there won't be any "what - kinds of objects?" prompt, so no opportunity to answer `m' at + ing via menu (to temporarily override the menustyle:traditional + option). Primarily useful for `,' (pickup) when there is only + one class of objects present (where there won't be any "what + kinds of objects?" prompt, so no opportunity to answer `m' at that prompt). The prefix will make "#travel" command show a menu of interesting - targets in sight. It can also be used with the `\' (known, show + targets in sight. It can also be used with the `\' (known, show a list of all discovered objects) and the ``' (knownclass, show a - list of discovered objects in a particular class) commands to - offer a menu of several sorting alternatives (which sets a new + list of discovered objects in a particular class) commands to + offer a menu of several sorting alternatives (which sets a new value for the sortdiscoveries option); also for "#vanquished" and "#genocided" commands to offer a sorting menu. - A few other commands (eat food, offer sacrifice, apply tinning- - kit, drink/quaff, dip, tip container) use the `m' prefix to skip - checking for applicable objects on the floor and go straight to - checking inventory, or (for "#loot" to remove a saddle), skip + A few other commands (eat food, offer sacrifice, apply tinning- + kit, drink/quaff, dip, tip container) use the `m' prefix to skip + checking for applicable objects on the floor and go straight to + checking inventory, or (for "#loot" to remove a saddle), skip containers and go straight to adjacent monsters. - In debug mode (aka "wizard mode"), the `m' prefix may also be + In debug mode (aka "wizard mode"), the `m' prefix may also be used with the "#teleport" and "#wizlevelport" commands. F[yuhjklbn] @@ -778,15 +778,15 @@ Prefix: move until something interesting is found. G[yuhjklbn] or +[yuhjklbn] - Prefix: similar to `g', but forking of corridors is not consid- + Prefix: similar to `g', but forking of corridors is not consid- ered interesting. - Note: + means holding the or key - down like while typing and releasing , then releas- - ing . ^ is used as shorthand elsewhere in the + Note: + means holding the or key + down like while typing and releasing , then releas- + ing . ^ is used as shorthand elsewhere in the - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -796,39 +796,39 @@ - Guidebook to mean the same thing. Control characters are case- + Guidebook to mean the same thing. Control characters are case- insensitive so ^x and ^X are the same. M[yuhjklbn] - Old versions supported `M' as a movement prefix which combined - the effect of `m' with +. That is no longer + Old versions supported `M' as a movement prefix which combined + the effect of `m' with +. That is no longer supported as a prefix but similar effect can be achieved by using - `m' and G in combination. m can also be used in com- + `m' and G in combination. m can also be used in com- bination with g, +, or +. _ Travel to a map location via a shortest-path algorithm. - The shortest path is computed over map locations the hero knows - about (e.g. seen or previously traversed). If there is no known - path, a guess is made instead. Stops on most of the same condi- - tions as the `G' prefix, but without picking up objects, so - implicitly forces the `m' prefix. For ports with mouse support, - the command is also invoked when a mouse-click takes place on a + The shortest path is computed over map locations the hero knows + about (e.g. seen or previously traversed). If there is no known + path, a guess is made instead. Stops on most of the same condi- + tions as the `G' prefix, but without picking up objects, so + implicitly forces the `m' prefix. For ports with mouse support, + the command is also invoked when a mouse-click takes place on a location other than the current position. . Wait or rest, do nothing for one turn. Precede with the `m' pre- - fix to wait for a turn even next to a hostile monster, if + fix to wait for a turn even next to a hostile monster, if safe_wait is on. a Apply (use) a tool (pick-axe, key, lamp...). - If used on a wand, that wand will be broken, releasing its magic + If used on a wand, that wand will be broken, releasing its magic in the process. Confirmation is required. A Remove one or more worn items, such as armor. - Use `T' (take off) to take off only one piece of armor or `R' + Use `T' (take off) to take off only one piece of armor or `R' (remove) to take off only one accessory. ^A Repeat the previous command. @@ -852,7 +852,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -864,8 +864,8 @@ "What kinds of things do you want to drop? [!%= BUCXPaium]" - you should type zero or more object symbols possibly followed by - `a' and/or `i' and/or `u' and/or `m'. In addition, one or more + you should type zero or more object symbols possibly followed by + `a' and/or `i' and/or `u' and/or `m'. In addition, one or more of the blessed/uncursed/cursed groups may be typed. DB - drop all objects known to be blessed. @@ -879,22 +879,22 @@ Dm - use a menu to pick which object(s) to drop. D%u - drop only unpaid food. - The last example shows a combination. There are four categories + The last example shows a combination. There are four categories of object filtering: class (`!' for potions, `?' for scrolls, and so on), shop status (`u' for unpaid, in other words, owned by the shop), bless/curse state (`B', `U', `C', and `X' as shown above), and novelty (`P', recently picked up items; controlled by picking up or dropping things rather than by any time factor). - If you specify more than one value in a category (such as "!?" - for potions and scrolls or "BU" for blessed and uncursed), an - inventory object will meet the criteria if it matches any of the + If you specify more than one value in a category (such as "!?" + for potions and scrolls or "BU" for blessed and uncursed), an + inventory object will meet the criteria if it matches any of the specified values (so "!?" means `!' or `?'). If you specify more than one category, an inventory object must meet each of the cat- egory criteria (so "%u" means class `%' and unpaid `u'). Lastly, - you may specify multiple values within multiple categories: - "!?BU" will select all potions and scrolls which are known to be - blessed or uncursed. (In versions prior to 3.6, filter combina- + you may specify multiple values within multiple categories: + "!?BU" will select all potions and scrolls which are known to be + blessed or uncursed. (In versions prior to 3.6, filter combina- tions behaved differently.) ^D Kick something (usually a door). @@ -903,13 +903,13 @@ Normally checks for edible item(s) on the floor, then if none are found or none are chosen, checks for edible item(s) in inventory. - Precede `e' with the `m' prefix to bypass attempting to eat any- + Precede `e' with the `m' prefix to bypass attempting to eat any- thing off the floor. - If you attempt to eat while already satiated, you might choke to - death. If you risk it, you will be asked whether to "continue - eating?" if you survive the first bite. You can set the para- - noid_confirmation:eating option to require a response of yes + If you attempt to eat while already satiated, you might choke to + death. If you risk it, you will be asked whether to "continue + eating?" if you survive the first bite. You can set the para- + noid_confirmation:eating option to require a response of yes instead of just y. E Engrave a message on the floor. @@ -918,7 +918,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -928,29 +928,29 @@ - Engraving the word "Elbereth" will cause most monsters to not + Engraving the word "Elbereth" will cause most monsters to not attack you hand-to-hand (but if you attack, you will rub it out); this is often useful to give yourself a breather. - f Fire (shoot or throw) one of the objects placed in your quiver - (or quiver sack, or that you have at the ready). You may select - ammunition with a previous `Q' command, or let the computer pick - something appropriate if autoquiver is true. If your wielded - weapon has the throw-and-return property, your quiver is empty, - and autoquiver is false, you will throw that wielded weapon - instead of filling the quiver. This will also automatically use - a polearm if wielded. If fireassist is true, firing will auto- + f Fire (shoot or throw) one of the objects placed in your quiver + (or quiver sack, or that you have at the ready). You may select + ammunition with a previous `Q' command, or let the computer pick + something appropriate if autoquiver is true. If your wielded + weapon has the throw-and-return property, your quiver is empty, + and autoquiver is false, you will throw that wielded weapon + instead of filling the quiver. This will also automatically use + a polearm if wielded. If fireassist is true, firing will auto- matically try to wield a launcher (for example, a bow or a sling) - matching the ammo in the quiver; this might take multiple turns, - and get interrupted by a monster. Remember to swap back to your + matching the ammo in the quiver; this might take multiple turns, + and get interrupted by a monster. Remember to swap back to your main melee weapon afterwards. See also `t' (throw) for more general throwing and shooting. i List your inventory (everything you're carrying). - I List selected parts of your inventory, usually be specifying the - character for a particular set of objects, like `[' for armor or + I List selected parts of your inventory, usually be specifying the + character for a particular set of objects, like `[' for armor or `!' for potions. I* - list all gems in inventory; @@ -967,24 +967,24 @@ O Set options. - A menu showing the current option values will be displayed. You + A menu showing the current option values will be displayed. You can change most values simply by selecting the menu entry for the - given option (ie, by typing its letter or clicking upon it, - depending on your user interface). For the non-boolean choices, - a further menu or prompt will appear once you've closed this - menu. The available options are listed later in this Guidebook. - Options are usually set before the game rather than with the `O' - command; see the section on options below. Precede `O' with the + given option (ie, by typing its letter or clicking upon it, + depending on your user interface). For the non-boolean choices, + a further menu or prompt will appear once you've closed this + menu. The available options are listed later in this Guidebook. + Options are usually set before the game rather than with the `O' + command; see the section on options below. Precede `O' with the `m' prefix to show advanced options. ^O Show overview. - Shortcut for "#overview": list interesting dungeon levels vis- + Shortcut for "#overview": list interesting dungeon levels vis- ited. - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -994,33 +994,33 @@ - (Prior to 3.6.0, `^O' was a debug mode command which listed the - placement of all special levels. Use "#wizwhere" to run that + (Prior to 3.6.0, `^O' was a debug mode command which listed the + placement of all special levels. Use "#wizwhere" to run that command.) p Pay your shopping bill. P Put on an accessory (ring, amulet, or blindfold). - This command may also be used to wear armor. The prompt for - which inventory item to use will only list accessories, but + This command may also be used to wear armor. The prompt for + which inventory item to use will only list accessories, but choosing an unlisted item of armor will attempt to wear it. (See - the `W' command below. It lists armor as the inventory choices + the `W' command below. It lists armor as the inventory choices but will accept an accessory and attempt to put that on.) ^P Repeat previous message. - Subsequent `^P's repeat earlier messages. For some interfaces, + Subsequent `^P's repeat earlier messages. For some interfaces, the behavior can be varied via the msg_window option. q Quaff (drink) something (potion, water, etc). - When there is a fountain or sink present, it asks whether to + When there is a fountain or sink present, it asks whether to drink from that. If that is declined, then it offers a chance to - choose a potion from inventory. Precede `q' with the `m' prefix + choose a potion from inventory. Precede `q' with the `m' prefix to skip asking about drinking from a fountain or sink. - Q Select an object for your quiver, quiver sack, or just generally + Q Select an object for your quiver, quiver sack, or just generally at the ready (only one of these is available at a time). You can then throw this (or one of these) using the `f' command. @@ -1033,24 +1033,24 @@ be removed without asking, but you can set the paranoid_confirma- tion:Remove option to require a prompt. - This command may also be used to take off armor. The prompt for - which inventory item to remove only lists worn accessories, but + This command may also be used to take off armor. The prompt for + which inventory item to remove only lists worn accessories, but an item of worn armor can be chosen. (See the `T' command below. It lists armor as the inventory choices but will accept an acces- sory and attempt to remove it.) ^R Redraw the screen. - s Search for secret doors and traps around you. It usually takes - several tries to find something. Precede with the `m' prefix to + s Search for secret doors and traps around you. It usually takes + several tries to find something. Precede with the `m' prefix to search for a turn even next to a hostile monster, if safe_wait is on. - Can also be used to figure out whether there is still a monster + Can also be used to figure out whether there is still a monster at an adjacent "remembered, unseen monster" marker. - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -1060,26 +1060,26 @@ - S Save the game (which suspends play and exits the program). The - saved game will be restored automatically the next time you play + S Save the game (which suspends play and exits the program). The + saved game will be restored automatically the next time you play using the same character name. - In normal play, once a saved game is restored the file used to - hold the saved data is deleted. In explore mode, once restora- - tion is accomplished you are asked whether to keep or delete the - file. Keeping the file makes it feasible to play for a while + In normal play, once a saved game is restored the file used to + hold the saved data is deleted. In explore mode, once restora- + tion is accomplished you are asked whether to keep or delete the + file. Keeping the file makes it feasible to play for a while then quit without saving and later restore again. - There is no "save current game state and keep playing" command, - not even in explore mode where saved game files can be kept and + There is no "save current game state and keep playing" command, + not even in explore mode where saved game files can be kept and re-used. t Throw an object or shoot a projectile. There's no separate "shoot" command. If you throw an arrow while - wielding a bow, you are shooting that arrow and any weapon skill - bonus or penalty for bow applies. If you throw an arrow while - not wielding a bow, you are throwing it by hand and it will gen- + wielding a bow, you are shooting that arrow and any weapon skill + bonus or penalty for bow applies. If you throw an arrow while + not wielding a bow, you are throwing it by hand and it will gen- erally be less effective than when shot. See also `f' (fire) for throwing or shooting an item pre-selected @@ -1087,17 +1087,17 @@ T Take off armor. - If you're wearing more than one piece, you'll be prompted for + If you're wearing more than one piece, you'll be prompted for which one to take off. (Note that this treats a cloak covering a suit and/or a shirt, or a suit covering a shirt, as if the under- - lying items weren't there.) When you're only wearing one, then - by default it will be taken off without asking, but you can set + lying items weren't there.) When you're only wearing one, then + by default it will be taken off without asking, but you can set the paranoid_confirmation:Remove option to require a prompt. - This command may also be used to remove accessories. The prompt + This command may also be used to remove accessories. The prompt for which inventory item to take off only lists worn armor, but a - worn accessory can be chosen. (See the `R' command above. It - lists accessories as the inventory choices but will accept an + worn accessory can be chosen. (See the `R' command above. It + lists accessories as the inventory choices but will accept an item of armor and attempt to take it off.) ^T Teleport, if you have the ability. @@ -1110,13 +1110,13 @@ w- - wield nothing, use your bare (or gloved) hands. - Some characters can wield two weapons at once; use the `X' com- + Some characters can wield two weapons at once; use the `X' com- mand (or the "#twoweapon" extended command) to do so. - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -1128,38 +1128,38 @@ W Wear armor. - This command may also be used to put on an accessory (ring, - amulet, or blindfold). The prompt for which inventory item to + This command may also be used to put on an accessory (ring, + amulet, or blindfold). The prompt for which inventory item to use will only list armor, but choosing an unlisted accessory will - attempt to put it on. (See the `P' command above. It lists - accessories as the inventory choices but will accept an item of + attempt to put it on. (See the `P' command above. It lists + accessories as the inventory choices but will accept an item of armor and attempt to wear it.) - x Exchange your wielded weapon with the item in your alternate + x Exchange your wielded weapon with the item in your alternate weapon slot. The latter is used as your secondary weapon when engaging in two- - weapon combat. Note that if one of these slots is empty, the + weapon combat. Note that if one of these slots is empty, the exchange still takes place. - X Toggle two-weapon combat, if your character can do it. Also + X Toggle two-weapon combat, if your character can do it. Also available via the "#twoweapon" extended command. - (In versions prior to 3.6 this keystroke ran the command to + (In versions prior to 3.6 this keystroke ran the command to switch from normal play to "explore mode", also known as "discov- ery mode", which has now been moved to "#exploremode" and M-X.) ^X Display basic information about your character. - Displays name, role, race, gender (unless role name makes that - redundant, such as Caveman or Priestess), and alignment, along - with your patron deity and his or her opposition. It also shows - most of the various items of information from the status line(s) - in a less terse form, including several additional things which + Displays name, role, race, gender (unless role name makes that + redundant, such as Caveman or Priestess), and alignment, along + with your patron deity and his or her opposition. It also shows + most of the various items of information from the status line(s) + in a less terse form, including several additional things which don't appear in the normal status display due to space considera- tions. - In normal play, that's all that `^X' displays. In explore mode, + In normal play, that's all that `^X' displays. In explore mode, the role and status feedback is augmented by the information pro- vided by enlightenment magic. @@ -1171,7 +1171,7 @@ Z. - to cast at yourself, use `.' for the direction. - ^Z Suspend the game (UNIX(R) versions with job control only). See + ^Z Suspend the game (UNIX(R) versions with job control only). See "#suspend" below for more details. : Look at what is here. @@ -1182,7 +1182,7 @@ (R)UNIX is a registered trademark of The Open Group. - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -1221,15 +1221,15 @@ + List the spells you know. - Using this command, you can also rearrange the order in which - your spells are listed, either by sorting the entire list or by - picking one spell from the menu then picking another to swap - places with it. Swapping pairs of spells changes their casting - letters, so the change lasts after the current `+' command fin- - ishes. Sorting the whole list is temporary. To make the most - recent sort order persist beyond the current `+' command, choose - the sort option again and then pick "reassign casting letters". - (Any spells learned after that will be added to the end of the + Using this command, you can also rearrange the order in which + your spells are listed, either by sorting the entire list or by + picking one spell from the menu then picking another to swap + places with it. Swapping pairs of spells changes their casting + letters, so the change lasts after the current `+' command fin- + ishes. Sorting the whole list is temporary. To make the most + recent sort order persist beyond the current `+' command, choose + the sort option again and then pick "reassign casting letters". + (Any spells learned after that will be added to the end of the list rather than be inserted into the sorted ordering.) \ Show what types of objects have been discovered. @@ -1240,15 +1240,15 @@ May be preceded by `m' to select preferred display order. - | If persistent inventory display is supported and enabled (with - the perm_invent option), interact with it instead of with the + | If persistent inventory display is supported and enabled (with + the perm_invent option), interact with it instead of with the map. - Allows scrolling with the menu_first_page, menu_previous_page, - menu_next_page, and menu_last_page keys (`^', `<', `>', `|' by + Allows scrolling with the menu_first_page, menu_previous_page, + menu_next_page, and menu_last_page keys (`^', `<', `>', `|' by - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -1259,7 +1259,7 @@ default). Some interfaces also support menu_shift_left and - menu_shift_right keys (`{' and `}' by default). Use the Return + menu_shift_right keys (`{' and `}' by default). Use the Return (aka Enter) or Escape key to resume play. ! Escape to a shell. See "#shell" below for more details. @@ -1268,53 +1268,53 @@ of the current level's map without monsters; without monsters and objects; or without monsters, objects, and traps. - The key is also shown as on some keyboards or - on others. It is sometimes displayed as ^? even though + The key is also shown as on some keyboards or + on others. It is sometimes displayed as ^? even though that is not an actual control character. - Many terminals have an option to swap the and - keys, so typing the key might not execute this - command. If that happens, you can use the extended command + Many terminals have an option to swap the and + keys, so typing the key might not execute this + command. If that happens, you can use the extended command "#terrain" instead. # Perform an extended command. - As you can see, the authors of NetHack used up all the letters, + As you can see, the authors of NetHack used up all the letters, so this is a way to introduce the less frequently used commands. What - extended commands are available depends on what features the game was + extended commands are available depends on what features the game was compiled with. #adjust - Adjust inventory letters (most useful when the fixinv option is + Adjust inventory letters (most useful when the fixinv option is "on"). Autocompletes. Default key is `M-a'. - This command allows you to move an item from one particular - inventory slot to another so that it has a letter which is more - meaningful for you or that it will appear in a particular loca- - tion when inventory listings are displayed. You can move to a - currently empty slot, or if the destination is occupied--and - won't merge--the item there will swap slots with the one being - moved. "#adjust" can also be used to split a stack of objects; + This command allows you to move an item from one particular + inventory slot to another so that it has a letter which is more + meaningful for you or that it will appear in a particular loca- + tion when inventory listings are displayed. You can move to a + currently empty slot, or if the destination is occupied--and + won't merge--the item there will swap slots with the one being + moved. "#adjust" can also be used to split a stack of objects; when choosing the item to adjust, enter a count prior to its let- ter. - Adjusting without a count used to collect all compatible stacks - when moving to the destination. That behavior has been changed; - to gather compatible stacks, "#adjust" a stack into its own + Adjusting without a count used to collect all compatible stacks + when moving to the destination. That behavior has been changed; + to gather compatible stacks, "#adjust" a stack into its own inventory slot. If it has a name assigned, other stacks with the - same name or with no name will merge provided that all their - other attributes match. If it does not have a name, only other + same name or with no name will merge provided that all their + other attributes match. If it does not have a name, only other stacks with no name are eligible. In either case, otherwise com- - patible stacks with a different name will not be merged. This + patible stacks with a different name will not be merged. This contrasts with using "#adjust" to move from one slot to a differ- - ent slot. In that situation, moving (no count given) a compati- - ble stack will merge if either stack has a name when the other - doesn't and give that name to the result, while splitting (count + ent slot. In that situation, moving (no count given) a compati- + ble stack will merge if either stack has a name when the other + doesn't and give that name to the result, while splitting (count - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -1324,13 +1324,13 @@ - given) will ignore the source stack's name when deciding whether + given) will ignore the source stack's name when deciding whether to merge with the destination stack. #annotate Allows you to specify one line of text to associate with the cur- rent dungeon level. All levels with annotations are displayed by - the "#overview" command. Autocompletes. Default key is `M-A', + the "#overview" command. Autocompletes. Default key is `M-A', and also `^N' if number_pad is on. #apply @@ -1340,7 +1340,7 @@ If the tool used acts on items on the floor, using the `m' prefix skips those items. - If used on a wand, that wand will be broken, releasing its magic + If used on a wand, that wand will be broken, releasing its magic in the process. Confirmation is required. #attributes @@ -1350,14 +1350,14 @@ Toggle the autopickup option on/off. Default key is `@'. #bugreport - Bring up a browser window to submit a report to the NetHack - Development Team. Can be disabled at the time the program is - built; when enabled, CRASHREPORTURL must be set in the system + Bring up a browser window to submit a report to the NetHack + Development Team. Can be disabled at the time the program is + built; when enabled, CRASHREPORTURL must be set in the system configuration file. #call - Call (name) a monster, or an object in inventory, on the floor, - or in the discoveries list, or add an annotation for the current + Call (name) a monster, or an object in inventory, on the floor, + or in the discoveries list, or add an annotation for the current level (same as "#annotate"). Default key is `C'. #cast @@ -1367,20 +1367,20 @@ Talk to someone. Default key is `M-c'. #chronicle - Show a list of important game events. + Show a list of important game events. Default key is `v'. #close Close a door. Default key is `c'. #conduct - List voluntary challenges you have maintained. Autocompletes. + List voluntary challenges you have maintained. Autocompletes. Default key is `M-C'. See the section below entitled "Conduct" for details. - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -1397,7 +1397,7 @@ Dip an object into something. Autocompletes. Default key is `M- d'. - The `m' prefix skips dipping into a fountain or pool if there is + The `m' prefix skips dipping into a fountain or pool if there is one at your location. #down @@ -1410,22 +1410,22 @@ Drop specific item types. Default key is `D'. #eat - Eat something. Default key is `e'. The `m' prefix skips eating + Eat something. Default key is `e'. The `m' prefix skips eating items on the floor. #engrave Engrave writing on the floor. Default key is `E'. #enhance - Advance or check weapon and spell skills. Autocompletes. + Advance or check weapon and spell skills. Autocompletes. Default key is `M-e'. #exploremode Switch from normal play to non-scoring explore mode. Default key is `M-X'. - Requires confirmation; default response is n (no). To really - switch to explore mode, respond with y. You can set the para- + Requires confirmation; default response is n (no). To really + switch to explore mode, respond with y. You can set the para- noid_confirmation:quit option to require a response of yes instead. @@ -1441,12 +1441,12 @@ Force a lock. Autocompletes. Default key is `M-f'. #genocided - List any monster types which have been genocided. In explore - mode and debug mode it also shows types which have become + List any monster types which have been genocided. In explore + mode and debug mode it also shows types which have become extinct. - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -1457,13 +1457,13 @@ The display order is the same as is used by #vanquished. The `m' - prefix brings up a menu of available sorting orders, and doing - that for either #genocided or #vanquished changes the order for + prefix brings up a menu of available sorting orders, and doing + that for either #genocided or #vanquished changes the order for both. - If the sorting order is "count high to low" or "count low to - high" (which are applicable for #vanquished), that will be - ignored for #genocided and alphabetical will be used instead. + If the sorting order is "count high to low" or "count low to + high" (which are applicable for #vanquished), that will be + ignored for #genocided and alphabetical will be used instead. The menu omits those two choices when used for #genocide. Autocompletes. Default key is `M-g'. @@ -1473,20 +1473,20 @@ is `;'. #help - Show the help menu. Default key is `?', and also `h' if num- + Show the help menu. Default key is `?', and also `h' if num- ber_pad is on. #herecmdmenu - Show a menu of possible actions directed at your current loca- - tion. The menu is limited to a subset of the likeliest actions, + Show a menu of possible actions directed at your current loca- + tion. The menu is limited to a subset of the likeliest actions, not an exhaustive set of all possibilities. Autocompletes. - If mouse support is enabled and the herecmd_menu option is On, - clicking on the hero (or steed when mounted) will execute this + If mouse support is enabled and the herecmd_menu option is On, + clicking on the hero (or steed when mounted) will execute this command. #history - Show long version and game history. Default key is `V'. + Show long version and game history. #inventory Show your inventory. Default key is `i'. @@ -1495,15 +1495,15 @@ Inventory specific item types. Default key is `I'. #invoke - Invoke an object's special powers. Autocompletes. Default key + Invoke an object's special powers. Autocompletes. Default key is `M-i'. #jump - Jump to another location. Autocompletes. Default key is `M-j', + Jump to another location. Autocompletes. Default key is `M-j', and also `j' if number_pad is on. #kick - Kick something. Default key is `^D', and `k' if number_pad is + Kick something. Default key is `^D', and `k' if number_pad is on. #known @@ -1512,7 +1512,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -1522,12 +1522,12 @@ - The `m' prefix allows assigning a new value to the sortdiscover- + The `m' prefix allows assigning a new value to the sortdiscover- ies option to control the order in which the discoveries are dis- played. #knownclass - Show discovered types for one class of objects. Default key is + Show discovered types for one class of objects. Default key is ``'. The `m' prefix operates the same as for "#known". @@ -1545,26 +1545,26 @@ Describe what you can see, or remember, of your surroundings. #loot - Loot a box or bag on the floor beneath you, or the saddle from a + Loot a box or bag on the floor beneath you, or the saddle from a steed standing next to you. Autocompletes. Precede with the `m' - prefix to skip containers at your location and go directly to - removing a saddle. Default key is `M-l', and also `l' if num- + prefix to skip containers at your location and go directly to + removing a saddle. Default key is `M-l', and also `l' if num- ber_pad is on. #monster - Use a monster's special ability (when polymorphed into monster + Use a monster's special ability (when polymorphed into monster form). Autocompletes. Default key is `M-m'. #name - Name a monster, an individual object, or a type of object. Same + Name a monster, an individual object, or a type of object. Same as "#call". Autocompletes. Default keys are `N', `M-n', and `M- N'. #offer - Offer a sacrifice to the gods. Autocompletes. Default key is + Offer a sacrifice to the gods. Autocompletes. Default key is `M-o'. - You'll need to find an altar to have any chance at success. + You'll need to find an altar to have any chance at success. Corpses of recently killed monsters are the fodder of choice. The `m' prefix skips offering any items which are on the altar. @@ -1573,12 +1573,12 @@ Open a door. Default key is `o'. #options - Show and change option settings. Default key is `O'. Precede + Show and change option settings. Default key is `O'. Precede with the `m' prefix to show advanced options. - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -1589,32 +1589,32 @@ #optionsfull - Show advanced game option settings. No default key. Precede - with the `m' prefix to execute the simpler options command. - (Mainly useful if you use BINDING=O:optionsfull to switch `O' + Show advanced game option settings. No default key. Precede + with the `m' prefix to execute the simpler options command. + (Mainly useful if you use BINDING=O:optionsfull to switch `O' from simple options back to traditional advanced options.) #overview - Display information you've discovered about the dungeon. Any - visited level with an annotation is included, and many things - (altars, thrones, fountains, and so on; extra stairs leading to + Display information you've discovered about the dungeon. Any + visited level with an annotation is included, and many things + (altars, thrones, fountains, and so on; extra stairs leading to another dungeon branch) trigger an automatic annotation. If dun- geon overview is chosen during end-of-game disclosure, every vis- ited level will be included regardless of annotations. - Precede #overview with the `m' prefix to display the dungeon - overview as a menu where you can select any visited level to add - or remove an annotation without needing to return to that level. - This will also force all visited levels to be displayed rather + Precede #overview with the `m' prefix to display the dungeon + overview as a menu where you can select any visited level to add + or remove an annotation without needing to return to that level. + This will also force all visited levels to be displayed rather than just the "interesting" subset. Autocompletes. Default keys are `^O', and `M-O'. #panic - Test the panic routine. Terminates the current game. Autocom- + Test the panic routine. Terminates the current game. Autocom- pletes. Debug mode only. - Asks for confirmation; default is n (no); continue playing. To + Asks for confirmation; default is n (no); continue playing. To really panic, respond with y. You can set the paranoid_confirma- tion:quit option to require a response of yes instead. @@ -1622,10 +1622,10 @@ Pay your shopping bill. Default key is `p'. #perminv - If persistent inventory display is supported and enabled (with - the perm_invent option), interact with it instead of with the - map. You'll be prompted for menu scrolling keystrokes such as - `>' and `<'. Press Return or Escape to resume normal play. + If persistent inventory display is supported and enabled (with + the perm_invent option), interact with it instead of with the + map. You'll be prompted for menu scrolling keystrokes such as + `>' and `<'. Press Return or Escape to resume normal play. Default key is `|'. #pickup @@ -1638,13 +1638,13 @@ #pray Pray to the gods for help. Autocompletes. Default key is `M-p'. - Praying too soon after receiving prior help is a bad idea. - (Hint: entering the dungeon alive is treated as having received - help. You probably shouldn't start off a new game by praying - right away.) Since using this command by accident can cause + Praying too soon after receiving prior help is a bad idea. + (Hint: entering the dungeon alive is treated as having received + help. You probably shouldn't start off a new game by praying + right away.) Since using this command by accident can cause - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -1654,8 +1654,8 @@ - trouble, there is an option to make you confirm your intent - before praying. It is enabled by default, and you can reset the + trouble, there is an option to make you confirm your intent + before praying. It is enabled by default, and you can reset the paranoid_confirmation option to disable it. #prevmsg @@ -1674,20 +1674,20 @@ Quit the program without saving your game. Autocompletes. Since using this command by accident would throw away the current - game, you are asked to confirm your intent before quitting. - Default response is n (no); continue playing. To really quit, - respond with y. You can set the paranoid_confirmation:quit + game, you are asked to confirm your intent before quitting. + Default response is n (no); continue playing. To really quit, + respond with y. You can set the paranoid_confirmation:quit option to require a response of yes instead. #quiver Select ammunition for quiver. Default key is `Q'. #read - Read a scroll, a spellbook, or something else. Default key is + Read a scroll, a spellbook, or something else. Default key is `r'. #redraw - Redraw the screen. Default key is `^R', and also `^L' if num- + Redraw the screen. Default key is `^R', and also `^L' if num- ber_pad is on. #remove @@ -1697,20 +1697,20 @@ Repeat the previous command. Default key is `^A'. #reqmenu - Prefix key to modify the behavior or request menu from some com- - mands. Prevents autopickup when used with movement commands. + Prefix key to modify the behavior or request menu from some com- + mands. Prevents autopickup when used with movement commands. Default key is `m'. #retravel - Travel to a previously selected travel destination. Default key + Travel to a previously selected travel destination. Default key is `C-_'. See also #travel. #ride - Ride (or stop riding) a saddled creature. Autocompletes. + Ride (or stop riding) a saddled creature. Autocompletes. Default key is `M-R'. - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -1724,8 +1724,8 @@ Rub a lamp or a stone. Autocompletes. Default key is `M-r'. #run - Prefix key to run towards a direction. Default key is `G' when - number_pad is off, `5' when number_pad is set to 1 or 3, other- + Prefix key to run towards a direction. Default key is `G' when + number_pad is off, `5' when number_pad is set to 1 or 3, other- wise `M-5' when it is set to 2 or 4. #rush @@ -1737,12 +1737,12 @@ Save the game and exit the program. Default key is `S'. #saveoptions - Save configuration options to the config file. This will over- - write the file, removing all comments, so if you have manually + Save configuration options to the config file. This will over- + write the file, removing all comments, so if you have manually edited the config file, don't use this. #search - Search for traps and secret doors around you. Default key is + Search for traps and secret doors around you. Default key is `s'. #seeall @@ -1759,24 +1759,24 @@ #seearmor Show the armor currently worn. Default key is `['. - Will display worn armor in a menu even when there is only thing + Will display worn armor in a menu even when there is only thing worn. #seerings Show the ring(s) currently worn. Default key is `='. - Will display worn rings in a menu if there are two (or there is - just one and is a meat ring rather than a "real" ring). Use the + Will display worn rings in a menu if there are two (or there is + just one and is a meat ring rather than a "real" ring). Use the `m' prefix to force a menu for one ring. #seetools Show the tools currently in use. Default key is `('. - Will display the result in a message if there is one tool in use + Will display the result in a message if there is one tool in use (worn blindfold or towel or lenses, lit lamp(s) and/or candle(s), - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -1792,32 +1792,32 @@ #seeweapon Show the weapon currently wielded. Default key is `)'. - If dual-wielding, a separate message about the secondary weapon - will be given. Using the `m' prefix will force a menu and it + If dual-wielding, a separate message about the secondary weapon + will be given. Using the `m' prefix will force a menu and it will include primary weapon, alternate weapon even when not dual- - wielding, and also whatever is currently assigned to the quiver + wielding, and also whatever is currently assigned to the quiver slot. #shell - Do a shell escape, switching from NetHack to a subprocess. Can - be disabled at the time the program is built. When enabled, + Do a shell escape, switching from NetHack to a subprocess. Can + be disabled at the time the program is built. When enabled, access for specific users can be controlled by the system config- - uration file. Use the shell command `exit' to return to the + uration file. Use the shell command `exit' to return to the game. Default key is `!'. #showgold - Report the gold in your inventory, including gold you know about - in containers you're carrying. If you are inside a shop, report + Report the gold in your inventory, including gold you know about + in containers you're carrying. If you are inside a shop, report any credit or debt you have in that shop. Default key is `$'. #showspells List and reorder known spells. Default key is `+'. #showtrap - Describe an adjacent trap, possibly covered by objects or a mon- + Describe an adjacent trap, possibly covered by objects or a mon- ster. To be eligible, the trap must already be discovered. (The "#terrain" command can display your map with all objects and mon- - sters temporarily removed, making it possible to see all discov- + sters temporarily removed, making it possible to see all discov- ered traps.) Default key is `^'. #sit @@ -1827,10 +1827,10 @@ Show memory usage statistics. Autocompletes. Debug mode only. #suspend - Suspend the game, switching from NetHack to the terminal it was - started from without performing save-and-exit. Can be disabled - at the time the program is built. When enabled, mainly useful - for tty and curses interfaces on UNIX. Use the shell command + Suspend the game, switching from NetHack to the terminal it was + started from without performing save-and-exit. Can be disabled + at the time the program is built. When enabled, mainly useful + for tty and curses interfaces on UNIX. Use the shell command `fg' to return to the game. Default key is `^Z'. #swap @@ -1842,7 +1842,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -1859,25 +1859,25 @@ Teleport around the level. Default key is `^T'. #terrain - Show map without obstructions. In normal play you can view the - explored portion of the current level's map without monsters; - without monsters and objects; or without monsters, objects, and + Show map without obstructions. In normal play you can view the + explored portion of the current level's map without monsters; + without monsters and objects; or without monsters, objects, and traps. If there are visible clouds of gas in view, they are treated like - traps when deciding whether to show them or the floor underneath + traps when deciding whether to show them or the floor underneath them. - In explore mode, you can choose to view the full map rather than - just its explored portion. In debug mode there are additional + In explore mode, you can choose to view the full map rather than + just its explored portion. In debug mode there are additional choices. - Autocompletes. Default key is `' or `' (see Del + Autocompletes. Default key is `' or `' (see Del above). #therecmdmenu - Show a menu of possible actions directed at a location next to - you. The menu is limited to a subset of the likeliest actions, + Show a menu of possible actions directed at a location next to + you. The menu is limited to a subset of the likeliest actions, not an exhaustive set of all possibilities. Autocompletes. #throw @@ -1888,27 +1888,27 @@ #tip Tip over a container (bag or box) to pour out its contents. When - there are containers on the floor, the game will prompt to pick - one of them or "tip something being carried". If the latter is - chosen, there will be another prompt for which item from inven- + there are containers on the floor, the game will prompt to pick + one of them or "tip something being carried". If the latter is + chosen, there will be another prompt for which item from inven- tory to tip. The `m' prefix makes the command skip containers on the floor and - pick one from inventory, except for the special case of - menustyle:traditional with two or more containers present; that + pick one from inventory, except for the special case of + menustyle:traditional with two or more containers present; that situation will start with the floor container menu. Autocompletes. Default key is `M-T'. - #travel - Travel to a specific location on the map. Default key is `_'. - Using the "request menu" prefix shows a menu of interesting tar- - gets in sight without asking to move the cursor. When picking a - target with cursor and the autodescribe option is on, the top - line will show "(no travel path)" if your character does not know + #toggle + Toggle a boolean option on or off. Requires a parameter in + parenthesis, the name of the option to toggle. The option must + be settable in-game. - NetHack 3.7.0 May 1, 2025 + + + NetHack 3.7.0 March 25, 2026 @@ -1918,23 +1918,34 @@ + For example: + + BIND=':toggle(price_quotes) + BIND=@:toggle(autopickup) + + #travel + Travel to a specific location on the map. Default key is `_'. + Using the "request menu" prefix shows a menu of interesting tar- + gets in sight without asking to move the cursor. When picking a + target with cursor and the autodescribe option is on, the top + line will show "(no travel path)" if your character does not know of a path to that location. See also #retravel. #turn Turn undead away. Autocompletes. Default key is `M-t'. #twoweapon - Toggle two-weapon combat on or off. Autocompletes. Default key + Toggle two-weapon combat on or off. Autocompletes. Default key is `X', and also `M-2' if number_pad is off. - Note that you must use suitable weapons for this type of combat, + Note that you must use suitable weapons for this type of combat, or it will be automatically turned off. #untrap - Untrap something (trap, door, or chest). Default key is `M-u', + Untrap something (trap, door, or chest). Default key is `M-u', and `u' if number_pad is on. - In some circumstances it can also be used to rescue trapped mon- + In some circumstances it can also be used to rescue trapped mon- sters. #up @@ -1943,38 +1954,27 @@ #vanquished List vanquished monsters by type and count. - Note that the vanquished monsters list includes all monsters - killed by traps and each other as well as by you, and omits any - which got removed from the game without being killed (perhaps by - genocide, or by a mollified shopkeeper dismissing summoned Kops) + Note that the vanquished monsters list includes all monsters + killed by traps and each other as well as by you, and omits any + which got removed from the game without being killed (perhaps by + genocide, or by a mollified shopkeeper dismissing summoned Kops) or were already corpses when placed on the map. - Using the "request menu" prefix prior to #vanquished brings up a - menu of sorting orders available (provided that the vanquished - monsters list contains at least two types of monsters). Which- - ever ordering is picked gets assigned to the sortvanquished + Using the "request menu" prefix prior to #vanquished brings up a + menu of sorting orders available (provided that the vanquished + monsters list contains at least two types of monsters). Which- + ever ordering is picked gets assigned to the sortvanquished option so is remembered for subsequent #vanquished requests. The "#genocided" command shares this sorting order. - During end-of-game disclosure, when asked whether to show van- - quished monsters answering `a' will let you choose from the sort + During end-of-game disclosure, when asked whether to show van- + quished monsters answering `a' will let you choose from the sort menu. - Autocompletes. Default key is `M-V'. - - #version - Print compile time options for this version of NetHack. - - The second paragraph lists the user interface(s) that are - included. If there are more than one, you can use the windowtype - option in your run-time configuration file to select the one you - want. - - Autocompletes. Default key is `M-v'. - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -1984,16 +1984,28 @@ + Autocompletes. Default key is `M-V'. + + #version + Print compile time options for this version of NetHack. + + The second paragraph lists the user interface(s) that are + included. If there are more than one, you can use the windowtype + option in your run-time configuration file to select the one you + want. + + Autocompletes. Default key is `M-v'. + #versionshort - Show the program's version number, plus the date and time that - the running copy was built from sources (not the version's - release date). Default key is `v'. + Show the program's version number, plus the date and time that + the running copy was built from sources (not the version's + release date). Default key is `V'. #vision Show vision array. Autocompletes. Debug mode only. #wait - Rest one move while doing nothing. Default key is `.', and also + Rest one move while doing nothing. Default key is `.', and also ` ' if rest_on_space is on. #wear @@ -2003,7 +2015,7 @@ Tell what a key does. Default key is `&'. #whatis - Show what type of thing a symbol corresponds to. Default key is + Show what type of thing a symbol corresponds to. Default key is `/'. #wield @@ -2013,34 +2025,22 @@ Wipe off your face. Autocompletes. Default key is `M-w'. #wizborn - Show monster birth, death, genocide, and extinct statistics. + Show monster birth, death, genocide, and extinct statistics. Debug mode only. #wizbury - Bury objects under and around you. Autocompletes. Debug mode + Bury objects under and around you. Autocompletes. Debug mode only. #wizcast Cast any spell. Debug mode only. #wizdetect - Reveal hidden things (secret doors or traps or unseen monsters) - within a modest radius. No time elapses. Autocompletes. Debug - mode only. Default key is `^E'. - - #wizgenesis - Create a monster. May be prefixed by a count to create more than - one. Autocompletes. Debug mode only. Default key is `^G'. - - #wizidentify - Identify all items in inventory. Autocompletes. Debug mode - only. Default key is `^I'. - - #wizintrinsic - Set one or more intrinsic attributes. Autocompletes. Debug mode + Reveal hidden things (secret doors or traps or unseen monsters) + within a modest radius. No time elapses. Autocompletes. Debug - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -2050,45 +2050,71 @@ + mode only. Default key is `^E'. + + #wizgenesis + Create a monster. May be prefixed by a count to create more than + one. Autocompletes. Debug mode only. Default key is `^G'. + + #wizidentify + Identify all items in inventory. Autocompletes. Debug mode + only. Default key is `^I'. + + #wizintrinsic + Set one or more intrinsic attributes. Autocompletes. Debug mode only. #wizkill - Remove monsters from play by just pointing at them. By default - the hero gets credit or blame for killing the targets. Precede - this command with the `m' prefix to override that. Autocom- + Remove monsters from play by just pointing at them. By default + the hero gets credit or blame for killing the targets. Precede + this command with the `m' prefix to override that. Autocom- pletes. Debug mode only. #wizlevelport - Teleport to another level. Autocompletes. Debug mode only. + Teleport to another level. Autocompletes. Debug mode only. Default key is `^V'. #wizmap - Map the level. Autocompletes. Debug mode only. Default key is + Map the level. Autocompletes. Debug mode only. Default key is `^F'. #wizrumorcheck - Verify rumor boundaries by displaying first and last true rumors + Verify rumor boundaries by displaying first and last true rumors and first and last false rumors. - Also displays first, second, and last random engravings, epi- + Also displays first, second, and last random engravings, epi- taphs, and hallucinatory monsters. Autocompletes. Debug mode only. #wizseenv - Show map locations' seen vectors. Autocompletes. Debug mode + Show map locations' seen vectors. Autocompletes. Debug mode only. #wizsmell Smell monster. Autocompletes. Debug mode only. #wizwhere - Show locations of special levels. Autocompletes. Debug mode + Show locations of special levels. Autocompletes. Debug mode only. #wizwish - Wish for something. Autocompletes. Debug mode only. Default - key is `^W'. + Wish for something. Autocompletes. Debug mode only. Default + key is `^W'. Precede this command with the `m' prefix to show a + wish history menu. + + + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 33 + + #wmode Show wall modes. Autocompletes. Debug mode only. @@ -2104,18 +2130,6 @@ If your keyboard has a meta key (which, when pressed in combina- tion with another key, modifies it by setting the "meta" [8th, or "high"] bit), you can invoke many extended commands by meta-ing the - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 33 - - - first letter of the command. On Windows and MS-DOS, the "Alt" key can be used in this fashion. @@ -2158,21 +2172,7 @@ - - - - - - - - - - - - - - - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -2238,7 +2238,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -2304,7 +2304,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -2370,7 +2370,7 @@ them). Some monsters who can open doors can also use unlocking tools. - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -2436,7 +2436,7 @@ been nullified, giving access to whatever is beyond them. In the - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -2502,7 +2502,7 @@ play. - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -2568,7 +2568,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -2634,7 +2634,7 @@ attack, when guessing where an unseen monster is or when deliberately - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -2700,7 +2700,7 @@ noid_confirmation:attack option to require a response of "yes" - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -2766,7 +2766,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -2832,7 +2832,7 @@ are encumbered, one of the conditions Burdened, Stressed, Strained, - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -2898,7 +2898,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -2922,7 +2922,59 @@ "BUCX" for Blessed, Uncursed, Cursed, or unknown. (The term beatitude is occasionally used as well.) - 7.2. Weapons (`)') + 7.2. Artifacts + + Some objects have been imbued with special powers and are known + as Artifacts. They have specific types (such as long sword or orcish + dagger) and distinct names such as Giantslayer or Grimtooth. Artifact + weapons typically do more damage than their ordinary counterparts. + Some do extra damage against all monsters, others only against spe- + cific types of monsters so aren't better than regular weapons against + other types. Some confer defensive capabilities when wielded or have + other powers that aren't listed here. + + You might find them simply lying on the floor, including but not + limited to inside shops, or be granted as a reward for "#offer" on an + altar to your patron deity. A few might be dropped by monsters, or + might be converted from an ordinary object of the same type via + assigning the right name. Or you might wish for them, if you happen + to be granted a wish, but such wishes can fail. + + Some artifacts have a specific alignment, others don't. You + won't obtain aligned ones that have a different alignment from yours + via offering and might get a shock if you attempt to wish for any of + those or find one and attempt to use it. + + Each role has a distinct artifact that is contained in the Quest + dungeon branch. These are commonly known as quest artifacts. All are + aligned and most are non-weapons. They won't be found randomly. + + The `\' and ``a' commands will list artifacts that you have fully + identified (knowing the name and item type isn't sufficient). + + 7.3. Relics + + There are three unique items that are named and have limited spe- + cial powers but aren't classified as artifacts. Each is guarded by a + particular monster and you'll need to collect all three for use late + in the game. They are the Bell of Opening, the Book of the Dead, and + the Candelabrum of Invocation. Their corresponding descriptions when + not yet identified are silver bell, papyrus spellbook, and cande- + labrum. + + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 46 + + + + 7.4. Weapons (`)') Given a chance, most monsters in the Mazes of Menace will gratu- itously try to kill you. You need weapons for self-defense (killing @@ -2962,18 +3014,6 @@ And if you have proficiency in the "two weapon combat" skill, you may wield both weapons simultaneously as primary and secondary; use the `X' command to engage or disengage that. Only some types of charac- - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 46 - - - ters (barbarians, for instance) have the necessary skill available. Even with that skill, using two weapons at once incurs a penalty in the chance to hit your target compared to using just one weapon at a @@ -2988,13 +3028,25 @@ each weapon which existed in AD&D does roughly the same damage to mon- sters in NetHack. Some of the more obscure weapons (such as the aklys, lucern hammer, and bec-de-corbin) are defined in an appendix to + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 47 + + + Unearthed Arcana, an AD&D supplement. The commands to use weapons are `w' (wield), `t' (throw), `f' (fire), `Q' (quiver), `x' (exchange), `X' (twoweapon), and "#enhance" (see below). - 7.2.1. Throwing and shooting + 7.4.1. Throwing and shooting You can throw just about anything via the `t' command. It will prompt for the item to throw; picking `?' will list things in your @@ -3027,19 +3079,6 @@ quiver. The fire command also has extra assistance, if fireassist is on it will try to wield a launcher matching the ammo in the quiver. - - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 47 - - - Some characters have the ability to throw or shoot a volley of multiple items (from the same stack) in a single action. Knowing how to load several rounds of ammunition at once--or hold several missiles @@ -3055,11 +3094,23 @@ most 2 arrows are shot even if you could have fired 3. If you specify a larger number than would have been shot ("4f" in this example), you'll just end up shooting the same number (3, here) as if no limit + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 48 + + + had been specified. Once the volley is in motion, all of the items will travel in the same direction; if the first ones kill a monster, the others can still continue beyond that spot. - 7.2.2. Weapon proficiency + 7.4.2. Weapon proficiency You will have varying degrees of skill in the weapons available. Weapon proficiency, or weapon skills, affect how well you can use par- @@ -3094,24 +3145,12 @@ the next skill level (unless you've already reached the limit for this skill). Once such training reaches the threshold for that next level, you'll be told that you feel more confident in your skills. At that - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 48 - - - point you can use "#enhance" to increase one or more skills. Such skills are not increased automatically because there is a limit to your total overall skills, so you need to actively choose which skills to enhance and which to ignore. - 7.2.3. Two-Weapon combat + 7.4.3. Two-Weapon combat Some characters can use two weapons at once. Setting things up to do so can seem cumbersome but becomes second nature with use. To @@ -3121,6 +3160,18 @@ with is considered primary and the other one is considered secondary. The most noticeable difference is after you stop--or before you begin, for that matter--wielding two weapons at once. The primary is your + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 49 + + + wielded weapon and the secondary is just an item in your inventory that's been designated as alternate weapon.) @@ -3145,7 +3196,7 @@ ons or having one of them be stolen or destroyed will also make you revert to single-weapon combat. - 7.3. Armor (`[') + 7.5. Armor (`[') Lots of unfriendly things lurk about; you need armor to protect yourself from their blows. Some types of armor offer better protec- @@ -3159,19 +3210,6 @@ Dragon scale mail 1 Plate mail, Crystal plate mail 3 Bronze plate mail, Splint mail, - - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 49 - - - Banded mail, Dwarvish mithril-coat 4 Chain mail, Elven mithril-coat 5 Scale mail, Orcish chain mail 6 @@ -3188,6 +3226,18 @@ also be enchanted. Shirts are an exception; they don't provide any protection unless enchanted. Some cloaks also don't improve AC when unenchanted but all cloaks offer some protection against rust or cor- + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 50 + + + rosion to suits worn under them and against some monster touch attacks. @@ -3214,7 +3264,7 @@ can be used for armor, but pieces of armor won't be shown as likely candidates in a prompt for choosing what to put on or remove. - 7.4. Food (`%') + 7.6. Food (`%') Food is necessary to survive. If you go too long without eating you will faint, and eventually die of starvation. Some types of food @@ -3227,17 +3277,6 @@ special powers when you eat them. A good rule of thumb is "you are what you eat." - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 50 - - - Some character roles and some monsters are vegetarian. Vegetar- ian monsters will typically never eat animal corpses, while vegetarian players can, but with some rather unpleasant side-effects. @@ -3247,13 +3286,24 @@ The command to eat food is `e'. - 7.5. Scrolls (`?') + 7.7. Scrolls (`?') Scrolls are labeled with various titles, probably chosen by ancient wizards for their amusement value (for example "READ ME," or "THANX MAUD" backwards). Scrolls disappear after you read them (except for blank ones, without magic spells on them). + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 51 + + + One of the most useful of these is the scroll of identify, which can be used to determine what another object is, whether it is cursed or blessed, and how many uses it has left. Some objects of subtle @@ -3261,8 +3311,8 @@ A scroll whose label is known can be read even when the hero is blind. If a scroll has been discovered, it will be listed in inven- - tory by type rather than by label, but the label is known in that - situtaion even though it isn't shown. + tory by type rather than by label, but the label is known in that sit- + uation even though it isn't shown. Many scrolls produce a different effect from usual if they are blessed or cursed, or read while the hero is confused. @@ -3281,7 +3331,7 @@ The command to read a scroll is `r'. - 7.6. Potions (`!') + 7.8. Potions (`!') Potions are distinguished by the color of the liquid inside the flask. They disappear after you quaff them. @@ -3292,21 +3342,9 @@ at them. It is also sometimes very useful to dip ("#dip") an object into a potion. - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 51 - - - The command to drink a potion is `q' (quaff). - 7.7. Wands (`/') + 7.9. Wands (`/') Wands usually have multiple magical charges. Some types of wands require a direction in which to zap them. You can also zap them at @@ -3320,6 +3358,18 @@ ally, however, it may be possible to squeeze the last few mana points from an otherwise spent wand, destroying it in the process. A wand may be recharged by using suitable magic, but doing so runs the risk + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 52 + + + of causing it to explode. The chance for such an explosion starts out very small and increases each time the wand is recharged. @@ -3337,7 +3387,7 @@ The command to use a wand is `z' (zap). To break one, use the `a' (apply) command. - 7.8. Rings (`=') + 7.10. Rings (`=') Rings are very useful items, since they are relatively permanent magic, unlike the usually fleeting effects of potions, scrolls, and @@ -3358,19 +3408,7 @@ The commands to use rings are `P' (put on) and `R' (remove). `A', `W', and `T' can also be used; see Amulets. - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 52 - - - - 7.9. Spellbooks (`+') + 7.11. Spellbooks (`+') Spellbooks are tomes of mighty magic. When studied with the `r' (read) command, they transfer to the reader the knowledge of a spell @@ -3386,6 +3424,18 @@ Casting a spell calls forth magical energies and focuses them with your naked mind. Some of the magical energy released comes from + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 53 + + + within you. Casting temporarily drains your magical power, which will slowly be recovered, and causes you to need additional food. Casting of spells also requires practice. With practice, your skill in each @@ -3417,25 +3467,13 @@ of how strongly it is remembered. The `Z' (cast) command casts a spell. - 7.10. Tools (`(') + 7.12. Tools (`(') Tools are miscellaneous objects with various purposes. Some tools have a limited number of uses, akin to wand charges. For exam- ple, lamps burn out after a while. Other tools are containers, which objects can be placed into or taken out of. - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 53 - - - Some tools (such as a blindfold) can be worn and can be put on and removed like other accessories (rings, amulets); see Amulets. Other tools (such as pick-axe) can be wielded as weapons in addition @@ -3448,10 +3486,22 @@ The command to use a tool is `a' (apply). - 7.10.1. Containers + 7.12.1. Containers You may encounter bags, boxes, and chests in your travels. A tool of this sort can be opened with the "#loot" extended command when + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 54 + + + you are standing on top of it (that is, on the same floor spot), or with the `a' (apply) command when you are carrying it. However, chests are often locked, and are in any case unwieldy objects. You @@ -3487,22 +3537,7 @@ tents into another container. (As of this writing, the other con- tainer must be carried rather than on the floor.) - - - - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 54 - - - - 7.11. Amulets (`"') + 7.13. Amulets (`"') Amulets are very similar to rings, and often more powerful. Like rings, amulets have various magical properties, some beneficial, some @@ -3519,7 +3554,21 @@ and eyewear), but accessories won't be shown as likely candidates in a prompt for choosing what to wear or take off. - 7.12. Gems (`*') + + + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 55 + + + + 7.14. Gems (`*') Some gems are valuable, and can be sold for a lot of gold. They are also a far more efficient way of carrying your riches. Valuable @@ -3530,7 +3579,7 @@ (if you have a sling). In the most desperate of cases, you can still throw them by hand. - 7.13. Large rocks (``') + 7.15. Large rocks (``') Statues and boulders are not particularly useful, and are gener- ally heavy. It is rumored that some statues are not what they seem. @@ -3556,19 +3605,7 @@ shown as ``' but by the letter representing the monster they depict instead. - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 55 - - - - 7.14. Gold (`$') + 7.16. Gold (`$') Gold adds to your score, and you can buy things in shops with it. There are a number of monsters in the dungeon that may be influenced @@ -3582,7 +3619,22 @@ matters when you're using an object selection prompt that can filter by "BUCX" state. - 7.15. Persistence of Objects + + + + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 56 + + + + 7.17. Persistence of Objects Normally, if you have seen an object at a particular map location and move to another location where you can't directly see that object @@ -3623,17 +3675,6 @@ your god for help with starvation does not violate any food challenges either. - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 56 - - - A strict vegan diet is one which avoids any food derived from animals. The primary source of nutrition is fruits and vegetables. The corpses and tins of blobs (`b'), jellies (`j'), and fungi (`F') @@ -3647,6 +3688,18 @@ Vegetarians do not eat animals; however, they are less selective about eating animal byproducts than vegans. In addition to the vegan items listed above, they may eat any kind of pudding (`P') other than + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 57 + + + the black puddings, eggs and food made from eggs (fortune cookies and pancakes), food made with milk (cream pies and candy bars), and lumps of royal jelly. Monks are expected to observe a vegetarian diet. @@ -3683,80 +3736,27 @@ other religious figure; a true atheist would hear the words but attach no special meaning to them. - Most players fight with a wielded weapon (or tool intended to be - wielded as a weapon). Another challenge is to win the game without - using such a wielded weapon. You are still permitted to throw, fire, - and kick weapons; use a wand, spell, or other type of item; or fight + A pauper starts the game with no possessions, no spells, and no + weapon or spell skills (and if playing as a knight, your pony will not + have a saddle). Can only be initiated by starting a new game with + OPTIONS=pauper set in your run-time configurtion file or NETHACKOP- + TIONS environment variable. Once the game is underway, you can + acquire and use items, spells, and skills in the usual way. + + Most players fight with a wielded weapon (or tool intended to be + wielded as a weapon). Another challenge is to win the game without + using such a wielded weapon. You are still permitted to throw, fire, + and kick weapons; use a wand, spell, or other type of item; or fight with your hands and feet. - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 57 - - - - In NetHack, a pacifist refuses to cause the death of any other - monster (i.e. if you would get experience for the death). This is a - particularly difficult challenge, although it is still possible to + In NetHack, a pacifist refuses to cause the death of any other + monster (i.e. if you would get experience for the death). This is a + particularly difficult challenge, although it is still possible to gain experience by other means. - An illiterate character does not read or write. This includes - reading a scroll, spellbook, fortune cookie message, or t-shirt; writ- - ing a scroll; or making an engraving of anything other than a single - "X" (the traditional signature of an illiterate person). Reading an - engraving, or any item that is absolutely necessary to win the game, - is not counted against this conduct. The identity of scrolls and - spellbooks (and knowledge of spells) in your starting inventory is - assumed to be learned from your teachers prior to the start of the - game and isn't counted. - - There is a side-branch to the main dungeon called "Sokoban," - briefly described in the earlier section about Traps. As mentioned - there, the goal is to push boulders into pits and/or holes to plug - those in order to both get the boulders out of the way and be able to - go past the traps. There are some special "rules" that are active - when in that branch of the dungeon. Some rules can't be bypassed, - such as being unable to push a boulder diagonally. Other rules can, - such as not smashing boulders with magic or tools, but doing so causes - you to receive a luck penalty. No message about that is given at the - time, but it is tracked as a conduct. The #conduct command and end of - game disclosure will report whether you have abided by the special - rules of Sokoban, and if not, how many times you violated them, pro- - viding you with a way to discover which actions incur bad luck so that - you can be better informed about whether or not to avoid repeating - those actions in the future. (Note: the Sokoban conduct will only be - displayed if you have entered the Sokoban branch of the dungeon during - the current game. Once that has happened, it becomes part of dis- - closed conduct even if you haven't done anything interesting there. - Ending the game with "never broke the Sokoban rules" conduct is most - meaningful if you also manage to perform the "obtained the Sokoban - prize" achievement (see Achievements below).) - - There are several other challenges tracked by the game. It is - possible to eliminate one or more species of monsters by genocide; - playing without this feature is considered a challenge. When the game - offers you an opportunity to genocide monsters, you may respond with - the monster type "none" if you want to decline. You can change the - form of an item into another item of the same type ("polypiling") or - the form of your own body into another creature ("polyself") by wand, - spell, or potion of polymorph; avoiding these effects are each consid- - ered challenges. Polymorphing monsters, including pets, does not - break either of these challenges. Finally, you may sometimes receive - wishes; a game without an attempt to wish for any items is a chal- - lenge, as is a game without wishing for an artifact (even if the arti- - fact immediately disappears). When the game offers you an opportunity - to make a wish for an item, you may choose "nothing" if you want to - decline. - - - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -3766,14 +3766,74 @@ + An illiterate character does not read or write. This includes + reading a scroll, spellbook, fortune cookie message, or t-shirt; writ- + ing a scroll; or making an engraving of anything other than a single + "X" (the traditional signature of an illiterate person). Reading an + engraving, or any item that is absolutely necessary to win the game, + is not counted against this conduct. The identity of scrolls and + spellbooks (and knowledge of spells) in your starting inventory is + assumed to be learned from your teachers prior to the start of the + game and isn't counted. + + There is a side-branch to the main dungeon called "Sokoban," + briefly described in the earlier section about Traps. As mentioned + there, the goal is to push boulders into pits and/or holes to plug + those in order to both get the boulders out of the way and be able to + go past the traps. There are some special "rules" that are active + when in that branch of the dungeon. Some rules can't be bypassed, + such as being unable to push a boulder diagonally. Other rules can, + such as not smashing boulders with magic or tools, but doing so causes + you to receive a luck penalty. No message about that is given at the + time, but it is tracked as a conduct. The #conduct command and end of + game disclosure will report whether you have abided by the special + rules of Sokoban, and if not, how many times you violated them, pro- + viding you with a way to discover which actions incur bad luck so that + you can be better informed about whether or not to avoid repeating + those actions in the future. (Note: the Sokoban conduct will only be + displayed if you have entered the Sokoban branch of the dungeon during + the current game. Once that has happened, it becomes part of dis- + closed conduct even if you haven't done anything interesting there. + Ending the game with "never broke the Sokoban rules" conduct is most + meaningful if you also manage to perform the "obtained the Sokoban + prize" achievement (see Achievements below).) + + There are several other challenges tracked by the game. It is + possible to eliminate one or more species of monsters by genocide; + playing without this feature is considered a challenge. When the game + offers you an opportunity to genocide monsters, you may respond with + the monster type "none" if you want to decline. You can change the + form of an item into another item of the same type ("polypiling") or + the form of your own body into another creature ("polyself") by wand, + spell, or potion of polymorph; avoiding these effects are each consid- + ered challenges. Polymorphing monsters, including pets, does not + break either of these challenges. Finally, you may sometimes receive + wishes; a game without an attempt to wish for any items is a chal- + lenge, as is a game without wishing for an artifact (even if the arti- + fact immediately disappears). When the game offers you an opportunity + to make a wish for an item, you may choose "nothing" if you want to + decline. + 8.1. Achievements - End of game disclosure will also display various achievements - representing progress toward ultimate ascension, if any have been - attained. They aren't directly related to conduct but are grouped - with it because they fall into the same category of "bragging rights" - and to limit the number of questions during disclosure. Listed here - roughly in order of difficulty and not necessarily in the order in + End of game disclosure will also display various achievements + representing progress toward ultimate ascension, if any have been + attained. They aren't directly related to conduct but are grouped + with it because they fall into the same category of "bragging rights" + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 59 + + + + and to limit the number of questions during disclosure. Listed here + roughly in order of difficulty and not necessarily in the order in which you might accomplish them. Rank - Attained rank title Rank. @@ -3785,12 +3845,12 @@ Novel - Read a passage from a Discworld Novel. Sokoban - Entered Sokoban. Big Room - Entered the Big Room. - Soko-Prize - Explored to the top of Sokoban and found a + Soko-Prize - Explored to the top of Sokoban and found a special item there. - Mines' End - Explored to the bottom of the Gnomish Mines + Mines' End - Explored to the bottom of the Gnomish Mines and found a special item there. Medusa - Defeated Medusa. - Tune - Discovered the tune that can be used to open + Tune - Discovered the tune that can be used to open and close the drawbridge on the Castle level. Bell - Acquired the Bell of Opening. Gehennom - Entered Gehennom. @@ -3810,85 +3870,25 @@ Notes: - Achievements are recorded and subsequently reported in the order - in which they happen during your current game rather than the order + Achievements are recorded and subsequently reported in the order + in which they happen during your current game rather than the order listed here. - There are nine titles for each role, bestowed at experi- - ence levels 1, 3, 6, 10, 14, 18, 22, 26, and 30. The one for experi- - ence level 1 is not recorded as an achievement. Losing enough levels + There are nine titles for each role, bestowed at experi- + ence levels 1, 3, 6, 10, 14, 18, 22, 26, and 30. The one for experi- + ence level 1 is not recorded as an achievement. Losing enough levels to revert to lower rank(s) does not discard the corresponding achieve- ment(s). - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 59 - - - - There's no guaranteed Novel so the achievement to read one might - not always be attainable (except perhaps by wishing). Similarly, the - Big Room level is not always present. Unlike with the Novel, there's + There's no guaranteed Novel so the achievement to read one might + not always be attainable (except perhaps by wishing). Similarly, the + Big Room level is not always present. Unlike with the Novel, there's no way to wish for this opportunity. - The "special items" hidden in Mines' End and Sokoban are not - unique but are considered to be prizes or rewards for exploring those - levels since doing so is not necessary to complete the game. Finding - other instances of the same objects doesn't record the corresponding - achievement. - - The Medusa achievement is recorded if she dies for any reason, - even if you are not directly responsible, and only if she dies. - - The 5-note tune can be learned via trial and error with a musical - instrument played closely enough--but not too close!--to the Castle - level's drawbridge or can be given to you via prayer boon. - - Blind, Deaf, Nudist, and Pauper are also conducts, and they can - only be enabled by setting the correspondingly named option in - NETHACKOPTIONS or run-time configuration file prior to game start. In - the case of Blind and Deaf, the option also enforces the conduct. - They aren't really significant accomplishments unless/until you make - substantial progress into the dungeon. - - 9. Options - - Due to variations in personal tastes and conceptions of how - NetHack should do things, there are options you can set to change how - NetHack behaves. - - 9.1. Setting the options - - Options may be set in a number of ways. Within the game, the `O' - command allows you to view all options and change most of them. You - can also set options automatically by placing them in a configuration - file, or in the NETHACKOPTIONS environment variable. Some versions of - NetHack also have front-end programs that allow you to set options - before starting the game or a global configuration for system adminis- - trators. - - 9.2. Using a configuration file - - The default name of the configuration file varies on different - operating systems. - - On UNIX, Linux, and macOS it is ".nethackrc" in the user's home - directory. The file may not exist, but it is a normal ASCII text file - and can be created with any text editor. - - On Windows, the name is ".nethackrc" located in the folder - "%USERPROFILE%\NetHack\". The file may not exist, but it is a normal - ASCII text file can can be created with any text editor. After run- - ning NetHack for the first time, you should find a default template - NetHack 3.7.0 May 1, 2025 + + NetHack 3.7.0 March 25, 2026 @@ -3898,63 +3898,63 @@ - for the configuration file named ".nethackrc.template" in - "%USERPROFILE%\NetHack\". If you have not created the configuration + The "special items" hidden in Mines' End and Sokoban are not + unique but are considered to be prizes or rewards for exploring those + levels since doing so is not necessary to complete the game. Finding + other instances of the same objects doesn't record the corresponding + achievement. + + The Medusa achievement is recorded if she dies for any reason, + even if you are not directly responsible, and only if she dies. + + The 5-note tune can be learned via trial and error with a musical + instrument played closely enough--but not too close!--to the Castle + level's drawbridge or can be given to you via prayer boon. + + Blind, Deaf, Nudist, and Pauper are also conducts, and they can + only be enabled by setting the correspondingly named option in + NETHACKOPTIONS or run-time configuration file prior to game start. In + the case of Blind and Deaf, the option also enforces the conduct. + They aren't really significant accomplishments unless/until you make + substantial progress into the dungeon. + + 9. Options + + Due to variations in personal tastes and conceptions of how + NetHack should do things, there are options you can set to change how + NetHack behaves. + + 9.1. Setting the options + + Options may be set in a number of ways. Within the game, the `O' + command allows you to view all options and change most of them. You + can also set options automatically by placing them in a configuration + file, or in the NETHACKOPTIONS environment variable. Some versions of + NetHack also have front-end programs that allow you to set options + before starting the game or a global configuration for system adminis- + trators. + + 9.2. Using a configuration file + + The default name of the configuration file varies on different + operating systems. + + On UNIX, Linux, and macOS it is ".nethackrc" in the user's home + directory. The file may not exist, but it is a normal ASCII text file + and can be created with any text editor. + + On Windows, the name is ".nethackrc" located in the folder + "%USERPROFILE%\NetHack\". The file may not exist, but it is a normal + ASCII text file can can be created with any text editor. After run- + ning NetHack for the first time, you should find a default template + for the configuration file named ".nethackrc.template" in + "%USERPROFILE%\NetHack\". If you have not created the configuration file, NetHack will create one for you using the default template file. - On MS-DOS, it is "defaults.nh" in the same folder as nethack.exe. - - Any line in the configuration file starting with `#' is treated - as a comment and ignored. Empty lines are ignored. - - Any line beginning with `[' and ending in `]' is a section marker - (the closing `]' can be followed by whitespace and then an arbitrary - comment beginning with `#'). The text between the square brackets is - the section name. Section markers are only valid after a CHOOSE - directive and their names are case insensitive. Lines after a section - marker belong to that section up until another section starts or a - marker without a name is encountered or the file ends. Lines within - sections are ignored unless a CHOOSE directive has selected that sec- - tion. - - You can use different configuration directives in the file, some - of which can be used multiple times. In general, the directives are - written in capital letters, followed by an equals sign, followed by - settings particular to that directive. - - Here is a list of allowed directives: - - OPTIONS - There are two types of options, boolean and compound options. Bool- - ean options toggle a setting on or off, while compound options take - more diverse values. Prefix a boolean option with "no" or `!' to - turn it off. For compound options, the option name and value are - separated by a colon. Some options are persistent, and apply only - to new games. You can specify multiple OPTIONS directives, and mul- - tiple options separated by commas in a single OPTIONS directive. - (Comma separated options are processed from right to left.) - - Example: - - OPTIONS=dogname:Fido - OPTIONS=!legacy,autopickup,pickup_types:$"=/!?+ - - HACKDIR - Default location of files NetHack needs. On Windows HACKDIR defaults - to the location of the NetHack.exe or NetHackw.exe file so setting - HACKDIR to override that is not usually necessary or recommended. - - LEVELDIR - The location that in-progress level files are stored. Defaults to - HACKDIR, must be writable. - - SAVEDIR - The location where saved games are kept. Defaults to HACKDIR, must - be writable. - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 @@ -3964,22 +3964,84 @@ + On MS-DOS, it is "defaults.nh" in the same folder as nethack.exe. + + Any line in the configuration file starting with `#' is treated + as a comment and ignored. Empty lines are ignored. + + Any line beginning with `[' and ending in `]' is a section marker + (the closing `]' can be followed by whitespace and then an arbitrary + comment beginning with `#'). The text between the square brackets is + the section name. Section markers are only valid after a CHOOSE + directive and their names are case-insensitive. Lines after a section + marker belong to that section up until another section starts or a + marker without a name is encountered or the file ends. Lines within + sections are ignored unless a CHOOSE directive has selected that sec- + tion. + + You can use different configuration directives in the file, some + of which can be used multiple times. In general, the directives are + written in capital letters, followed by an equals sign, followed by + settings particular to that directive. + + Here is a list of allowed directives: + + OPTIONS + There are two types of options, boolean and compound options. Bool- + ean options toggle a setting on or off, while compound options take + more diverse values. Prefix a boolean option with "no" or `!' to + turn it off. For compound options, the option name and value are + separated by a colon. Some options are persistent, and apply only + to new games. You can specify multiple OPTIONS directives, and mul- + tiple options separated by commas in a single OPTIONS directive. + (Comma separated options are processed from right to left.) + + Example: + + OPTIONS=dogname:Fido + OPTIONS=!legacy,autopickup,pickup_types:$"=/!?+ + + HACKDIR + Default location of files NetHack needs. On Windows HACKDIR defaults + to the location of the NetHack.exe or NetHackw.exe file so setting + HACKDIR to override that is not usually necessary or recommended. + + LEVELDIR + The location that in-progress level files are stored. Defaults to + HACKDIR, must be writable. + + SAVEDIR + The location where saved games are kept. Defaults to HACKDIR, must + be writable. + BONESDIR The location that bones files are kept. Defaults to HACKDIR, must be writable. + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 62 + + + LOCKDIR The location that file synchronization locks are stored. Defaults to HACKDIR, must be writable. TROUBLEDIR - The location that a record of game aborts and self-diagnosed game + The location that a record of game aborts and self-diagnosed game problems is kept. Defaults to HACKDIR, must be writable. AUTOCOMPLETE - Enable or disable an extended command autocompletion. Autocomple- + Enable or disable an extended command autocompletion. Autocomple- tion has no effect for the X11 windowport. You can specify multiple - autocompletions. To enable autocompletion, list the extended com- + autocompletions. To enable autocompletion, list the extended com- mand. Prefix the command with "!" to disable the autocompletion for that command. @@ -3988,13 +4050,13 @@ AUTOCOMPLETE=zap,!annotate AUTOPICKUP_EXCEPTION - Set exceptions to the pickup_types option. See the "Configuring + Set exceptions to the pickup_types option. See the "Configuring Autopickup Exceptions" section. BINDINGS - Change the key bindings of some special keys, menu accelerators, + Change the key bindings of some special keys, menu accelerators, extended commands, or mouse buttons. You can specify multiple bind- - ings. Format is key followed by the command, separated by a colon. + ings. Format is key followed by the command, separated by a colon. See the "Changing Key Bindings" section for more information. Example: @@ -4017,27 +4079,27 @@ OPTIONS=!rest_on_space If [] is present, the preceding section is closed and no new section - begins; whatever follows will be common to all sections. Otherwise - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 62 - - - + begins; whatever follows will be common to all sections. Otherwise the last section extends to the end of the options file. MENUCOLOR - Highlight menu lines with different colors. See the "Configuring + Highlight menu lines with different colors. See the "Configuring + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 63 + + + Menu Colors" section. MSGTYPE - Change the way messages are shown in the top status line. See the + Change the way messages are shown in the top status line. See the "Configuring Message Types" section. ROGUESYMBOLS @@ -4047,24 +4109,25 @@ Define a sound mapping. See the "Configuring User Sounds" section. SOUNDDIR - Define the directory that contains the sound files. See the "Con- + Define the directory that contains the sound files. See the "Con- figuring User Sounds" section. SYMBOLS - Override one or more symbols in the symbol set used for all dungeon - levels except for the special rogue level. See the "Modifying + Override one or more symbols in the symbol set used for all dungeon + levels except for the special rogue level. See the "Modifying NetHack Symbols" section. Example: # replace small punctuation (tick marks) with digits - SYMBOLS=S_boulder:0,S_golem:7 + SYMBOLS=S_golem:7 WIZKIT Debug mode only: extra items to add to initial inventory. Value is - the name of a text file containing a list of item names, one per - line, up to a maximum of 128 lines. Each line is processed by the - function that handles wishing. + the name of a text file containing a list of item names, one per + line, up to a maximum of 128 lines. Each line is processed by the + function that handles wishing. Entries are added to the wish his- + tory; see the wizwish-command. Example: @@ -4086,13 +4149,16 @@ - NetHack 3.7.0 May 1, 2025 + + + + NetHack 3.7.0 March 25, 2026 - NetHack Guidebook 63 + NetHack Guidebook 64 @@ -4107,7 +4173,8 @@ OPTIONS=lit_corridor # Show lit corridors differently OPTIONS=hilite_pet,hilite_pile # Replace small punctuation (tick marks) with digits - SYMBOLS=S_boulder:0,S_golem:7 + OPTIONS=boulder:0 + SYMBOLS=S_golem:7 # # No startup splash screen. Windows GUI only. OPTIONS=!splash_screen @@ -4149,19 +4216,20 @@ be set to the full name of a configuration file you want to use. If that full name doesn't start with a slash, precede it with `@' (at- sign) to let NetHack know that the rest is intended as a file name. + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 65 + + + If it does start with `/', the at-sign is optional. - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 64 - - - 9.4. Customization options Here are explanations of what the various options do. Character @@ -4214,20 +4282,20 @@ autoquiver This option controls what happens when you attempt the `f' (fire) + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 66 + + + command when nothing is quivered or readied (default false). When true, the computer will fill your quiver or quiver sack or make - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 65 - - - ready some suitable weapon. Note that it will not take into account the blessed/cursed status, enchantment, damage, or quality of the weapon; you are free to manually fill your quiver or quiver sack or @@ -4280,20 +4348,20 @@ catname Name your starting cat (for example "catname:Morris"). Cannot be + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 67 + + + set with the `O' command. - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 66 - - - character Synonym for "role" to pick the type of your character (for example "character:Monk"). See role for more details. @@ -4346,24 +4414,25 @@ The listings of vanquished monsters and of genocided types can be sorted, so there are two additional choices for `v' and `g': + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 68 + + + ? - prompt you and default to ask on the prompt; # - disclose it without prompting, ask for sort order. - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 67 - - - - Asking refers to picking one of the orderings from a menu. The - `+' disclose without prompting choice, or being prompted and - answering `y' rather than `a', will default to showing monsters - in the order specified by the sortvanquished option. + Asking refers to picking one of the orderings from a menu. The `+' + disclose without prompting choice, or being prompted and answering + `y' rather than `a', will default to showing monsters in the order + specified by the sortvanquished option. Omitted categories are implicitly added with `n' prefix. Specified categories with omitted prefix implicitly use `+' prefix. Order of @@ -4411,21 +4480,22 @@ on). If this is off, dropping an object shifts all the remaining inventory letters. Persistent. + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 69 + + + force_invmenu Commands asking for an inventory item show a menu instead of a text query with possible menu letters. Default is off. - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 68 - - - fruit Name a fruit after something you enjoy eating (for example "fruit:mango") (default "slime mold"). Basically a nostalgic whimsy @@ -4475,23 +4545,24 @@ highlight pets and setting it will turn the hilite_pet option on or off as warranted. + + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 70 + + + hilite_pile Visually distinguish piles of objects from individual objects (default off). The behavior of this option depends on the type of windowing you use. In text windowing, text highlighting or inverse video is often used; with tiles, generally displays a small plus- - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 69 - - - symbol beside the object on the top of the pile. hitpointbar @@ -4541,23 +4612,23 @@ lootabc When using a menu to interact with a container, use the old `a', `b', and `c' keyboard shortcuts rather than the mnemonics `o', `i', + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 71 + + + and `b' (default off). Persistent. mail Enable mail delivery during the game (default on). Persistent. - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 70 - - - male An obsolete synonym for "gender:male". Cannot be set with the `O' command. @@ -4607,23 +4678,24 @@ menu_first_page Key to jump to the first page in a menu. Default `^'. + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 72 + + + menu_headings Controls how the headings in a menu are highlighted. Takes a text attribute, or text color and attribute separated by ampersand. For allowed attributes and colors, see "Configuring Menu Colors". Not all ports can actually display all types. - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 71 - - - menu_invert_all Key to invert all items in a menu. Default `@'. @@ -4672,24 +4744,24 @@ Key to search for some text and toggle selection state of matching menu items. Default `:'. + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 73 + + + menu_select_all Key to select all items in a menu. Default `.'. menu_select_page Key to select all items on this page of a menu. Default `,'. - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 72 - - - menu_shift_left Key to scroll a menu--one which has been scrolled right--back to the left. Implemented for perm_invent only by curses and X11. Default @@ -4738,24 +4810,24 @@ For backward compatibility, no value needs to be specified (which defaults to "full"), or it can be negated (which defaults to "sin- + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 74 + + + gle"). name Set your character's name (defaults to your user name). You can also set your character's role by appending a dash and one or more letters of the role (that is, by suffixing one of -A -B -C -H -K -M - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 73 - - - -P -Ra -Ro -S -T -V -W). If -@ is used for the role, then a random one will be automatically chosen. @@ -4804,24 +4876,24 @@ paranoid_confirmation A space separated list of specific situations where alternate + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 75 + + + prompting is desired. The default is "paranoid_confirmation:pray swim trap". Confirm - for any prompts which are set to require "yes" rather than `y', also require "no" to reject instead of accepting any non-yes response as no; changes pray and - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 74 - - - AutoAll to require "yes" or `no' too; quit - require "yes" rather than `y' to confirm quitting the game or switching into non-scoring explore mode; @@ -4870,6 +4942,18 @@ new entries and remove some old ones, you can use multiple para- noid_confirmation option settings, or you can use the `+' form and list entries to be added by their name and entries to be removed by + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 76 + + + `!' and name. The positive (no `!') and negative (with `!') entries can be intermixed. @@ -4877,16 +4961,9 @@ Start the character with no possessions (default false). Persis- tent. - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 75 - - + Also start with no spells or skills, which are tied to starting + equipment. Does not inhibit acquiring and using items, spells, and + skills once play has started. perm_invent If true, always display your current inventory in a window (default @@ -4931,6 +5008,18 @@ pickup_burden When you pick up an item that would exceed this encumbrance level (Unencumbered, Burdened, streSsed, straiNed, overTaxed, or over- + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 77 + + + Loaded), you will be asked if you want to continue. (Default `S'). Persistent. @@ -4942,18 +5031,6 @@ pickup_thrown If this option is on and autopickup is also on, try to pick up - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 76 - - - things that you threw, even if they aren't in pickup_types or match an autopickup exception. Default is on. Persistent. @@ -4991,6 +5068,27 @@ it when not allowed or not possible results in explore mode instead. Default is normal play. + price_quotes + Whenever the game mentions the name of an object you haven't identi- + fied yet, it also mentions the range of buy and sell prices you have + seen for that item (to help narrow down what it could be). The + price shown is the unit price for one item (even when you are look- + ing at a stack of multiple items). Many players may want to turn + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 78 + + + + this on while identifying objects, and then turn it back off again + for general play. Default is off. + pushweapon Using the `w' (wield) command when already wielding something pushes the old item into your alternate weapon slot (default off). Like- @@ -5008,18 +5106,6 @@ ance spell where pausing to examine revealed objects or monsters is less intrusive. Default is off. Persistent. - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 77 - - - race Selects your race (for example, race:human). Choices are human, dwarf, elf, gnome, and orc but most roles restrict which of the non- @@ -5030,6 +5116,17 @@ prompted unless role forces a choice for race. Cannot be set with the `O' command. Persistent. + reroll + Allows rerolling your character's starting inventory and attributes + (default false). Persistent. + + Note that rerolling your character is not a recommended way to play + if aiming merely to win (a lucky start has a much smaller influence + on whether or not you win the game than your actions later in the + game). This option exists partly as an acknowledgement that some + players will insist on doing so anyway, and partly because rerolling + may be necessary for certain types of challenge games. + rest_on_space Make the space bar a synonym for the `.' (#wait) command (default off). Persistent. @@ -5043,6 +5140,18 @@ randomly. Use a space-separated list of roles and either negate each one or negate the option itself instead. Negation is accom- plished in the same manner as with boolean options, by prefixing the + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 79 + + + option or its value(s) with `!' or "no". Examples: @@ -5074,18 +5183,6 @@ teleport - update the map after movement has finished; run - update the map after every seven or so steps; walk - update the map after each step; - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 78 - - - crawl - like walk, but pause briefly after each step. This option only affects the game's screen display, not the actual @@ -5109,6 +5206,18 @@ scores Control what parts of the score list you are shown at the end (for example "scores:5 top scores/4 around my score/own scores"). Only + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 80 + + + the first letter of each category (`t', `a', or `o') is necessary. Persistent. @@ -5136,22 +5245,10 @@ Potentially useful if you switch between different versions or vari- ants, or you are making screenshots or streaming video. Using the statuslines:3 option is recommended so that there will be more room - available for status information, unless you're using nethack's Qt + available for status information, unless you're using NetHack's Qt interface or your terminal emulator window displays fewer than 25 lines. Persistent. - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 79 - - - silent Suppress terminal beeps (default on). Persistent. @@ -5174,6 +5271,19 @@ Can be interactively set via the `O' command or via using the `m' prefix before the `\' or ``' command. + + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 81 + + + sortloot Controls the sorting behavior of the pickup lists for inventory and #loot commands and some others. Persistent. @@ -5206,18 +5316,6 @@ index; z - order by count, low to high; ties broken by internal index. - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 80 - - - Can be interactively set via the `m O' command or via using the `m' prefix before either the #vanquished command or the #genocided com- mand. @@ -5241,6 +5339,17 @@ If negated or set to zero, disables status hiliting. See "Configur- ing Status Hilites" for further information. + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 82 + + + status_updates Allow updates to the status lines at the bottom of the screen (default true). @@ -5272,18 +5381,6 @@ tombstone Draw a tombstone graphic upon your death (default on). Persistent. - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 81 - - - toptenwin Put the ending display in a NetHack window instead of on stdout (default off). Setting this option makes the score list visible @@ -5307,6 +5404,18 @@ whatis_coord When using the `/' or `;' commands to look around on the map with autodescribe on, display coordinates after the description. Also + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 83 + + + works in other situations where you are asked to pick a location. The possible settings are: @@ -5337,19 +5446,6 @@ Filtering can also be changed when getting a location with the "get- pos.filter" key. - - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 82 - - - whatis_menu When getting a location on the map, and using a key to cycle through next and previous targets, use a menu instead to pick a target. @@ -5373,6 +5469,19 @@ OPTIONS line in a configuration file, that would be the rightmost option in the list. + + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 84 + + + wizweight Augment object descriptions with their objects' weight (default off). Debug mode only. @@ -5404,18 +5513,6 @@ Where to align or place the status window (top, bottom, left, or right). - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 83 - - - ascii_map If NetHack can, it should display the map using simple characters (letters and punctuation) rather than tiles graphics. In some @@ -5439,6 +5536,18 @@ font_menu If NetHack can, it should use a font by the chosen name for menu + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 85 + + + windows. font_message @@ -5470,18 +5579,6 @@ fullscreen If NetHack can, it should try to display on the entire screen rather - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 84 - - - than in a window. guicolor @@ -5505,6 +5602,18 @@ preload_tiles If NetHack can, it should preload tiles into memory. For example, in the protected mode MS-DOS version, control whether tiles get pre- + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 86 + + + loaded into RAM at the start of the game. Doing so enhances perfor- mance of the tile graphics, but uses more memory. (default on). Cannot be set with the `O' command. @@ -5536,18 +5645,6 @@ When set to 3, the tty interface moves some fields around and mainly shows status conditions on their own line. A display capable of - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 85 - - - showing at least 25 lines is recommended. The value can be toggled back and forth during the game with the `O' command. @@ -5572,6 +5669,17 @@ will settle for smaller sizes if they are too big. Default is the current window size. + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 87 + + + tile_file Specify the name of an alternative tile file to override the default. @@ -5603,17 +5711,6 @@ If NetHack can, it should display this number of messages at a time in the message window. - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 86 - - - windowborders Whether to draw boxes around the map, status area, message area, and persistent inventory window if enabled. Curses interface only. @@ -5637,6 +5734,18 @@ tional lines of inventory plus widen each inventory line by two col- umns. + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 88 + + + windowcolors If NetHack can, it should display all windows of a particular style with the specified foreground and background colors. Windows GUI @@ -5667,19 +5776,6 @@ OPTION=crash_email:email_address - - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 87 - - - OPTION=crash_name:your_name These options are used only to save you some typing on the crash report and #bugreport forms. @@ -5704,6 +5800,18 @@ NetHack to convert a two character sequence beginning with ESC into a meta-shifted version of the second character (default off). + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 89 + + + This conversion is only done for commands, not for other input prompts. Note that typing one or more digits as a count prefix prior to a command--preceded by n if the number_pad option is set-- @@ -5734,18 +5842,6 @@ video Set the video mode used (PC NetHack only). Values are "autodetect", - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 88 - - - "default", "vga", or "vesa". Setting "vesa" will cause the game to display tiles, using the full capability of the VGA hardware. Set- ting "vga" will cause the game to display tiles, fixed at 640x480 in @@ -5770,6 +5866,18 @@ videoshades Set the intensity level of the three gray scales available (default dark normal light, PC NetHack only). If the game display is diffi- + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 90 + + + cult to read, try adjusting these scales; if this does not correct the problem, try !color. Cannot be set with the `O' command. @@ -5801,17 +5909,6 @@ In addition, some characters are treated specially if they occur as the first character in the pattern, specifically: - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 89 - - - < - always pickup an object that matches rest of pattern; > - never pickup an object that matches rest of pattern. @@ -5835,6 +5932,18 @@ autopickup. The last example results in the exclusion of items known to be cursed from autopickup. + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 91 + + + 9.10. Changing Key Bindings It is possible to change the default key bindings of some special @@ -5865,19 +5974,6 @@ You can bind "mouse1" or "mouse2" to "nothing", "therecmdmenu", "clicklook", or "mouseaction". - - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 90 - - - Special command keys Below are the special commands you can rebind. Some of them can be bound to same keys with no problems, others are in the same "con- @@ -5902,6 +5998,18 @@ getdir.self When asked for a direction, the key to target yourself. Default is + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 92 + + + `.'. getdir.self2 @@ -5931,19 +6039,6 @@ getpos.help When asked for a location, the key to show help. Default is `?'. - - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 91 - - - getpos.mon.next When asked for a location, the key to go to next closest monster. Default is `m'. @@ -5970,6 +6065,17 @@ meta-digit keys to fast-move around, move by skipping the same glyphs instead of by 8 units. Default is `*'. + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 93 + + + getpos.filter When asked for a location, change the filtering mode when using one of the next or previous keys to cycle through targets. Toggles @@ -5997,19 +6103,6 @@ When asked for a location, the key to choose the location, and show more info without asking. Default is `:'. - - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 92 - - - getpos.self When asked for a location, the key to go to your location. Default is `@'. @@ -6034,6 +6127,21 @@ When asked for a location, the key to go to previous closest valid location. Default is `Z'. + + + + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 94 + + + 9.11. Configuring Message Types You can change the way the messages are shown in the message @@ -6065,17 +6173,6 @@ user is prompted with more-prompt, and a message matching "You dis- placed ." is not shown at all. - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 93 - - - The order of the defined MSGTYPE lines is important; the last match- ing rule is used. Put the general case first, exceptions below them. @@ -6099,6 +6196,18 @@ The pattern should be a regular expression. + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 95 + + + Allowed colors are black, red, green, brown, blue, magenta, cyan, gray, orange, light-green, yellow, light-blue, light-magenta, light- cyan, and white. And no-color, the default foreground color, which @@ -6127,21 +6236,6 @@ implicit_uncursed option off so that all items known to be uncursed are actually displayed with the "uncursed" description. - - - - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 94 - - - 9.13. Configuring User Sounds Some platforms allow you to define sound files to be played when @@ -6168,6 +6262,18 @@ volume - the volume to be set while playing the sound file; sound index - optional; the index corresponding to a sound file. + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 96 + + + The pattern should be a regular expression. For example: @@ -6196,18 +6302,6 @@ OPTION=hilite_status:hitpoints/<=30%/red/normal (That example is actually specifying red&normal for <=30% and no- - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 95 - - - color&normal for >30%.) For another example, the following line in your configuration @@ -6234,6 +6328,18 @@ attributes depending upon its capabilities, and in general may inter- pret the attributes any way it wants. For example, on some display systems a request for bold might yield blink or vice versa. On oth- + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 97 + + + ers, issuing an attribute request while another is already set up will replace the earlier attribute rather than combine with it. Since NetHack issues attribute requests sequentially (at least with the tty @@ -6263,17 +6369,6 @@ hallu, "movement" for lev, fly, and ride, and "all" for every condi- tion. - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 96 - - - Allowed behaviors are "always", "up", "down", "changed", a percent- age or absolute number threshold, or text to match against. For the hitpoints field, the additional behavior "criticalhp" is available. @@ -6299,6 +6394,18 @@ prefixed with `<=' or `>=', it also matches when value is below or above the percentage. Use prefix `<' or `>' to match when strictly below or above. (The numeric limit is relaxed + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 98 + + + slightly for those: >-1% and <101% are allowed.) Only four fields support percentage rules. Percentages for "hitpoints" and "power" are straightforward; they're based on the corre- @@ -6327,19 +6434,6 @@ met, a criticalhp rule takes precedence over all other hit- points rules. - - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 97 - - - * text match sets the attribute when the field value matches the text. Text matches can only be used for "alignment", "carry- ing-capacity", "hunger", "dungeon-level", and "title". For @@ -6367,6 +6461,17 @@ + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 99 + + + 9.15. Modifying NetHack Symbols NetHack can load entire symbol sets from the symbol file. @@ -6393,19 +6498,6 @@ NetHack Symbols Symbol Name Description ----------------------------------------------------------------- - - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 98 - - - S_air (air) _ S_altar (altar) " S_amulet (amulet) @@ -6433,6 +6525,19 @@ C S_centaur (centaur) _ S_chain (iron chain) # S_cloud (cloud) + + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 100 + + + c S_cockatrice (cockatrice) $ S_coin (pile of coins) # S_corr (corridor) @@ -6459,19 +6564,6 @@ - S_expl_bc (explosion bottom center) / S_expl_br (explosion bottom right) e S_eye (eye or sphere) - - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 99 - - - ^ S_falling_rock_trap (falling rock trap) f S_feline (cat or other feline) ^ S_fire_trap (fire trap) @@ -6499,6 +6591,19 @@ i S_imp (imp or minor demon) I S_invisible (invisible monster) J S_jabberwock (jabberwock) + + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 101 + + + j S_jelly (jelly) k S_kobold (kobold) K S_kop (Keystone Kop) @@ -6525,19 +6630,6 @@ p S_piercer (piercer) ^ S_pit (pit) # S_poisoncloud (poison cloud) - - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 100 - - - ^ S_polymorph_trap (polymorph trap) } S_pool (water) ! S_potion (potion) @@ -6565,6 +6657,19 @@ * S_ss4 (magic shield 4 of 4) ^ S_statue_trap (statue trap) S_stone (solid rock) + + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 102 + + + ] S_strange_obj (strange object) - S_sw_bc (swallow bottom center) \ S_sw_bl (swallow bottom left) @@ -6591,19 +6696,6 @@ S_unexplored (unexplored terrain) u S_unicorn (unicorn or horse) < S_upladder (ladder up) - - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 101 - - - < S_upstair (staircase up) V S_vampire (vampire) | S_vbeam (vertical beam [zap animation]) @@ -6632,6 +6724,18 @@ Notes: + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 103 + + + * Several symbols in this table appear to be blank. They are the space character, except for S_pet_override and S_hero_override which don't have any default value and can only be used if enabled in the @@ -6658,18 +6762,6 @@ The window port that is active needs to provide support for dis- playing UTF-8 character sequences and explicit red-green-blue colors - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 102 - - - in order for the glyph representation to be visible. For example, the following line in your configuration file will cause the glyph repre- sentation for glyphid G_pool to use Unicode codepoint U+224B and the @@ -6698,6 +6790,18 @@ find the search capabilities of their screen-readers to be quite valu- able. Be certain to examine this Guidebook before playing so you have an idea what the screen layout is like. You'll also need to be able to + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 104 + + + locate the PC cursor. It is always where your character is located. Merely searching for an @-sign will not always find your character since there are other humanoids represented by the same sign. Your @@ -6723,19 +6827,6 @@ menustyle:traditional This will assist in the interface to speech synthesizers. - - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 103 - - - nomenu_overlay Show menus on a cleared screen and aligned to the left edge. @@ -6764,6 +6855,19 @@ mention_map Give feedback messages when interesting map locations change. + + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 105 + + + mention_walls Give feedback messages when walking towards a wall or when travel command was interrupted. @@ -6788,20 +6892,6 @@ showdamage Give a message of damage taken and how many hit points are left. - - - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 104 - - - 9.18. Global Configuration for System Administrators If NetHack is compiled with the SYSCF option, a system adminis- @@ -6833,6 +6923,17 @@ SUPPORT = A string explaining how to get local support (no default value). + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 106 + + + RECOVER = A string explaining how to recover a game on this system (no default value). @@ -6856,18 +6957,6 @@ POINTSMIN = Minimum number of points to get an entry in the score file. - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 105 - - - PERS_IS_UID = 0 or 1 to use user names or numeric userids, respec- tively, to identify unique people for the score file. @@ -6900,6 +6989,17 @@ %n - player name %N - first character of player name + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 107 + + + LIVELOG = A bit-mask of types of events that should be written to the livelog file if one is present. The sample sysconf file accom- panying the program contains a comment which lists the meaning of @@ -6922,18 +7022,6 @@ NetHack maintains a list of the top scores or scorers on your machine, depending on how it is set up. In the latter case, each - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 106 - - - account on the machine can post only one non-winning score on this list. If you score higher than someone else on this list, or better your previous score, you will be inserted in the proper place under @@ -6966,6 +7054,18 @@ option. The other is to issue the "#exploremode" extended command while already playing the game. Starting a new game in explore mode provides your character with a wand of wishing in initial inventory; + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 108 + + + switching during play does not. The other benefits of explore mode are left for the trepid reader to discover. @@ -6987,19 +7087,6 @@ start a game in debug mode when not allowed or not available will result in falling back to explore mode instead. - - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 107 - - - 12. Credits The original hack game was modeled on the Berkeley UNIX rogue @@ -7033,6 +7120,18 @@ R. Black ported PC HACK 3.51 to Lattice C and the Atari 520/1040ST, producing ST Hack 1.03. + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 109 + + + Mike Stephenson merged these various versions back together, incorporating many of the added features, and produced NetHack version 1.4 in 1987. He then coordinated a cast of thousands in enhancing and @@ -7054,18 +7153,6 @@ Olaf Seibert ported NetHack 2.3 and 3.0 to the Amiga. Norm Meluch, Stephen Spackman and Pierre Martineau designed overlay code for PC NetHack 3.0. Johnny Lee ported NetHack 3.0 to the Macintosh. - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 108 - - - Along with various other Dungeoneers, they continued to enhance the PC, Macintosh, and Amiga ports through the later revisions of 3.0. @@ -7100,6 +7187,17 @@ Macintosh, porting it for MPW. Building on their development, Bart House added a Think C port. + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 110 + + + Timo Hakulinen ported NetHack 3.1 to OS/2. Eric Smith ported NetHack 3.1 to the Atari. Pat Rankin, with help from Joshua Delahunty, was responsible for the VMS version of NetHack 3.1. @@ -7120,18 +7218,6 @@ just their classes. He contributed them to the NetHack Development Team which rechristened them "tiles", original usage which has subse- quently been picked up by various other games. NetHack's tiles sup- - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 109 - - - port was then implemented on other platforms (initially MS-DOS but eventually Windows, Qt, and X11 too). @@ -7166,6 +7252,18 @@ Warwick Allison improved the spell casting system with the Wizard Patch. Warwick Allison also ported NetHack to use the Qt interface. + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 111 + + + Warren Cheung combined SLASH with the Wizard Patch to produce Slash'EM, and with the help of Kevin Hugo, added more features. Kevin later joined the NetHack Development Team and incorporated the best of @@ -7187,17 +7285,6 @@ retrieval of old character names to use for random ghost and statue names in the current game.) - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 110 - - - The 3.3 NetHack Development Team, consisting of Michael Allison, Ken Arromdee, David Cohrs, Jessie Collet, Steve Creps, Kevin Darcy, Timo Hakulinen, Kevin Hugo, Steve Linhart, Ken Lorber, Dean Luick, Pat @@ -7231,6 +7318,18 @@ Michael Allison maintained NetHack 3.4 for the MS-DOS platform. Paul Winner and Yitzhak Sapir provided encouragement. + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 112 + + + Dean Luick, Mark Modrall, and Kevin Hugo maintained and enhanced the Macintosh port of 3.4. @@ -7251,19 +7350,6 @@ Christian "Marvin" Bressler maintained 3.4 for the Atari after he resurrected it for 3.3.1. - - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 111 - - - The release of NetHack 3.4.3 in December 2003 marked the begin- ning of a long release hiatus. 3.4.3 proved to be a remarkably stable version that provided continued enjoyment by the community for more @@ -7298,6 +7384,18 @@ Near the end of the development of 3.6.0, one of the significant inspirations for many of the humorous and fun features found in the + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 113 + + + game, author Terry Pratchett, passed away. NetHack 3.6.0 introduced a tribute to him. @@ -7317,19 +7415,6 @@ Kompel, Dion Nicolaas, Derek S. Ray and Yitzhak Sapir maintained the port of NetHack 3.6 for Microsoft Windows. - - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 112 - - - Pat Rankin attempted to keep the VMS port running for NetHack 3.6, hindered by limited access. Kevin Smolkowski has updated and tested it for the most recent version of OpenVMS (V8.4 as of this @@ -7365,6 +7450,18 @@ NetHack 3.6.6 was released on March 8, 2020 containing a security fix and some bug fixes. + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 114 + + + NetHack 3.6.7 was released on February 16, 2023 containing a security fix and some bug fixes. @@ -7386,13 +7483,28 @@ - NetHack 3.7.0 May 1, 2025 - NetHack Guidebook 113 + + + + + + + + + + + + + + + + + @@ -7404,6 +7516,18 @@ Thomson for hardfought.org. Thanks to all those unnamed dungeoneers who invest their time and effort into annual NetHack tournaments such as Junethack, The November NetHack Tournament, and in days past, + + + NetHack 3.7.0 March 25, 2026 + + + + + + NetHack Guidebook 115 + + + devnull.net (gone for now, but not forgotten). @@ -7450,18 +7574,6 @@ From time to time, some depraved individual out there in netland sends a particularly intriguing modification to help out with the - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 114 - - - game. The NetHack Development Team sometimes makes note of the names of the worst of these miscreants in this, the list of Dungeoneers: @@ -7472,35 +7584,13 @@ + NetHack 3.7.0 March 25, 2026 - - - - - - - - - - - - - - - - - - - - - - - - + NetHack Guidebook 116 @@ -7515,19 +7605,6 @@ Bart House John S. Bien Pierre Martineau Benson I. Margulies Johnny Lee Ralf Brown Bill Dyer Jon W{tte Ray Chason - - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 115 - - - Boudewijn Waijers Jonathan Handler Richard Addison Bruce Cox Joshua Delahunty Richard Beigel Bruce Holloway Karl Garrison Richard P. Hughey @@ -7573,24 +7650,13 @@ + NetHack 3.7.0 March 25, 2026 - - - - - - - NetHack 3.7.0 May 1, 2025 - - - - - - NetHack Guidebook 116 + NetHack Guidebook 117 @@ -7650,7 +7716,7 @@ - NetHack 3.7.0 May 1, 2025 + NetHack 3.7.0 March 25, 2026 diff --git a/doc/dlb.txt b/doc/dlb.txt index 412c19820..7c021e273 100644 --- a/doc/dlb.txt +++ b/doc/dlb.txt @@ -6,47 +6,47 @@ NAME dlb - NetHack data librarian SYNOPSIS - dlb { xct } [ vfIC ] arguments... [ files... ] + dlb {c|t|x}[v][C directory] [file] ... + + dlb {c|t|x}[v]I list-file + + dlb {c|t|x}[v][f archive-file-name] [file] ... DESCRIPTION - Dlb is a file archiving tool in the spirit (and tradition) of tar for - NetHack version 3.1 and higher. It is used to maintain the archive - files from which NetHack reads special level files and other read-only - information. Note that like tar the command and option specifiers are - specified as a continuous string and are followed by any arguments - required in the same order as the option specifiers. + Dlb is a file archiving tool in the spirit (and tradition) of tar(1) + for nethack(6) version 3.1 and higher. It is used to maintain the + archive files from which the game reads special level files and other + read-only information. Note that like tar, the letters specifying the + operation and options are expressed as a continuous string. Unlike + tar, dlb is configured with a default set of file names to process. - This facility is optional and may be excluded during NetHack configura- - tion. + Operations + c causes dlb to create a new archive from files in the current direc- + tory. -COMMANDS - The x command causes dlb to extract the contents of the archive into - the current directory. + t lists the files in the archive. - The c command causes dlb to create a new archive from files in the cur- - rent directory. + x causes dlb to extract the contents of the archive into the current + directory. - The t command lists the files in the archive. +OPTIONS + C dir Change directory to dir before trying to read any files. -OPTIONS AND ARGUMENTS - v verbose output + f archive Read from or write to archive instead of LIBFILE (usually + the nhdat file in the playground). - f archive specify the archive. Default if f not specified is LIBFILE - (usually the nhdat file in the playground). + I list-file Read from list-file the names of files to emplace within + or extract from the archive. The default for archive cre- + ation is LIBLISTFILE. - I lfile specify the file containing the list of files to put in to - or extract from the archive if no files are listed on the command line. - Default for archive creation if no files are listed is LIBLISTFILE. - - C dir change directory. Changes directory before trying to read - any files (including the archive and the lfile). + v Operate verbosely. EXAMPLES Create the default archive from the default file list: - dlb c + dlb c - List the contents of the archive 'foo': - dlb tf foo + List the contents of the archive foo: + dlb tf foo AUTHOR Kenneth Lorber @@ -55,15 +55,17 @@ SEE ALSO nethack(6), tar(1) BUGS - Not a good tar emulation; - does not mean stdin or stdout. Should - include an optional compression facility. Not all read-only files for - NetHack can be read out of an archive; examining the source is the only - way to know which files can be. + o Not a good tar emulation; - does not mean stdin or stdout. + + o Should include an optional compression facility. + + o Not all read-only files for NetHack can be read out of an archive; + examining the source is the only way to know which files can be. COPYRIGHT This file is Copyright (C) Kenneth Lorber, 2024 for version keni-git- - set:1.13. NetHack may be freely redistributed. See license for - details. + set:1.13. NetHack may be freely redistributed. See license for de- + tails. diff --git a/doc/fixes3-6-7.txt b/doc/fixes3-6-7.txt index a00aab090..e34d9f1b4 100644 --- a/doc/fixes3-6-7.txt +++ b/doc/fixes3-6-7.txt @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.0 $ $NHDT-Date: 1676619775 2023/02/17 07:42:55 $ +$NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.21 $ $NHDT-Date: 1683746783 2023/05/10 19:26:23 $ fixes36.7 contains a summary of changes made to 3.6.6 in order to produce 3.6.7 as well as any post-release fixes in binaries. @@ -40,6 +40,32 @@ none Fixes to 3.6.7 Post-release Problems and other Post-release changes ------------------------------------------------------------------- -none - - +extend the fix for build failure w/ newer C library headers to macOS (pr #988) +Windows: fix range error detected by address sanitizer in plselInitDialog() +Windows: nethackw.exe was vertically spacing out menu items based on + the height of the tileset in use, even though the tiles in the + menu were drawn at the default height of 16 anyway; get rid of + the extraneous vertical spacing between the menu rows +hilite_pile can remain on the map after eating food off the floor +vms: update winprocs.h to ensure that CLR_MAX is defined; necessary for + compiling vmsmail.c which uses winprocs.h but not hack.h +curses: when #quitting, just before the high scores are about to be shown + status_window was NULL and dereferenced, so add checks (issue #1090) +unix: update Makefile.dat so that if parallel make is used, it will build + the data files "engrave", "epitaph", and "bogusmon" sequentially; + 'makedefs -s' builds all three at once and doing parallel instances + of that can produce seemingly mysterious problems by stomping on + each other's results +fix the mingw32 build of NetHack 3.6.7 by updating sys/winnt/Makefile.gcc, + sys/winnt/winnt.c and sys/winnt/stubs.c +correct the Ixoth tile +makedefs can produce individual bogusmon, engrave and epitaph files +drinkfountain() fate 24 to curse objects could end up cursing objects + on the floor chain instead of the intended inventory object chain +proceed with showpaths option even if the sysconf file is missing +avoid memory leak in mk_artifact(); cherry-pick of 3cca4f27 from 3.7 WIP +avoid using col in Guidebook build, since some distros no longer include + it, particularly some that use musl libc +back-port some nroff macro updates for Guidebook +prevent a crash when using 'O' to interactively set a text match highlight + for hunger diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index fb87d1121..4a0bb71e4 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1,4 +1,4 @@ -NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1535 $ $NHDT-Date: 1740629713 2025/02/26 20:15:13 $ +NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1614 $ $NHDT-Date: 1769342601 2026/01/25 04:03:21 $ General Fixes and Modified Features ----------------------------------- @@ -215,7 +215,7 @@ if riding or levitating, hero could apply bullwhip downward to pull up things some monster code was checking whether pets or engulfers were eating green slime by checking for green slime corpse instead of glob change light radius of stack of candles to square root -could get redundate "mon hits other-mon" messages when mon wields an artifact +could get redundant "mon hits other-mon" messages when mon wields an artifact failed untrap while mounted that moved hero onto the trap would leave steed with stale coordinates, triggering warnings if 'sanity_check' is On when digging a pit results in it being filled by adjacent pool or lava, any @@ -530,7 +530,7 @@ innocuous items like scrolls or eucalyptus leaves did harm when falling on hero's head after being thrown upward fighter types who start out knowing all non-magic armor should not know cornuthaum and dunce cap -prediscovered weapons adjustmens: only knights and samurai know polearms; +prediscovered weapons adjustments: only knights and samurai know polearms; rangers know launchers (bows), ammo (arrows), and spears regardless of their race/species; likewise, rogues know all daggers if the move counter ever reaches 1000000000, end the game @@ -715,7 +715,7 @@ selection of random engravings, epitaphs, and hallucinatory monster names had follow longer than average lines are most likely to be chosen and ones which follow shorter than average lines are least likely; use same workaround as for rumors: pad the shortest lines; result isn't - uniforn distribution but is better (tradeoff vs size; see makedefs) + uniform distribution but is better (tradeoff vs size; see makedefs) make selection of random rumors, engravings, epitaphs, and hallucinatory monst names have uniform distribution by handling long lines specially when filling a special room with monsters, if one that can come in groups got @@ -837,7 +837,8 @@ if a lit potion of oil on the floor was launched by an explosion and it hit it could trigger an "obj_is_local" panic when end of game cleanup tried to extinguish it as a light source place_object() validated coordinates after using them to index level.objects -killed rope golem may drop leashes and bullwhips +killed rope golem may drop leashes, bullwhips, and grappling hooks +killed leather golem may drop leather cloaks, and saddles using magic portals stuns hero for a few turns using level teleporters confuses hero without teleport control for a few turns clear obj->bypass for buried objects [a giant on ice triggers a fire trap, @@ -1237,7 +1238,7 @@ restore the ability for trap creation via magic which creates pits to destroy allow #sit while flying over a squeaky board trap to trigger it weight of statues of wraiths and of monsters which never leave a corpse was 0 when a werecreature in human form attacked hero, it could transform to critter - despite hero having the Protection_from_shape_changers_attibute + despite hero having the Protection_from_shape_changers_attribute status highlighting for hit points didn't work as intended for up or down HP changes; 'up' rule was used for both, 'down' rule was ignored unhide an unseen water monster using a polymorph trap on land @@ -1510,6 +1511,7 @@ if a tree and a boulder or statue were at the same location, applying an axe would break the boulder or statue rather than chop the tree avoid premapping outside Sokoban map to prevent showing stone glyphs Grimtooth is permanently poisoned, protects from poison, and can fling poison +invoke cost for Grimtooth and Sunsword can be paid with magic power cursed magic whistle can teleport you to your pet Fire and Frost Brand can be invoked for expert level fireball or cone of cold wielding Trollsbane grants hungerless regeneration @@ -1528,6 +1530,72 @@ a gas spore that was killed when an engulfer swallowed it produced an explosion hallucination can display objects on the map that have a description (for shuffling into play at game start) but no name; examining those might trigger a crash +some food and paper items do no damage when bashing +improved messages for oilskin sacks protecting from water damage +some messages by amorous demons and mail daemon were delivered as verbal ones + even when the hero is deaf +travel couldn't find the vibrating square if it was covered by an object or + a monster; it isn't really a trap so treat it as special terrain +travel would stop one step in front of known vibrating square like other traps +fix bug which delayed burden changes due to monster actions (e.g. reverting to + natural form due to damage, changing carry capacity) for one turn +on arboreal levels (Ranger quest) where STONE terrain is treated as TREE, an + object at a tree location would be described as "embedded in stone" +an item at an ordinary tree location, whether the level is arboreal or not, + would be described as itself with no mention of the tree +some types of shopkeeper now start with a scroll of charging +objects are now accurately tracked as discovered even if not type-named nor + formally identified (fixing some bugs in scroll writing, and making + the discoveries list more accurate) +you cannot sacrifice objects/corpses while stunned or confused +'whatis' actions // and /? didn't work when the lootabc option was on, they + required /a and /c instead; add '/' and '?' as group accelerators so + that they work; /y and /n for them now only work when lootabc is off +writing on an unidentified scroll of blank paper identifies blank paper +dumplogs include spells and skills +praying will not restore monster-form HP while polymorphed, unless you + have unchanging +winter wolf cub was missing for monster to lycanthrope conversion +monster elves shooting arrows weren't getting intended small to-hit and damage + bonuses +luck has a reduced effect on to-hit chance +monsters use wand of teleportation to move hero away from item pile +lycanthrope instincts keep hero from changing shape while next to hostiles +scroll of enchant armor formula has changed (in particular, magical armor now + gains less enchantment when enchanted than nonmagical armor and + uncursed scrolls can sometimes enchant by more than one point) +dragonhide can rot +throwing ammo without a launcher produces a message +polymorphing into a large polyform unequips rather than destroying cloaks +clarify in the #quit message that it doesn't save the game +cursed potion of invisibility removes intrinsic invisibility +fix bug which caused engulf damage from air elementals and fog clouds to + ignore damage reduction from armor class +elementals do double damage on their home plane +water elementals move slightly more slowly +the "totally digested" instadeath timer is much faster +slow monster effects are more effective against faster enemies +light-spell is clerical, if playing a priest +boomerang can hit multiple monsters +the "bustling town" minetown has more peacefuls +healers may get tiny damage increase when attacking with knives +allow rogues to also backstab sleeping or paralyzed monsters +rogues cannot backstab monsters that have no backside +give experience if opening Schroedinger's Box causes death of the cat inside +priest donation amounts are explicitly stated, randomized slightly, based on + peak rather than current level, and allow for bulk donations (buying + larger amounts of clairvoyance/protection) if you have a lot of gold +amulet of magical breathing increases power regeneration +change some command keys, 'v' is now chronicle, 'V' is versionshort, + m-prefix 'V' is longer version, remove key binding of #history +one orc-town shaman has a higher level, affecting spellcasting +hero has a small chance of catching items thrown at them +wizard mode: history menu for #wizwish and WIZKIT +monster priests and wizards did not cast spells +prevent phaseable monsters hiding deep inside nondiggable walls +blessed potion of see invisible does not guarantee the intrinsic +prevent selecting all options in #optionsfull menu +shapeshifters change shape less Fixes to 3.7.0-x General Problems Exposed Via git Repository @@ -1855,7 +1923,7 @@ after the fix for zombie reviving near hero (which now interrupts hero's occupation), could get "revive panic" when eating a troll corpse if it revived on the same turn that eating the corpse would finish a wizard's starting equipment was supposed to include a random spellbook of - spell level 1 through 3 but it was being foced to be level 1 + spell level 1 through 3 but it was being forced to be level 1 earlier fix for prices of unpaid objects going away in persistent inventory display when hero bought something during itemized billing didn't work if paying for a used-up shop item--prices of any unpaid items vanished @@ -1945,7 +2013,7 @@ don't blame/credit hero for stinking cloud if dying quest nemesis releases one Lua selection operations 'subtract' and 'xor' didn't always work as intended accept 'true' or 'false' as value for des.object field 'trapped' special level loader didn't check for bad coordinates supplied by .lua - file thorougnly enough + file thoroughly enough hero might not be credited with "entered Mine Town" achievement for the town variations which treat the whole level as the town if that level gets entered via falling or level teleport @@ -2069,7 +2137,7 @@ restoring a save file could trigger impossible "rnd(0) attempted" if it tried a released version, it would crash due to divide by 0 error instead fix regression of a post-3.6 fix: if 2 Wizards of Yendor were in play and 1 escaped the dungeon, bookkeeping for current number of Wizards stayed - at 2, interferring with future Wizard behavior + at 2, interfering with future Wizard behavior sometimes a repeat count from the preceding command carried over to most recent one when using do-again (^A); if the most recent one was an extended command, the spurious repeat was for '#' @@ -2128,6 +2196,36 @@ successfully disarming a chest trap was clearing the chest's 'tknown' bit when game ended with 'force_invmenu' On, final disclosure of inventory would contain spurious menu entry "? - (list likely candidates)" avoid reporting "a gold pieces appears next to you" for mimic +don't try to split a pudding that got jousted into a hole and is off the map +mounted hero was able to deliver joust hits when trapped +timer sanity check for melting ice gave false complaint about non-ice for + frozen moat under open drawbridge +mhitm_ad_phys() was not applying Half_physical_damage when hero was target +throwing crystal plate mail or helm of brilliance up against the ceiling could + result in the item being cracked and then vanishing +yn_function (used all over the place) sometimes triggered an impossible() + during do-again (^A) processing [ongoing revisions; more are probably + needed; listed here in case ^A behavior changes] +uncursed genocide while hallucinating deliberately mis-reports hero's role as + target but was also inappropriately showing it to livelog/#chronicle +livelog/#chronicle for bribing a demon lord reported random monster and + currency if hero was hallucinating +livelog/#chronicle for saving-grace: if saving-grace prevents hero's death, + report it [at present, it uses the livelog classification for breaking + a conduct] +naming a type of item, unnaming it (with name of ), naming it again + (new name or re-use of old one), then unnaming it again would issue + impossible: "named object not in disco" +throwing entire quiver and then picking the stack back up was not putting it + back into quiver +when reading a T-shirt or apron, add trailing period to the slogan unless + already present in the quoted text +when reading an engraving, suppress the addition of a trailing period if the + text already has one +cursed magic whistle could teleport hero on no-teleport levels +give a brief explanation when receiving a wish for acquiring the Amulet +teleportation traps no longer teleport you on levels made temporarily + no-teleport by the presence of a monster Fixes to 3.7.0-x Platform and/or Interface Problems Exposed Via git Repository @@ -2285,7 +2383,7 @@ curses: if user's terminal was set to 'application keypad mode' (DEC VTxxx "ESC SPC G"), nethack wasn't recognizing number pad keys curses: change petattr attributes, dropping support for curses-only ones curses: swap the grey and no-color color initialization -curses+Qt: allow changing default colors with the 'palette' config option +curses+tty+Qt: allow changing default colors with the 'palette' config option (only if compiled with CHANGE_COLOR) curses: if messages have been issued during start-up (for instance, warnings about issues in run-time config file), prompt user to press @@ -2475,6 +2573,8 @@ Unix: add ../include/nhlua.h to the alloc.o dependencies in Makefile.utl to Unix: implement SELF_RECOVER compile-time option, on by default on linux Unix: allow build to succeed with musl (instead of glibc), by specifying musl=1 on the make command line +Unix: support tilde expansion for home directory path, "~/relative-path", in + command line --nethackrc=path and environment NETHACKOPTIONS=@path user_sounds: move the message hook from inside individual window display ports to the core where it allows MSGTYP_NOSHOW msgtyp's to still trigger sounds to correct a reported github issue; also fixes a past reported @@ -2656,7 +2756,7 @@ menu for what-is command supports /^ and /" to view a list of nearby or whole level visible and remembered traps spiders will occasionally spin webs when moving around drinking a burning potion of oil will cure being turned into slime -new bigroom variants, a boulder maze and hexagons +new bigroom variants, a boulder maze, hexagons, pillars vomiting on an altar provokes the deities wrath branch stairs have a different glyph, show up in yellow color in tty duration of confusion when drinking booze depends upon hunger state @@ -2794,9 +2894,6 @@ wand of secret door detection, spell of detect unseen, and wizard mode ^E now flash the cursor at each location where detection finds something saving-grace: once per game if receiving a killing blow from full or nearly full HP, survive with 1 HP -livelog/#chronicle for saving-grace: if saving-grace prevents hero's death, - report it [at present, it uses the livelog classification for breaking - a conduct] enlightenment/attribute disclosure for saving-grace: include a line for have or haven't been saved (game in progress) or did or didn't get saved (game over) via saving-grace @@ -2809,11 +2906,24 @@ enlightenment/attribute disclosure for saving-grace: include a line for have tourists gain experience by "taking photos" of new creatures, and going to new dungeon levels healers gain experience by healing pets -blessed scroll of destroy armor asks which armor to destroy +blessed scroll of destroy armor asks which armor to destroy if wearing more + than one armor; otherwise it'll destroy a cursed armor, or erodes + random armor archeologists' fedora is lucky telepathic hero can discern which particular monster just read a scroll add "make fetch-docs" to download pre-formatted documentation monsters will use throw-and-return weapons such as an aklys +archeologists can identify scrolls by deciphering their labels +monster destroy armor -spell first erodes armor +iron shoes protect the wearer against certain floor-based traps +ring of stealth prevents hero from leaving tracks +the game now automatically tracks which sell prices and buy prices you have + seen for each type of item; these are visible in the discoveries list, + and can also be shown elsewhere using the new 'price_quotes' option +new shields: shield of shock resistance, shield of drain resistance +new wand: wand of stasis +early-game monsters always miss with their first use of an attack wand +new command #toggle which can be used to toggle a boolean option Platform- and/or Interface-Specific New Features @@ -2872,7 +2982,7 @@ Unix: support --nethackrc=filename on the command line; same effect as NETHACKOPTIONS='@filename' but leaves NETHACKOPTIONS available for specifying options; --no-nethackrc is same as --nethackrc=/dev/null Windows: implement MSGHANDLER (pr #749 by argrath) -Windows: Add configuration to support Curses on WinGUI (pr #1028 by chasonr) +Windows: Add configuration to support Curses on WinGUI (pr #1028 by chasonr) X11: implement 'selectsaved', restore via menu of saved games X11: echo getline prompt and response (wishes, applying names) to message window and dumplog message history @@ -3012,6 +3122,13 @@ split making pits on do_earthquake() into a separate function and update the WASM cross-compile to work with the current code (pr #1331 by guillaumebrunerie) add silver maces (pr #1405 by disperse) +applied several source comment spelling or grammar fixes that were linked + to an old rec.games.roguelike.nethack post from May 2006 + by Arthur J. O'Dwyer +modernize code of LaTeX Guidebook (pr #1473 by Daniel Clow) +restore ability for a characteristic also resets exercise/abuse for that + characteristic (pr #1403 by greg-kennedy) +cross-compilation for the Amiga on Linux (pr #1494 by ingpaschke) Code Cleanup and Reorganization @@ -3145,4 +3262,3 @@ relocate general-purpose function choose_classes_menu(), from options.c to windows.c remove register from variable declarations make glyph_to_cmap() a function instead of a macro - diff --git a/doc/lua.adoc b/doc/lua.adoc index 3b457b670..9cfa1cd8e 100644 --- a/doc/lua.adoc +++ b/doc/lua.adoc @@ -61,6 +61,7 @@ Set debugging flags. | mongen | boolean | Do monsters generate | hunger | boolean | Does hero's hunger-state increase | overwrite_stairs | boolean | Allow special-file commands overwrite the stairs +| prevent_pline | boolean | Prevent messages going out to the UI |=== Example: @@ -120,6 +121,15 @@ Example: local k = nh.eckey("help"); +=== flip_level + +Flip the level horizontally or vertically. + +Example: + + nh.flip_level(1); + + === getlin Asks the player for a text to enter, and returns the entered string. @@ -250,6 +260,26 @@ Example: local str = nh.ing_suffix("foo"); +=== int_to_objname + +Convert integer value to object name and class. +Returns two strings, the object base name and the class character. +The returned strings may be empty if an error occurred. + +Example: + + local oname, oclass = nh.int_to_objname(45); + + +=== int_to_pmname + +Convert integer value to monster type name. + +Example: + + local pmname = nh.int_to_pmname(12); + + === is_genocided Is specific monster type genocided? Returns a boolean value. @@ -789,6 +819,7 @@ The hash parameter accepts the following keys: | stunned | boolean | | confused | boolean | | waiting | boolean | monster will wait until hero gets next to it +| m_lev_adj | integer | monster's level adjustment | tail | boolean | generate worm without a tail? | group | boolean | generate a group of monsters? | adjacentok | boolean | is adjacent location ok, if given one is not suitable? @@ -832,7 +863,7 @@ Example: === object -Create an object. Returns the object as an <> class. +Create an object and place it somewhere on the map. Returns the object as an <> class. The table parameter accepts the following: [options="header"] @@ -856,8 +887,8 @@ The table parameter accepts the following: | greased | boolean | Is the object greased? | broken | boolean | Is the object broken? | achievement | boolean | Is there an achievement attached to the object? -| x, y | int | Coordinates on the level -| coord | table | x,y coordinates in table format +| x, y | int | Coordinates on the level; defaults to a random location. +| coord | table | x,y coordinates in table format; defaults to a random location. | montype | string | Monster id or class | historic | boolean | Is statue historic? | male | boolean | Is statue male? @@ -1136,6 +1167,16 @@ Example: local sel2 = selection.clone(sel); +=== describe_size + +Return a text describing the size of the selection. + +Example: + + local sel = selection.fillrect(1,1, 3,3); + local txt = sel:describe_size(); + + === ellipse Example: @@ -1352,11 +1393,13 @@ Handling objects via obj-class. === new -Create a new object via wishing routine. +Create a new object, either via wishing routine, or specifying object name and class. +Unlike des.object, does not place the object anywhere. Example: local o = obj.new("rock"); + local o = obj.new({ id = "invisibility", class = "!" }); === isnull @@ -1545,6 +1588,11 @@ These constants are in the `nhc` table. |=== | COLNO | Number of map columns | ROWNO | Number of map rows +| NUMMONS | Number of different monster types +| LOW_PM | First monster type id. See <<_int_to_pmname>>. +| HIGH_PM | Last monster type id. See <<_int_to_pmname>>. +| FIRST_OBJECT | First object type id. See <<_int_to_objname>>. +| LAST_OBJECT | Number of object type ids. See <<_int_to_objname>>. | DLB | 1 or 0, depending if NetHack is compiled with DLB |=== diff --git a/doc/makedefs.6 b/doc/makedefs.6 index 136084473..5e24242fa 100644 --- a/doc/makedefs.6 +++ b/doc/makedefs.6 @@ -99,7 +99,7 @@ only if it is present, to obtain .B githash= and .B gitbranch= - info and include related preprocessor #defines in +info and include related preprocessor #defines in .I date.h file. .br diff --git a/doc/makedefs.txt b/doc/makedefs.txt index e784946c3..38a27f308 100644 --- a/doc/makedefs.txt +++ b/doc/makedefs.txt @@ -31,8 +31,8 @@ SHORT COMMANDS the MDGREP FUNCTIONS section below for details. -m Generate date.h and options file. It will read dat/gitinfo.txt, - only if it is present, to obtain githash= and gitbranch= - info and include related preprocessor #defines in date.h file. + only if it is present, to obtain githash= and gitbranch= info + and include related preprocessor #defines in date.h file. -p Generate pm.h @@ -53,7 +53,7 @@ LONG COMMANDS Show debugging output. --make [command] - Execute a short command. Command is given without preceding + Execute a short command. Command is given without preceding dash. --input file @@ -61,66 +61,66 @@ LONG COMMANDS is - standard input is read. --output file - Specify the output file for the command (if needed). If the + Specify the output file for the command (if needed). If the file is - standard output is written. --svs [delimiter] - Generate a version string to standard output without a trailing - newline. If specified, the delimiter is used between each part + Generate a version string to standard output without a trailing + newline. If specified, the delimiter is used between each part of the version string. - --grep Filter the input file to the output file. See the MDGREP FUNC- + --grep Filter the input file to the output file. See the MDGREP FUNC- TIONS section below for information on controlling the filtering operation. --grep-showvars - Show the name and value for each variable known to the grep + Show the name and value for each variable known to the grep option. --grep-trace - Turn on debug tracing for the grep function ( --grep must be + Turn on debug tracing for the grep function ( --grep must be specified as well). --grep-defined symbol - Exit shell true (0) if symbol is known and defined, otherwise + Exit shell true (0) if symbol is known and defined, otherwise exit shell false (1). --grep-define symbol - Force the value of symbol to be "defined." Symbol must already + Force the value of symbol to be "defined." Symbol must already be known to makedefs. --grep-undef symbol - Force the definition of symbol to be "undefined." Symbol must + Force the definition of symbol to be "undefined." Symbol must already be known to makedefs. MDGREP FUNCTIONS - The --grep command (and certain other commands) filter their input, on - a line-by-line basis, according to control lines embedded in the input - and on information gleaned from the NetHack(6) configuration. This - allows certain changes such as embedding platform-specific documenta- + The --grep command (and certain other commands) filter their input, on + a line-by-line basis, according to control lines embedded in the input + and on information gleaned from the NetHack(6) configuration. This + allows certain changes such as embedding platform-specific documenta- tion into the master documentation files. Rules: - The default conditional state is printing enabled. - - Any line NOT starting with a caret (^) is either suppressed - or passed through unchanged depending on the current condi- + - Any line NOT starting with a caret (^) is either suppressed + or passed through unchanged depending on the current condi- tional state. - - Any line starting with a caret is a control line; as in C, - zero or more spaces may be embedded in the line almost any- - where (except immediately after the caret); however the + - Any line starting with a caret is a control line; as in C, + zero or more spaces may be embedded in the line almost any- + where (except immediately after the caret); however the caret must be in column 1. - Conditionals may be nested. - - Makedefs will exit with an error code if any errors are - detected; processing will continue (if it can) to allow as + - Makedefs will exit with an error code if any errors are + detected; processing will continue (if it can) to allow as many errors as possible to be detected. - - Unknown identifiers are treated as both TRUE and as an - error. Note that --undef or #undef in the NetHack(6) con- + - Unknown identifiers are treated as both TRUE and as an + error. Note that --undef or #undef in the NetHack(6) con- figuration are different from unknown. Control lines: @@ -143,8 +143,8 @@ AUTHOR The NetHack Development Team COPYRIGHT - This file is Copyright (C) Kenneth Lorber, 2024 for version - NetHack-3.7:1.22. NetHack may be freely redistributed. See license + This file is Copyright (C) Kenneth Lorber, 2024 for version + NetHack-3.7:1.22. NetHack may be freely redistributed. See license for details. diff --git a/doc/mn.7 b/doc/mn.7 index 1d40edc2c..827ed82a8 100644 --- a/doc/mn.7 +++ b/doc/mn.7 @@ -136,7 +136,7 @@ b num \- i used to embolden italics _bi \*Z mac \- \- print \*x in emboldened font 2, \*y after, \*z before bm num 1i,1i+1v \- height of bottom margin -_bt mac \- \- print pottom title +_bt mac \- \- print bottom title bt num .5i+1v \- bottom of footer to bottom of page _cf \*Z mac \- \- print contents of header line (double quotes around \*x, \*y before, \*z after) diff --git a/doc/mn.txt b/doc/mn.txt index 1c42c2a04..7623777fd 100644 --- a/doc/mn.txt +++ b/doc/mn.txt @@ -75,7 +75,7 @@ b num - i used to embolden italics .bi x y z mac - - print x in emboldened font 2, y after, z before bm num 1i,1i+1v - height of bottom margin -.bt mac - - print pottom title +.bt mac - - print bottom title bt num .5i+1v - bottom of footer to bottom of page .cf x y z mac - - print contents of header line (double quotes around x, y before, z after) diff --git a/doc/mnh.txt b/doc/mnh.txt index b7e966065..4d78af3d5 100644 --- a/doc/mnh.txt +++ b/doc/mnh.txt @@ -36,6 +36,7 @@ REQUESTS Macro What Initial Note Explanation Name It Is Value +nH num 0 - avoid processing multiple times .BR mac - - hard line break with vertical padding inserted bR num - i .CC x y mac - - aligned one char key x with short definition y @@ -52,6 +53,7 @@ PX num - i PY num - i .SD x mac - - .sd with options c-center i-indent n-no indent SF num - i +UR mac - - URL passthrough for HTML generator .UX mac - - .ux with updated trademark owner diff --git a/doc/options.txt b/doc/options.txt index a07b316cc..71a4dd6bc 100644 --- a/doc/options.txt +++ b/doc/options.txt @@ -1,4 +1,4 @@ -The definitition for each OPTIONS= option resides in include/optlist.h as of +The definition for each OPTIONS= option resides in include/optlist.h as of February 2020 in 3.7 WIP. Boolean and compound options are combined into a single allopt[] array. @@ -40,9 +40,14 @@ To add an entirely new option macro type: iii) an initialization of an element in the allopt[] array, at index opt_xxxx from step ii (xxxx is the option name). - 2. Create the optfn_xxxx() function in options.c. Failure to do that will - result in a link error of "undefined function optfn_xxxx." The functions are - in options.c in alphabetical sequence by function name. + 2. If you are adding a boolean option, link it to a global variable + (e.g. in flags or iflags) and update optfn_boolean in options.c if + necessary. + + 3. If you are not adding a boolean option, create the optfn_xxxx() + function in options.c. Failure to do that will result in a link error + of "undefined function optfn_xxxx." The functions are in options.c in + alphabetical sequence by function name. The skeletal template for an optn_xxxx() function is: @@ -83,7 +88,7 @@ To add an entirely new option macro type: return optn_ok; } - 3. NOTE: If you add (or delete) an option, please update the short + 4. NOTE: If you add (or delete) an option, please update the short options help (option_help()), the long options help (dat/opthelp) and also the Guidebooks. diff --git a/doc/sound.txt b/doc/sound.txt index 2f90d3334..66ff2d0d0 100644 --- a/doc/sound.txt +++ b/doc/sound.txt @@ -87,7 +87,7 @@ There are 4 distinct types of sound sound_triggers used by NetHack. at the outset (ambience_begin), at the termination (ambience_end), and periodically in-between as needed - (ambience_upate), likely with a different + (ambience_update), likely with a different hero proximity value. SOUND_TRIGGER_VERBAL Invoked by the core when someone (or something) @@ -233,7 +233,7 @@ sound_play_usersound(char *filename, int32_t volume, int32_t usidx); sound_ambience(int32_t ambienceid, int32_t ambience_action, int32_t hero_proximity); -- NetHack will call this function when it wants a particular - ambience related sound played in order to provide atomosphere + ambience related sound played in order to provide atmosphere or flavor. -- The ambienceid is used to identify the particular ambience sound being sought. The abienceid identifiers are from the diff --git a/doc/tmac.nh b/doc/tmac.nh index 86bc643b4..f8c30c2d0 100644 --- a/doc/tmac.nh +++ b/doc/tmac.nh @@ -29,7 +29,7 @@ .\" labeled paragraph label (and first line) .de PL .br -\\h'|-\\n(PYu'\\$1\\h'|-\\n(PXu'\ -\ \\c \" back up, output the label, then +\\h'|-\\n(PYu'\\$1\\h'|-\\n(PXu'\ -\ \\c\" back up, output the label, then . \" skip to width-of(" - ") before the . \" normal indentation, output the " - " . \" then attach the next line (the diff --git a/doc/window.txt b/doc/window.txt index 45593eb88..d2f55793a 100644 --- a/doc/window.txt +++ b/doc/window.txt @@ -284,7 +284,7 @@ update_inventory(arg) -- For an argument of 0: -- Indicate to the window port that the inventory has been changed. - -- Merely calls display_inventory() for window-ports + -- Merely calls repopulate_perminvent() for window-ports that leave the window up, otherwise empty. -- or for a non-zero argument: -- Prompts the user for a menu scrolling action and @@ -1001,6 +1001,10 @@ char display_inventory(lets, want_reply) -- Calls a start_menu()/add_menu()/select_menu() sequence. It returns the item selected, or '\0' if none is selected. Returns '\033' if the menu was canceled. +void repopulate_perminvent(void) + -- a minimal alternative to display_inventory() that + focuses only on repopulating the permanent inventory + window. raw_printf(str, ...) -- Like raw_print(), but accepts arguments like printf(). This routine processes the arguments and then calls raw_print(). diff --git a/outdated/include/amiconf.h b/include/amiconf.h similarity index 89% rename from outdated/include/amiconf.h rename to include/amiconf.h index d09801509..77baa09f1 100644 --- a/outdated/include/amiconf.h +++ b/include/amiconf.h @@ -17,6 +17,10 @@ #ifdef CROSS_TO_AMIGA #include #include +#include +#include +#include +#include #endif #ifdef __SASC_60 /* since SAS can prevent re-inclusion */ @@ -48,13 +52,9 @@ typedef long off_t; #define PATHLEN 130 /* data librarian defs */ -#ifndef NOCWD_ASSUMPTIONS -#define DLBFILE "NetHack:nhdat" /* main library */ -#define DLBFILE2 "NetHack:nhsdat" /* sound library */ -#else #define DLBFILE "nhdat" /* main library */ -#define DLBFILE2 "nhsdat" /* sound library */ -#endif +/* nhsdat sound library not used in 3.7 */ +#undef DLBFILE2 #ifndef CROSS_TO_AMIGA #define FILENAME_CMP stricmp /* case insensitive */ @@ -193,21 +193,21 @@ void amii_setpens(int); #define M(c) ((c) -128) #endif struct ami_sysflags { - char sysflagsid[10]; + char sysflagsid[10]; #ifdef AMIFLUSH - boolean altmeta; /* use ALT keys as META */ - boolean amiflush; /* kill typeahead */ + boolean altmeta; /* use ALT keys as META */ + boolean amiflush; /* kill typeahead */ #endif #ifdef AMII_GRAPHICS - int numcols; - unsigned short amii_dripens[20]; /* DrawInfo Pens currently there are 13 in v39 */ - AMII_COLOR_TYPE amii_curmap[AMII_MAXCOLORS]; /* colormap */ + int numcols; + unsigned short amii_dripens[20]; /* DrawInfo Pens currently there are 13 in v39 */ + AMII_COLOR_TYPE amii_curmap[AMII_MAXCOLORS]; /* colormap */ #endif #ifdef OPT_DISPMAP - boolean fast_map; /* use optimized, less flexible map display */ + boolean fast_map; /* use optimized, less flexible map display */ #endif #ifdef MFLOPPY - boolean asksavedisk; + boolean asksavedisk; #endif }; extern struct ami_sysflags sysflags; diff --git a/include/artilist.h b/include/artilist.h index b5dce04f8..0dd2181a8 100644 --- a/include/artilist.h +++ b/include/artilist.h @@ -197,7 +197,7 @@ static NEARDATA struct artifact artilist[] = { * or the shriek that shrieked he, * As I gnashed my teeth, and from my sheath * I drew my Snickersnee! - * --Koko, Lord high executioner of Titipu + * --Ko-Ko, Lord high executioner of Titipu * (From Sir W.S. Gilbert's "The Mikado") */ A("Snickersnee", KATANA, SPFX_RESTR, 0, 0, PHYS(0, 8), NO_DFNS, NO_CARY, diff --git a/include/botl.h b/include/botl.h index 87c82d25c..1a1507010 100644 --- a/include/botl.h +++ b/include/botl.h @@ -27,6 +27,9 @@ Astral Plane \GXXXXNNNN:123456 HP:1234(1234) Pw:1234(1234) AC:-127 #define MAXCO (COLNO + 40) #endif +/* limit of the player's name in the status window */ +#define BOTL_NSIZ 16 + struct condmap { const char *id; unsigned long bitmask; diff --git a/include/config.h b/include/config.h index 7069b268d..5016c6702 100644 --- a/include/config.h +++ b/include/config.h @@ -149,10 +149,11 @@ #ifdef X11_GRAPHICS /* - * There are two ways that X11 tiles may be defined. (1) using a custom - * format loaded by NetHack code, or (2) using the XPM format loaded by - * the free XPM library. The second option allows you to then use other - * programs to generate tiles files. For example, the PBMPlus tools + * There are two ways that X11 tiles may be defined: + * (1) using a custom format loaded by NetHack code. + * (2) using the XPM format loaded by the free XPM library. + * The second option allows you to then use other programs to + * generate tiles files. For example, the PBMPlus tools * would allow: * xpmtoppm x11tiles_big.xpm diff --git a/include/config1.h b/include/config1.h index 3a8632564..402e0e73c 100644 --- a/include/config1.h +++ b/include/config1.h @@ -95,7 +95,7 @@ #undef UNIX #define DLB #define HACKDIR "NetHack:" -#define NO_MACRO_CPATH + #endif /* diff --git a/include/context.h b/include/context.h index 589a80691..c207c5a8d 100644 --- a/include/context.h +++ b/include/context.h @@ -168,7 +168,7 @@ struct context_info { boolean bypasses; /* bypass flag is set on at least one fobj */ boolean door_opened; /* set to true if door was opened during test_move */ boolean resume_wish; /* game was exited while in wish prompt */ - boolean tips[NUM_TIPS]; + unsigned long tips; struct dig_info digging; struct victual_info victual; struct engrave_info engraving; diff --git a/include/cstd.h b/include/cstd.h index 3e4764b52..54c950a40 100644 --- a/include/cstd.h +++ b/include/cstd.h @@ -59,6 +59,7 @@ #include #include #include +#include #else /* !__cplusplus */ /* for FILE */ diff --git a/include/decl.h b/include/decl.h index 72f85eb90..f66c0f5a8 100644 --- a/include/decl.h +++ b/include/decl.h @@ -240,6 +240,7 @@ struct instance_globals_c { /* decl.c */ char chosen_windowtype[WINTYPELEN]; int cmd_key; /* parse() / rhack() */ + struct Cmd_bind *cmd_bind; cmdcount_nht command_count; /* some objects need special handling during destruction or placement */ struct obj *current_wand; /* wand currently zapped/applied */ @@ -475,6 +476,7 @@ struct instance_globals_i { /* invent.c */ char *invbuf; unsigned invbufsiz; + boolean item_action_in_progress; int in_sync_perminvent; /* mon.c */ @@ -723,7 +725,8 @@ struct instance_globals_o { /* options.c */ - int opt_phase; /* builtin_opt, syscf_, rc_file_, environ_, play_opt */ + /* builtin_opt, syscf_, rc_file_, environ_, play_opt */ + enum option_phases opt_phase; boolean opt_initial; boolean opt_from_file; boolean opt_need_redraw; /* for doset() */ @@ -837,6 +840,9 @@ struct instance_globals_r { struct instance_globals_s { + /* allmain.c */ + boolean saving_grace_turn; /* saving grace was triggered this turn */ + /* artifact.c */ int spec_dbon_applies; /* coordinate effects from spec_dbon() with messages in artifact_hit() */ @@ -968,6 +974,9 @@ struct instance_globals_t { struct instance_globals_u { + /* allmain.c */ + int uhp_at_start_of_monster_turn; + /* botl.c */ boolean update_all; diff --git a/include/dgn_file.h b/include/dgn_file.h index 106de5655..4a94e0cdc 100644 --- a/include/dgn_file.h +++ b/include/dgn_file.h @@ -43,7 +43,7 @@ struct tmpbranch { }; /* - * Values for type for tmpbranch structure. + * Values for type in tmpbranch structure. */ #define TBR_STAIR 0 /* connection with both ends having a staircase */ #define TBR_NO_UP 1 /* connection with no up staircase */ diff --git a/include/dungeon.h b/include/dungeon.h index d4a1dcf62..f364bcd8e 100644 --- a/include/dungeon.h +++ b/include/dungeon.h @@ -169,7 +169,7 @@ struct linfo { /* 0x02 was FORGOTTEN, when amnesia made you forget maps */ #define LFILE_EXISTS 0x04 /* a level file exists for this level */ /* Note: VISITED and LFILE_EXISTS are currently almost always - * set at the same time. However they _mean_ different things. + * set at the same time. However, they _mean_ different things. */ }; diff --git a/include/engrave.h b/include/engrave.h index 47fd84783..33ee19c80 100644 --- a/include/engrave.h +++ b/include/engrave.h @@ -13,6 +13,8 @@ enum engraving_texts { text_states }; +#define engr_text_space(ep) ((char *) ((ep) + 1)) + struct engr { struct engr *nxt_engr; char *engr_txt[text_states]; diff --git a/include/extern.h b/include/extern.h index 17923a5d2..d3c295a23 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 extern.h $NHDT-Date: 1738638877 2025/02/03 19:14:37 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1476 $ */ +/* NetHack 3.7 extern.h $NHDT-Date: 1770949988 2026/02/12 18:33:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1523 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -71,6 +71,10 @@ #include "artifact.h" #endif +#ifndef MFNDPOS_H +#include "mfndpos.h" +#endif + /* ### alloc.c ### */ #if 0 @@ -101,7 +105,6 @@ extern void stop_occupation(void); extern void init_sound_disp_gamewindows(void); extern void newgame(void); extern void welcome(boolean); -extern int argcheck(int, char **, enum earlyarg); extern long timet_to_seconds(time_t); extern long timet_delta(time_t, time_t); @@ -258,6 +261,7 @@ extern void free_ebones(struct monst *) NONNULLARG1; /* ### botl.c ### */ +extern char *get_strength_str(void); extern char *do_statusline1(void); extern void check_gold_symbol(void); extern char *do_statusline2(void); @@ -331,6 +335,15 @@ extern boolean parse_conf_file(FILE *fp, boolean (*proc)(char *arg)); extern void set_configfile_name(const char *); extern char *get_configfile(void); extern const char *get_default_configfile(void); +extern void rcfile(void); +extern void rcfile_interface_options(void); +extern void heed_all_config_statements(void); +extern void disregard_all_config_statements(void); +extern void heed_this_config_statement(int); +extern void disregard_this_config_statement(int); +extern boolean config_unmatched_ignored(void); +extern void clear_ignore_errors_on_unmatched(void); +extern void set_ignore_errors_on_unmatched(void); /* ### coloratt.c ### */ @@ -365,6 +378,8 @@ extern void change_palette(void); /* ### cmd.c ### */ +extern void cmdbind_freeall(void); +extern int dotoggleoption(void); extern void set_move_cmd(int, int); extern int do_move_west(void); extern int do_move_northwest(void); @@ -442,10 +457,10 @@ extern int doextlist(void); extern int extcmd_via_menu(void); extern int enter_explore_mode(void); extern boolean bind_mousebtn(int, const char *); -extern boolean bind_key(uchar, const char *); +extern boolean bind_key(uchar, const char *, boolean); extern void dokeylist(void); -extern int xytod(coordxy, coordxy); -extern void dtoxy(coord *, int); +extern int xytodir(int, int); +extern void dirtocoord(coord *, int); extern int movecmd(char, int); extern int dxdy_moveok(void); extern int getdir(const char *); @@ -746,6 +761,7 @@ extern void Ring_gone(struct obj *) NONNULLARG1; extern void Blindf_on(struct obj *) NONNULLARG1; extern void Blindf_off(struct obj *); extern int dotakeoff(void); +extern int ia_dotakeoff(void); extern int doremring(void); extern int cursed(struct obj *); extern int armoroff(struct obj *); @@ -760,7 +776,8 @@ extern struct obj *unchanger(void); extern void reset_remarm(void); extern int doddoremarm(void); extern int remarm_swapwep(void); -extern int destroy_arm(struct obj *); +extern int disintegrate_arm(struct obj *); +extern int destroy_arm(void); extern void adj_abon(struct obj *, schar) NONNULLARG1; extern boolean inaccessible_equipment(struct obj *, const char *, boolean); extern int any_worn_armor_ok(struct obj *); @@ -823,6 +840,7 @@ extern void hurtle(int, int, int, boolean); extern void mhurtle(struct monst *, int, int, int) NONNULLARG1; extern boolean harmless_missile(struct obj *) NONNULLARG1; extern boolean throwing_weapon(struct obj *) NONNULLARG1; +extern boolean throwit_mon_hit(struct obj *, struct monst *) NONNULLARG1; extern void throwit(struct obj *, long, boolean, struct obj *) NONNULLARG1; extern int omon_adj(struct monst *, struct obj *, boolean) NONNULLPTRS; extern boolean should_mulch_missile(struct obj *); @@ -910,6 +928,14 @@ extern void overview_stats(winid, const char *, long *, long *) NONNULLPTRS; extern void remdun_mapseen(int); extern const char *endgamelevelname(char *, int); +/* ### earlyarg.c ### */ + +extern int argcheck(int, char **, enum earlyarg); +extern void early_options(int *argc_p, char ***argv_p, char **hackdir_p); +#ifdef WIN32 +int windows_early_options(const char *); +#endif + /* ### eat.c ### */ extern void eatmupdate(void); @@ -1155,6 +1181,7 @@ extern void dump_glyphids(void); extern void clear_all_glyphmap_colors(void); extern void reset_customcolors(void); extern int glyph_to_cmap(int); +extern void maybe_shuffle_customizations(void); /* ### hack.c ### */ @@ -1233,6 +1260,10 @@ extern boolean pmatchi(const char *, const char *) NONNULLPTRS; extern boolean pmatchz(const char *, const char *) NONNULLPTRS; */ +/* ### iactions.c ### */ + +extern int itemactions(struct obj *otmp) NONNULLARG1; + /* ### insight.c ### */ extern int doattributes(void); @@ -1356,6 +1387,7 @@ extern void sync_perminvent(void); extern void perm_invent_toggled(boolean negated); extern void prepare_perminvent(winid window); extern struct obj *carrying_stoning_corpse(void); +extern void repopulate_perminvent(void); /* ### ioctl.c ### */ @@ -1531,7 +1563,7 @@ extern int monster_census(boolean); extern int msummon(struct monst *); extern void summon_minion(aligntyp, boolean); extern int demon_talk(struct monst *) NONNULLARG1; -extern long bribe(struct monst *) NONNULLARG1; +extern long bribe(struct monst *, const char *) NONNULLARG12; extern int dprince(aligntyp); extern int dlord(aligntyp); extern int llord(void); @@ -1654,6 +1686,7 @@ extern struct obj *mk_named_object(int, struct permonst *, coordxy, coordxy, const char *) ; extern struct obj *rnd_treefruit_at(coordxy, coordxy); +extern boolean is_treefruit(struct obj *) NONNULLARG1; extern void set_corpsenm(struct obj *, int) NONNULLARG1; extern long rider_revival_time(struct obj *, boolean) NONNULLARG1; extern void start_corpse_timeout(struct obj *) NONNULLARG1; @@ -1743,7 +1776,7 @@ extern boolean can_touch_safely(struct monst *, struct obj *) NONNULLARG12; extern int can_carry(struct monst *, struct obj *) NONNULLARG12; extern long mon_allowflags(struct monst *) NONNULLARG1; extern boolean m_in_air(struct monst *) NONNULLARG1; -extern int mfndpos(struct monst *, coord *, long *, long) NONNULLPTRS; +extern int mfndpos(struct monst *, struct mfndposdata *, long) NONNULLPTRS; extern boolean monnear(struct monst *, coordxy, coordxy) NONNULLARG1; extern void dmonsfree(void); extern void elemental_clog(struct monst *) NONNULLARG1; @@ -1913,8 +1946,8 @@ extern boolean accessible(coordxy, coordxy); extern void set_apparxy(struct monst *) NONNULLARG1; extern boolean can_ooze(struct monst *) NONNULLARG1; extern boolean can_fog(struct monst *) NONNULLARG1; -extern boolean should_displace(struct monst *, coord *, long *, int, coordxy, - coordxy) NONNULLPTRS; +extern boolean should_displace(struct monst *, const struct mfndposdata *, + coordxy, coordxy) NONNULLPTRS; extern boolean undesirable_disp(struct monst *, coordxy, coordxy) NONNULLARG1; extern boolean can_hide_under_obj(struct obj *); @@ -1952,7 +1985,7 @@ extern long filesize(char *); #endif /* MSDOS */ extern char *foundfile_buffer(void); #endif /* __GO32__ */ -extern void chdrive(char *); +extern void chdrive(const char *); #ifndef TOS extern void disable_ctrlP(void); extern void enable_ctrlP(void); @@ -2151,7 +2184,8 @@ extern boolean objdescr_is(struct obj *, const char *) NONNULLARG2; extern void oinit(void); extern void savenames(NHFILE *) NONNULLARG1; extern void restnames(NHFILE *) NONNULLARG1; -extern void discover_object(int, boolean, boolean); +extern void observe_object(struct obj *) NONNULLARG1; +extern void discover_object(int, boolean, boolean, boolean); extern void undiscover_object(int); extern boolean interesting_to_discover(int); extern int choose_disco_sort(int); @@ -2268,6 +2302,7 @@ extern char *get_option_value(const char *, boolean) NONNULLARG1; extern int doset_simple(void); extern int doset(void); extern int dotogglepickup(void); +extern int toggle_bool_option(const char *); extern void option_help(void); extern void all_options_strbuf(strbuf_t *) NONNULLARG1; extern void next_opt(winid, const char *) NONNULLARG2; @@ -2294,6 +2329,11 @@ extern int msgtype_type(const char *, boolean) NONNULLARG1; extern void hide_unhide_msgtypes(boolean, int); extern void msgtype_free(void); extern void options_free_window_colors(void); +extern void heed_all_options(void); +extern void disregard_all_options(void); +extern void heed_this_option(enum opt); +extern void disregard_this_option(enum opt); +extern void clear_ignore_errors_on_unmatched(void); #ifdef TTY_PERM_INVENT extern void check_perm_invent_again(void); #endif @@ -2319,12 +2359,13 @@ extern int dowhatdoes(void); extern char *dowhatdoes_core(char, char *) NONNULLARG2; /*might return NULL*/ extern int dohelp(void); extern int dohistory(void); +void allopt_array_init(void); /* ### xxmain.c ### */ -#if defined(MICRO) || defined(WIN32) +#if defined(UNIX) || defined(MICRO) || defined(WIN32) #ifdef CHDIR -extern void chdirx(char *, boolean); +extern void chdirx(const char *, boolean); #endif /* CHDIR */ extern boolean authorize_wizard_mode(void); extern boolean authorize_explore_mode(void); @@ -2401,8 +2442,9 @@ extern int query_category(const char *, struct obj *, int, menu_item **, int) NO /* dotypeinv() call query_objlist with NULL arg1 */ extern int query_objlist(const char *, struct obj **, int, menu_item **, int, boolean(*)(struct obj *)) NONNULLARG24; +extern boolean reroll_menu(void); extern struct obj *pick_obj(struct obj *) NONNULLARG1; -extern int encumber_msg(void); +extern void encumber_msg(void); extern int container_at(coordxy, coordxy, boolean); extern int doloot(void); extern void observe_quantum_cat(struct obj *, boolean, boolean) NONNULLARG1; @@ -2694,6 +2736,9 @@ void restore_gamelog(NHFILE *); boolean restgamestate(NHFILE *); void restore_msghistory(NHFILE *); #endif +extern void rest_adjust_levelflags(void); +extern void moves_to_relative_time(long *); +extern void relative_time_to_moves(long *); /* ### rip.c ### */ @@ -2846,6 +2891,8 @@ extern void bclose(int); /* setpaid() has a conditional code block near the end of the function, where arg1 is tested for NULL, preventing NONNULLARG1 */ extern void setpaid(struct monst *) NO_NNARGS; +extern void record_price_quote(int, unsigned long, boolean); +extern void append_price_quote(char *, char **, int) NONNULLARG12; extern long money2mon(struct monst *, long) NONNULLARG1; extern void money2u(struct monst *, long) NONNULLARG1; extern void shkgone(struct monst *) NONNULLARG1; @@ -2923,6 +2970,8 @@ extern void globby_bill_fixup(struct obj *, struct obj *) NONNULLARG12; extern void credit_report(struct monst *shkp, int idx, boolean silent) NONNULLARG1; extern void use_unpaid_trapobj(struct obj *, coordxy, coordxy) NONNULLARG1; +extern void noisy_shop(struct mkroom *); + /* ### shknam.c ### */ @@ -3019,6 +3068,8 @@ extern int nhl_abs_coord(lua_State *) NONNULLARG1; extern void update_croom(void); extern const char *get_trapname_bytype(int); extern void l_register_des(lua_State *) NONNULLARG1; +extern int get_table_objclass(lua_State *) NONNULLARG1; +extern int get_table_objtype(lua_State *) NONNULLARG1; #endif /* !CROSSCOMPILE || CROSSCOMPILE_TARGET */ /* ### spell.c ### */ @@ -3035,6 +3086,7 @@ extern int spelleffects(int, boolean, boolean); extern int tport_spell(int); extern void losespells(void); extern int dovspell(void); +extern void show_spells(void); extern void initialspell(struct obj *) NONNULLARG1; extern int known_spell(short); extern int spell_idx(short); @@ -3305,7 +3357,9 @@ extern void check_twitch(void); /* ### u_init.c ### */ -extern void u_init(void); +extern void u_init_misc(void); +extern void u_init_inventory_attrs(void); +extern void u_init_skills_discoveries(void); /* ### uhitm.c ### */ @@ -3417,6 +3471,8 @@ extern int passive(struct monst *, struct obj *, boolean, boolean, uchar, extern void passive_obj(struct monst *, struct obj *, struct attack *) NONNULLARG1; extern void that_is_a_mimic(struct monst *, unsigned) NONNULLARG1; extern void stumble_onto_mimic(struct monst *) NONNULLARG1; +extern boolean disguised_as_non_mon(struct monst *) NONNULLARG1; +extern boolean disguised_as_mon(struct monst *) NONNULLARG1; extern int flash_hits_mon(struct monst *, struct obj *) NONNULLARG12; extern void light_hits_gremlin(struct monst *, int) NONNULLARG1; @@ -3433,8 +3489,8 @@ extern void append_slash(char *) NONNULLARG1; extern boolean check_user_string(const char *) NONNULLARG1; extern char *get_login_name(void); extern unsigned long sys_random_seed(void); -ATTRNORETURN extern void after_opt_showpaths(const char *) NORETURN; #endif /* UNIX */ +ATTRNORETURN extern void after_opt_showpaths(const char *) NORETURN; /* ### unixtty.c ### */ @@ -3668,6 +3724,7 @@ extern void dry_a_towel(struct obj *, int, boolean) NONNULLARG1; extern char *skill_level_name(int, char *) NONNULLARG2; extern const char *skill_name(int); extern boolean can_advance(int, boolean); +extern void show_skills(void); extern int enhance_weapon_skill(void); extern void unrestrict_weapon_skill(int); extern void use_skill(int, int); @@ -3832,6 +3889,7 @@ extern void wizcustom_callback(winid win, int glyphnum, char *id); #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) extern int wiz_display_macros(void); extern int wiz_mon_diff(void); +extern int wiz_objprobs(void); #endif extern void sanity_check(void); @@ -3869,9 +3927,11 @@ extern void setworn(struct obj *, long) NO_NNARGS; /* has tests for obj */ extern void setnotworn(struct obj *) NO_NNARGS; /* has tests for obj */ extern void allunworn(void); extern struct obj *wearmask_to_obj(long); +extern int wornmask_to_armcat(long); +extern long armcat_to_wornmask(int); extern long wearslot(struct obj *) NONNULLARG1; extern void check_wornmask_slots(void); -extern void mon_set_minvis(struct monst *) NONNULLARG1; +extern void mon_set_minvis(struct monst *, boolean) NONNULLARG1; extern void mon_adjust_speed(struct monst *, int, struct obj *) NONNULLARG1; extern void update_mon_extrinsics(struct monst *, struct obj *, boolean, boolean) NONNULLARG12; @@ -3937,16 +3997,17 @@ extern int spell_damage_bonus(int); extern const char *exclam(int force) NONNULL; extern void hit(const char *, struct monst *, const char *) NONNULLPTRS; extern void miss(const char *, struct monst *) NONNULLPTRS; -extern struct monst *bhit(coordxy, coordxy, int, enum bhit_call_types, +extern struct monst *bhit(int, int, int, enum bhit_call_types, int(*)(struct monst *, struct obj *), int(*)(struct obj *, struct obj *), struct obj **) NONNULLARG7; -extern struct monst *boomhit(struct obj *, coordxy, coordxy) NONNULLARG1; +extern struct monst *boomhit(struct obj *, int, int) NONNULLARG1; extern int zhitm(struct monst *, int, int, struct obj **) NONNULLPTRS; extern int burn_floor_objects(coordxy, coordxy, boolean, boolean); extern void ubuzz(int, int); extern void buzz(int, int, coordxy, coordxy, int, int); -extern void dobuzz(int, int, coordxy, coordxy, int, int, boolean); +extern void dobuzz(int, int, coordxy, coordxy, int, int, + boolean, boolean, boolean); extern void melt_ice(coordxy, coordxy, const char *) NO_NNARGS; extern void start_melt_ice_timeout(coordxy, coordxy, long); extern void melt_ice_away(union any *, long) NONNULLARG1; @@ -3960,6 +4021,8 @@ extern boolean inventory_resistance_check(int); extern char *item_what(int); extern int destroy_items(struct monst *, int, int) NONNULLARG1; extern int resist(struct monst *, char, int, int) NONNULLARG1; +extern void wish_history_add(char *); +extern void wish_history_flush(void); extern void makewish(void); extern const char *flash_str(int, boolean) NONNULL; diff --git a/include/flag.h b/include/flag.h index bd6a5dc58..c251236f0 100644 --- a/include/flag.h +++ b/include/flag.h @@ -254,6 +254,7 @@ struct instance_flags { boolean remember_getpos; /* save getpos() positioning in do-again queue */ boolean sad_feeling; /* unseen pet is dying */ boolean showdamage; /* extra message reporting damage hero has taken */ + boolean pending_customizations; /* at least one custom. was specified */ xint8 debug_fuzzer; /* fuzz testing */ int at_midnight; /* only valid during end of game disclosure */ int at_night; /* also only valid during end of game disclosure */ @@ -292,6 +293,7 @@ struct instance_flags { boolean debug_overwrite_stairs; /* debug: allow overwriting stairs */ boolean debug_mongen; /* debug: prevent monster generation */ boolean debug_hunger; /* debug: prevent hunger */ + boolean debug_prevent_pline; /* debug: prevent pline going to UI */ boolean mon_polycontrol; /* debug: control monster polymorphs */ boolean mon_telecontrol; /* debug: control monster teleports */ boolean in_dumplog; /* doing the dumplog right now? */ @@ -328,6 +330,7 @@ struct instance_flags { boolean num_pad; /* use numbers for movement commands */ boolean perm_invent; /* display persistent inventory window */ boolean perm_invent_pending; /* need to try again */ + boolean pricequotes; /* display price quotes on unIDd objects */ boolean renameallowed; /* can change hero name during role selection */ boolean renameinprogress; /* we are changing hero name */ boolean sounds; /* master on/off switch for using soundlib */ @@ -481,7 +484,7 @@ struct instance_flags { }; /* - * Old deprecated names + * Old, deprecated names */ #ifdef TTY_GRAPHICS #define eight_bit_tty wc_eight_bit_input diff --git a/include/func_tab.h b/include/func_tab.h index 2924b3bf3..e5438ce58 100644 --- a/include/func_tab.h +++ b/include/func_tab.h @@ -22,6 +22,7 @@ #define MOUSECMD 0x0800 /* cmd allowed to be bound to mouse button */ #define CMD_INSANE 0x1000 /* suppress sanity check (for ^P and ^R) */ #define AUTOCOMP_ADJ 0x2000 /* user changed command autocompletion */ +#define CMD_PARAM 0x4000 /* command requires a param from key bind */ /* flags for extcmds_match() */ #define ECM_NOFLAGS 0 @@ -29,6 +30,15 @@ #define ECM_EXACTMATCH 0x02 /* needs exact match of findstr */ #define ECM_NO1CHARCMD 0x04 /* ignore commands like '?' and '#' */ +/* a key bound to ext_func_tab */ +struct Cmd_bind { + uchar key; + boolean userbind; /* added by user */ + char *param; + const struct ext_func_tab *cmd; + struct Cmd_bind *next; +}; + struct ext_func_tab { uchar key; const char *ef_txt, *ef_desc; diff --git a/include/global.h b/include/global.h index 6d785ac9a..47e783742 100644 --- a/include/global.h +++ b/include/global.h @@ -102,18 +102,20 @@ typedef unsigned readLenType; #endif #define BOOL_RANDOM (-1) -enum optchoice { opt_in, opt_out}; - /* * type nhsym: loadable symbols go into this type */ typedef uchar nhsym; #ifndef STRNCMPI -#ifndef __SASC_60 /* SAS/C already shifts to stricmp */ +/* SAS/C already shifts to stricmp */ +#if !defined(__SASC_60) && !defined(CROSS_TO_AMIGA) #define strcmpi(a, b) strncmpi((a), (b), -1) #endif #endif +#ifdef CROSS_TO_AMIGA +#define strcmpi(a, b) stricmp(a, b) +#endif /* #define SPECIALIZATION */ /* do "specialized" version of new topology */ @@ -571,6 +573,45 @@ typedef enum NHL_pcall_action { NHLpa_impossible } NHL_pcall_action; +enum optchoice { opt_in, opt_out}; +/* + * option setting restrictions + */ +enum optset_restrictions { + set_in_sysconf = 0, /* system config file option only */ + set_in_config = 1, /* config file option only */ + set_viaprog = 2, /* may be set via extern program, not seen in game */ + set_gameview = 3, /* may be set via extern program, displayed in game */ + set_in_game = 4, /* may be set via extern program or set in the game */ + set_wizonly = 5, /* may be set in the game if wizmode */ + set_wiznofuz = 6, /* wizard-mode only, but not by fuzzer */ + set_hidden = 7 /* placeholder for prefixed entries, never show it */ +}; + +/* these aren't the same as set_xxx */ +enum option_phases { + phase_not_set = 0, + builtin_opt = 1, /* compiled-in default value of an option */ + syscf_opt, /* sysconf setting of an option, overrides builtin */ + rc_file_opt, /* player's run-time config file setting, overrides syscf */ + environ_opt, /* player's environment NETHACKOPTIONS, overrides rc_file */ + cmdline_opt, /* program invocation command-line, overrides environ */ + play_opt, /* 'O' command, interactively set so overrides all */ + num_opt_phases +}; + +#define SET__IS_VALUE_VALID(s) ((s < set_in_sysconf) || (s > set_wiznofuz)) +#include "optlist.h" +enum opt { + opt_prefix_only = -1, +#define NHOPT_ENUM +#include "optlist.h" +#undef NHOPT_ENUM + OPTCOUNT +}; + + + #define SFCTOOL_BIT (1UL << 30) #endif /* GLOBAL_H */ diff --git a/include/hack.h b/include/hack.h index 361b95cd3..f655a8961 100644 --- a/include/hack.h +++ b/include/hack.h @@ -252,7 +252,7 @@ struct cmd { boolean swap_yz; /* QWERTZ keyboards; use z to move NW, y to zap */ const char *dirchars; /* current movement/direction characters */ const char *alphadirchars; /* same as dirchars if !numpad */ - const struct ext_func_tab *commands[256]; /* indexed by input character */ + struct Cmd_bind *cmdbinds; const struct ext_func_tab *mousebtn[NUM_MOUSE_BUTTONS]; char spkeys[NUM_NHKF]; char extcmd_char; /* key that starts an extended command ('#') */ @@ -571,6 +571,11 @@ enum hunger_state_types { STARVED = 6 }; +/* fake inventory letters, not 'a'..'z' or 'A'..'Z' */ +#define NOINVSYM '#' /* overflow because all 52 letters are in use */ +#define CONTAINED_SYM '>' /* designator for inside a container */ +#define HANDS_SYM '-' /* hands|fingers|self depending on context */ + /* inventory counts (slots in tty parlance) * a...zA..Z invlet_basic (52) * $a...zA..Z# 2 special additions @@ -697,22 +702,6 @@ enum nhcb_calls { NUM_NHCB }; -/* - * option setting restrictions - */ - -enum optset_restrictions { - set_in_sysconf = 0, /* system config file option only */ - set_in_config = 1, /* config file option only */ - set_viaprog = 2, /* may be set via extern program, not seen in game */ - set_gameview = 3, /* may be set via extern program, displayed in game */ - set_in_game = 4, /* may be set via extern program or set in the game */ - set_wizonly = 5, /* may be set in the game if wizmode */ - set_wiznofuz = 6, /* wizard-mode only, but not by fuzzer */ - set_hidden = 7 /* placeholder for prefixed entries, never show it */ -}; -#define SET__IS_VALUE_VALID(s) ((s < set_in_sysconf) || (s > set_wiznofuz)) - struct plinemsg_type { xint16 msgtype; /* one of MSGTYP_foo */ struct nhregex *regex; @@ -776,7 +765,7 @@ struct selectionvar { /* structure for 'program_state'; not saved and restored */ struct sinfo { - int gameover; /* self explanatory? */ + int gameover; /* self-explanatory? */ int stopprint; /* inhibit further end of game disclosure */ #ifdef HANGUPHANDLING volatile int done_hup; /* SIGHUP or moral equivalent received @@ -812,6 +801,7 @@ struct sinfo { interface to suppress menu commands in similar conditions; readchar() always resets it to 'otherInp' prior to returning */ int input_state; /* whether next key pressed will be entering a command */ + int early_options; /* inside early_options processing */ #ifdef TTY_GRAPHICS /* resize_pending only matters when handling a SIGWINCH signal for tty; getting_char is used along with that and also separately for UNIX; @@ -1345,7 +1335,7 @@ typedef uint32_t mmflags_nht; /* makemon MM_ flags */ /* Macros for launching objects */ #define ROLL 0x01 /* the object is rolling */ -#define FLING 0x02 /* the object is flying thru the air */ +#define FLING 0x02 /* the object is flying through the air */ #define LAUNCH_UNSEEN 0x40 /* hero neither caused nor saw it */ #define LAUNCH_KNOWN 0x80 /* the hero caused this by explicit action */ @@ -1527,7 +1517,7 @@ typedef uint32_t mmflags_nht; /* makemon MM_ flags */ (objects[(obj)->otyp].a_ac + (obj)->spe \ - min((int) greatest_erosion(obj), objects[(obj)->otyp].a_ac)) -#define makeknown(x) discover_object((x), TRUE, TRUE) +#define makeknown(x) discover_object((x), TRUE, TRUE, TRUE) #define distu(xx, yy) dist2((coordxy) (xx), (coordxy) (yy), u.ux, u.uy) #define mdistu(mon) distu((mon)->mx, (mon)->my) #define onlineu(xx, yy) online2((coordxy)(xx), (coordxy)(yy), u.ux, u.uy) diff --git a/include/mcastu.h b/include/mcastu.h new file mode 100644 index 000000000..53a37070e --- /dev/null +++ b/include/mcastu.h @@ -0,0 +1,39 @@ + + +#define MCF_NONE 0x0000 +#define MCF_INDIRECT 0x0001 /* untargeted/indirect spell */ +#define MCF_SIGHT 0x0002 /* monster needs to see hero */ +#define MCF_HOSTILE 0x0004 /* cast by hostile monsters only */ + +#if defined(MCASTU_ENUM) +#define MONSPELL(def, lvl, flags) MCAST_##def +#elif defined(MCASTU_INIT) +#define MONSPELL(def, lvl, flags) { lvl, flags } +#elif defined(DUMP_MCASTU_ENUM1) +#define MONSPELL(def, lvl, flags) MCAST_DUMPENUM_##def +#elif defined(DUMP_MCASTU_ENUM2) +#define MONSPELL(def, lvl, flags) { MCAST_DUMPENUM_##def, #def } +#endif + +MONSPELL(PSI_BOLT, 0, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(OPEN_WOUNDS, 0, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(CURE_SELF, 1, MCF_INDIRECT), +MONSPELL(HASTE_SELF, 2, MCF_INDIRECT), +MONSPELL(CONFUSE_YOU, 2, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(STUN_YOU, 3, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(DISAPPEAR, 4, MCF_INDIRECT), +MONSPELL(PARALYZE, 4, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(BLIND_YOU, 6, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(WEAKEN_YOU, 6, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(DESTRY_ARMR, 8, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(INSECTS, 8, MCF_HOSTILE|MCF_INDIRECT|MCF_SIGHT), +MONSPELL(CURSE_ITEMS, 10, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(LIGHTNING, 11, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(FIRE_PILLAR, 12, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(GEYSER, 13, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(AGGRAVATION, 13, MCF_INDIRECT|MCF_HOSTILE|MCF_SIGHT), +MONSPELL(SUMMON_MONS, 15, MCF_HOSTILE|MCF_INDIRECT|MCF_SIGHT), +MONSPELL(CLONE_WIZ, 18, MCF_HOSTILE|MCF_INDIRECT|MCF_SIGHT), +MONSPELL(DEATH_TOUCH, 20, MCF_HOSTILE|MCF_SIGHT), + +#undef MONSPELL diff --git a/include/mextra.h b/include/mextra.h index 6cafb4890..8472a5ad2 100644 --- a/include/mextra.h +++ b/include/mextra.h @@ -98,6 +98,7 @@ struct epri { schar shroom; /* index in rooms */ coord shrpos; /* position of shrine */ d_level shrlevel; /* level (& dungeon) of shrine */ + unsigned cheapskate_count; /* number of cheapskate donations */ long intone_time, /* used to limit verbosity +*/ enter_time, /*+ of temple entry messages */ hostile_time, /* forbidding feeling */ diff --git a/include/mfndpos.h b/include/mfndpos.h index 762f5cdb9..a1ec75461 100644 --- a/include/mfndpos.h +++ b/include/mfndpos.h @@ -18,9 +18,9 @@ #define UNLOCKDOOR 0x00800000L /* unlocks locked doors */ #define BUSTDOOR 0x01000000L /* breaks any doors */ #define ALLOW_ROCK 0x02000000L /* pushes rocks */ -#define ALLOW_WALL 0x04000000L /* walks thru walls */ +#define ALLOW_WALL 0x04000000L /* walks through walls */ #define ALLOW_DIG 0x08000000L /* digs */ -#define ALLOW_BARS 0x10000000L /* may pass thru iron bars */ +#define ALLOW_BARS 0x10000000L /* may pass through iron bars */ #define ALLOW_SANCT 0x20000000L /* enters temples */ #define ALLOW_SSM 0x40000000L /* ignores scare monster */ #ifdef NHSTDC @@ -30,4 +30,10 @@ #endif /* clang-format on */ +struct mfndposdata { + int cnt; + coord poss[9]; + long info[9]; +}; + #endif /* MFNDPOS_H */ diff --git a/include/monattk.h b/include/monattk.h index 04fbef021..912d93284 100644 --- a/include/monattk.h +++ b/include/monattk.h @@ -53,7 +53,7 @@ #define AD_BLND 11 /* blinds (yellow light) */ #define AD_STUN 12 /* stuns */ #define AD_SLOW 13 /* slows */ -#define AD_PLYS 14 /* paralyses */ +#define AD_PLYS 14 /* paralyzes */ #define AD_DRLI 15 /* drains life levels (Vampire) */ #define AD_DREN 16 /* drains magic energy */ #define AD_LEGS 17 /* damages legs (xan) */ @@ -101,7 +101,7 @@ struct mhitm_data { }; /* - * Monster to monster attacks. When a monster attacks another (mattackm), + * Monster-to-monster attacks. When a monster attacks another (mattackm), * any or all of the following can be returned. See mattackm() for more * details. */ diff --git a/include/mondata.h b/include/mondata.h index 80a177f76..430b5e83d 100644 --- a/include/mondata.h +++ b/include/mondata.h @@ -173,7 +173,7 @@ as unique even though they really aren't; that's ok here */ #define unique_corpstat(ptr) (((ptr)->geno & G_UNIQ) != 0) -/* this returns the light's range, or 0 if none; if we add more light emitting +/* this returns the light's range, or 0 if none; if we add more light-emitting monsters, we'll likely have to add a new light range field to mons[] */ #define emits_light(ptr) \ (((ptr)->mlet == S_LIGHT || (ptr) == &mons[PM_FLAMING_SPHERE] \ diff --git a/include/monflag.h b/include/monflag.h index 43400fea9..18c2e4256 100644 --- a/include/monflag.h +++ b/include/monflag.h @@ -85,9 +85,9 @@ enum ms_sounds { #define M1_FLY 0x00000001L /* can fly or float */ #define M1_SWIM 0x00000002L /* can traverse water */ #define M1_AMORPHOUS 0x00000004L /* can flow under doors */ -#define M1_WALLWALK 0x00000008L /* can phase thru rock */ +#define M1_WALLWALK 0x00000008L /* can phase through rock */ #define M1_CLING 0x00000010L /* can cling to ceiling */ -#define M1_TUNNEL 0x00000020L /* can tunnel thru rock */ +#define M1_TUNNEL 0x00000020L /* can tunnel through rock */ #define M1_NEEDPICK 0x00000040L /* needs pick to tunnel */ #define M1_CONCEAL 0x00000080L /* hides under objects */ #define M1_HIDE 0x00000100L /* mimics, blends in with ceiling */ diff --git a/include/monst.h b/include/monst.h index 15114c600..8f189b4b7 100644 --- a/include/monst.h +++ b/include/monst.h @@ -162,6 +162,9 @@ struct monst { Bitfield(meverseen, 1); /* mon has been seen at some point */ Bitfield(mspotted, 1); /* mon is currently seen by hero */ + Bitfield(mwandexp, 1); /* mon has experience with wands */ + Bitfield(mgenmklev, 1); /* made by the level generation */ + /* 5 spare bits */ unsigned long mstrategy; /* for monsters with mflag3: current strategy */ #ifdef NHSTDC diff --git a/include/monsters.h b/include/monsters.h index d13cc9178..c7605d3b6 100644 --- a/include/monsters.h +++ b/include/monsters.h @@ -1600,7 +1600,7 @@ M2_STRONG | M2_NEUTER, 0, 10, CLR_BROWN, EARTH_ELEMENTAL), MON(NAM("water elemental"), S_ELEMENTAL, - LVL(8, 6, 2, 30, 0), (G_NOCORPSE | 1), + LVL(8, 5, 2, 30, 0), (G_NOCORPSE | 1), A(ATTK(AT_CLAW, AD_PHYS, 5, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(2500, 0, MS_SILENT, MZ_HUGE), MR_POISON | MR_STONE, 0, @@ -3395,8 +3395,8 @@ /* monster priests are separate monsters (above; "aligned cleric") */ MON(NAMS("priest", "priestess", "cleric"), S_HUMAN, LVL(10, 12, 10, 2, 0), G_NOGEN, - A(ATTK(AT_WEAP, AD_PHYS, 1, 6), - NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_MAGC, AD_CLRC, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_HUMANOID, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_STRONG | M2_COLLECT, @@ -3451,8 +3451,8 @@ 12, HI_DOMESTIC, VALKYRIE), MON(NAM("wizard"), S_HUMAN, LVL(10, 12, 10, 3, 0), G_NOGEN, - A(ATTK(AT_WEAP, AD_PHYS, 1, 6), - NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_MAGC, AD_SPEL, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_HUMANOID, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_STRONG | M2_COLLECT | M2_MAGIC, diff --git a/include/obj.h b/include/obj.h index e23c5f457..d9ef83d21 100644 --- a/include/obj.h +++ b/include/obj.h @@ -107,7 +107,9 @@ struct obj { * or enchantment); many items have this preset if * they lack anything interesting to discover */ Bitfield(dknown, 1); /* description known (item seen "up close"); - * some types of items always have dknown set */ + * some types of items always have dknown set; + * use observe_object to set to TRUE so that the + * discoveries list is still correct */ Bitfield(bknown, 1); /* BUC (blessed/uncursed/cursed) known */ Bitfield(rknown, 1); /* rustproofing status known */ Bitfield(cknown, 1); /* for containers (including statues): the contents @@ -449,6 +451,7 @@ struct obj { #define BURIED_TOO 0x2 /* object erosion types */ +#define ERODE_NONE -1 #define ERODE_BURN 0 #define ERODE_RUST 1 #define ERODE_ROT 2 diff --git a/include/objclass.h b/include/objclass.h index 23c3a86f2..fa210f519 100644 --- a/include/objclass.h +++ b/include/objclass.h @@ -54,8 +54,8 @@ struct objclass { * otherwise, obj->dknown and obj->bknown * tell all, and obj->known should always * be set for proper merging behavior. */ - Bitfield(oc_pre_discovered, 1); /* already known at start of game; flagged - * as such when discoveries are listed */ + Bitfield(oc_encountered, 1); /* hero has observed such an item at least + once (perhaps without naming it) */ Bitfield(oc_magic, 1); /* inherently magical object */ Bitfield(oc_charged, 1); /* may have +n or (n) charges */ Bitfield(oc_unique, 1); /* special one-of-a-kind object */ @@ -76,9 +76,9 @@ struct objclass { #define IMMEDIATE 2 /* directional beam that doesn't ricochet */ #define RAY 3 /* beam that does bounce off walls */ /* overloaded oc_dir: strike mode bit mask for weapons and weptools */ -#define PIERCE 01 /* pointed weapon punctures target */ -#define SLASH 02 /* sharp weapon cuts target */ -#define WHACK 04 /* blunt weapon bashes target */ +#define PIERCE 1 /* pointed weapon punctures target */ +#define SLASH 2 /* sharp weapon cuts target */ +#define WHACK 4 /* blunt weapon bashes target */ Bitfield(oc_material, 5); /* one of obj_material_types */ schar oc_subtyp; @@ -91,7 +91,7 @@ struct objclass { uchar oc_color; /* color of the object */ short oc_prob; /* probability, used in mkobj() */ - unsigned short oc_weight; /* encumbrance (1 cn = 0.1 lb.) */ + unsigned oc_weight; /* encumbrance (1 cn = 0.1 lb.) */ short oc_cost; /* base cost in shops */ /* Check the AD&D rules! The FIRST is small monster damage. */ /* for weapons, and tools, rocks, and gems useful as weapons */ @@ -104,6 +104,11 @@ struct objclass { #define oc_level oc_oc2 /* books: spell level */ unsigned short oc_nutrition; /* food value */ + + unsigned long oc_sell_minseen; + unsigned long oc_sell_maxseen; + unsigned long oc_buy_minseen; + unsigned long oc_buy_maxseen; }; struct class_sym { @@ -146,7 +151,7 @@ enum objclass_syms { /* for mkobj() use ONLY! odd '-SPBOOK_CLASS' is in case of unsigned enums */ #define SPBOOK_no_NOVEL (0 - (int) SPBOOK_CLASS) -#define BURNING_OIL (MAXOCLASSES + 1) /* Can be used as input to explode. */ +#define BURNING_OIL (MAXOCLASSES + 1) /* Can be used as input to explode */ #define MON_EXPLODE (MAXOCLASSES + 2) /* Exploding monster (e.g. gas spore) */ #define TRAP_EXPLODE (MAXOCLASSES + 3) diff --git a/include/objects.h b/include/objects.h index 1108f14b0..41076db28 100644 --- a/include/objects.h +++ b/include/objects.h @@ -41,10 +41,14 @@ the second zero is oc_spare1 for padding between oc_tough and oc_dir */ #define BITS(nmkn,mrg,uskn,ctnr,mgc,chrg,uniq,nwsh,big,tuf,dir,sub,mtrl) \ nmkn,mrg,uskn,0,mgc,chrg,uniq,nwsh,big,tuf,0,dir,mtrl,sub /*cpp fodder*/ +/* note: 0UL-1UL is a method of expressing the largest possible + unsigned long value whilst working around a false-positive warning + in Microsoft Visual C (which assumes that a negative number was + intended despite the explicit U suffix) */ #define OBJECT(obj,bits,prp,sym,prob,dly,wt, \ cost,sdam,ldam,oc1,oc2,nut,color,sn) \ { 0, 0, (char *) 0, bits, prp, sym, dly, color, prob, wt, \ - cost, sdam, ldam, oc1, oc2, nut } + cost, sdam, ldam, oc1, oc2, nut, (0UL-1UL), 0, (0UL-1UL), 0 } #define MARKER(tag,sn) /*empty*/ #elif defined(OBJECTS_ENUM) @@ -451,12 +455,12 @@ HELM("fedora", NoDes, 1, 0, 0, 0, 0, 3, 1, 10, 0, CLOTH, CLR_BROWN, FEDORA), HELM("cornuthaum", "conical hat", - 0, 1, CLAIRVOYANT, 3, 1, 4, 80, 10, 1, CLOTH, CLR_BLUE, + 0, 1, CLAIRVOYANT, 5, 1, 4, 80, 10, 1, CLOTH, CLR_BLUE, /* name coined by devteam; confers clairvoyance for wizards, blocks clairvoyance if worn by role other than wizard */ CORNUTHAUM), HELM("dunce cap", "conical hat", - 0, 1, 0, 3, 1, 4, 1, 10, 0, CLOTH, CLR_BLUE, + 0, 1, 0, 5, 1, 4, 1, 10, 0, CLOTH, CLR_BLUE, /* sets Int and Wis to fixed value of 6, so actually provides protection against death caused by Int being drained below 3 */ DUNCE_CAP), @@ -464,7 +468,7 @@ HELM("dented pot", NoDes, 1, 0, 0, 2, 0, 10, 8, 9, 0, IRON, CLR_BLACK, DENTED_POT), HELM("helm of brilliance", "crystal helmet", - 0, 1, 0, 3, 1, 40, 50, 9, 0, GLASS, CLR_WHITE, + 0, 1, 0, 6, 1, 40, 50, 9, 0, GLASS, CLR_WHITE, /* used to be iron and shuffled as "etched helmet" but required special case for the effect of iron armor on spell casting */ HELM_OF_BRILLIANCE), @@ -473,13 +477,13 @@ HELM("helmet", "plumed helmet", 0, 0, 0, 10, 1, 30, 10, 9, 0, IRON, HI_METAL, HELMET), HELM("helm of caution", "etched helmet", - 0, 1, WARNING, 3, 1, 50, 50, 9, 0, IRON, CLR_GREEN, + 0, 1, WARNING, 6, 1, 50, 50, 9, 0, IRON, CLR_GREEN, HELM_OF_CAUTION), HELM("helm of opposite alignment", "crested helmet", - 0, 1, 0, 6, 1, 50, 50, 9, 0, IRON, HI_METAL, + 0, 1, 0, 10, 1, 50, 50, 9, 0, IRON, HI_METAL, HELM_OF_OPPOSITE_ALIGNMENT), HELM("helm of telepathy", "visored helmet", - 0, 1, TELEPAT, 2, 1, 50, 50, 9, 0, IRON, HI_METAL, + 0, 1, TELEPAT, 4, 1, 50, 50, 9, 0, IRON, HI_METAL, HELM_OF_TELEPATHY), /* suits of armor */ @@ -550,19 +554,19 @@ DRGN_ARMR("yellow dragon scales", 0, ACID_RES, 500, 7, CLR_YELLOW, #undef DRGN_ARMR /* other suits */ ARMOR("plate mail", NoDes, - 1, 0, 1, 0, 44, 5, 450, 600, 3, 2, ARM_SUIT, IRON, HI_METAL, + 1, 0, 1, 0, 40, 5, 450, 600, 3, 2, ARM_SUIT, IRON, HI_METAL, PLATE_MAIL), ARMOR("crystal plate mail", NoDes, 1, 0, 1, 0, 10, 5, 415, 820, 3, 2, ARM_SUIT, GLASS, CLR_WHITE, CRYSTAL_PLATE_MAIL), ARMOR("bronze plate mail", NoDes, - 1, 0, 1, 0, 25, 5, 450, 400, 4, 1, ARM_SUIT, COPPER, HI_COPPER, + 1, 0, 1, 0, 23, 5, 450, 400, 4, 1, ARM_SUIT, COPPER, HI_COPPER, BRONZE_PLATE_MAIL), ARMOR("splint mail", NoDes, - 1, 0, 1, 0, 62, 5, 400, 80, 4, 1, ARM_SUIT, IRON, HI_METAL, + 1, 0, 1, 0, 57, 5, 400, 80, 4, 1, ARM_SUIT, IRON, HI_METAL, SPLINT_MAIL), ARMOR("banded mail", NoDes, - 1, 0, 1, 0, 72, 5, 350, 90, 4, 1, ARM_SUIT, IRON, HI_METAL, + 1, 0, 1, 0, 66, 5, 350, 90, 4, 1, ARM_SUIT, IRON, HI_METAL, BANDED_MAIL), ARMOR("dwarvish mithril-coat", NoDes, 1, 0, 0, 0, 10, 1, 150, 240, 4, 2, ARM_SUIT, MITHRIL, HI_SILVER, @@ -571,28 +575,28 @@ ARMOR("elven mithril-coat", NoDes, 1, 0, 0, 0, 15, 1, 150, 240, 5, 2, ARM_SUIT, MITHRIL, HI_SILVER, ELVEN_MITHRIL_COAT), ARMOR("chain mail", NoDes, - 1, 0, 0, 0, 72, 5, 300, 75, 5, 1, ARM_SUIT, IRON, HI_METAL, + 1, 0, 0, 0, 66, 5, 300, 75, 5, 1, ARM_SUIT, IRON, HI_METAL, CHAIN_MAIL), ARMOR("orcish chain mail", "crude chain mail", - 0, 0, 0, 0, 20, 5, 300, 75, 6, 1, ARM_SUIT, IRON, CLR_BLACK, + 0, 0, 0, 0, 19, 5, 300, 75, 6, 1, ARM_SUIT, IRON, CLR_BLACK, ORCISH_CHAIN_MAIL), ARMOR("scale mail", NoDes, - 1, 0, 0, 0, 72, 5, 250, 45, 6, 1, ARM_SUIT, IRON, HI_METAL, + 1, 0, 0, 0, 66, 5, 250, 45, 6, 1, ARM_SUIT, IRON, HI_METAL, SCALE_MAIL), ARMOR("studded leather armor", NoDes, - 1, 0, 0, 0, 72, 3, 200, 15, 7, 1, ARM_SUIT, LEATHER, HI_LEATHER, + 1, 0, 0, 0, 66, 3, 200, 15, 7, 1, ARM_SUIT, LEATHER, HI_LEATHER, STUDDED_LEATHER_ARMOR), ARMOR("ring mail", NoDes, - 1, 0, 0, 0, 72, 5, 250, 100, 7, 1, ARM_SUIT, IRON, HI_METAL, + 1, 0, 0, 0, 66, 5, 250, 100, 7, 1, ARM_SUIT, IRON, HI_METAL, RING_MAIL), ARMOR("orcish ring mail", "crude ring mail", - 0, 0, 0, 0, 20, 5, 250, 80, 8, 1, ARM_SUIT, IRON, CLR_BLACK, + 0, 0, 0, 0, 19, 5, 250, 80, 8, 1, ARM_SUIT, IRON, CLR_BLACK, ORCISH_RING_MAIL), ARMOR("leather armor", NoDes, - 1, 0, 0, 0, 82, 3, 150, 5, 8, 1, ARM_SUIT, LEATHER, HI_LEATHER, + 1, 0, 0, 0, 75, 3, 150, 5, 8, 1, ARM_SUIT, LEATHER, HI_LEATHER, LEATHER_ARMOR), ARMOR("leather jacket", NoDes, - 1, 0, 0, 0, 12, 0, 30, 10, 9, 0, ARM_SUIT, LEATHER, CLR_BLACK, + 1, 0, 0, 0, 11, 0, 30, 10, 9, 0, ARM_SUIT, LEATHER, CLR_BLACK, LEATHER_JACKET), /* shirts */ @@ -620,52 +624,58 @@ CLOAK("oilskin cloak", "slippery cloak", 0, 0, 0, 8, 0, 10, 50, 9, 2, CLOTH, HI_CLOTH, OILSKIN_CLOAK), CLOAK("robe", NoDes, - 1, 1, 0, 3, 0, 15, 50, 8, 2, CLOTH, CLR_RED, ROBE), + 1, 1, 0, 6, 0, 15, 50, 8, 2, CLOTH, CLR_RED, ROBE), /* robe was adopted from slash'em, where it's worn as a suit rather than as a cloak and there are several variations */ CLOAK("alchemy smock", "apron", - 0, 1, POISON_RES, 9, 0, 10, 50, 9, 1, CLOTH, CLR_WHITE, + 0, 1, POISON_RES, 11, 0, 10, 50, 9, 1, CLOTH, CLR_WHITE, ALCHEMY_SMOCK), CLOAK("leather cloak", NoDes, 1, 0, 0, 8, 0, 15, 40, 9, 1, LEATHER, CLR_BROWN, LEATHER_CLOAK), /* with shuffled appearances... */ CLOAK("cloak of protection", "tattered cape", - 0, 1, PROTECTION, 9, 0, 10, 50, 7, 3, CLOTH, HI_CLOTH, + 0, 1, PROTECTION, 11, 0, 10, 50, 7, 3, CLOTH, HI_CLOTH, CLOAK_OF_PROTECTION), /* cloak of protection is now the only item conferring MC 3 */ CLOAK("cloak of invisibility", "opera cloak", - 0, 1, INVIS, 10, 0, 10, 60, 9, 1, CLOTH, CLR_BRIGHT_MAGENTA, + 0, 1, INVIS, 12, 0, 10, 60, 9, 1, CLOTH, CLR_BRIGHT_MAGENTA, CLOAK_OF_INVISIBILITY), CLOAK("cloak of magic resistance", "ornamental cope", - 0, 1, ANTIMAGIC, 2, 0, 10, 60, 9, 1, CLOTH, CLR_WHITE, + 0, 1, ANTIMAGIC, 6, 0, 10, 60, 9, 1, CLOTH, CLR_WHITE, CLOAK_OF_MAGIC_RESISTANCE), /* 'cope' is not a spelling mistake... leave it be */ CLOAK("cloak of displacement", "piece of cloth", - 0, 1, DISPLACED, 10, 0, 10, 50, 9, 1, CLOTH, HI_CLOTH, + 0, 1, DISPLACED, 12, 0, 10, 50, 9, 1, CLOTH, HI_CLOTH, CLOAK_OF_DISPLACEMENT), /* shields */ -SHIELD("small shield", NoDes, - 1, 0, 0, 0, 6, 0, 30, 3, 9, 0, WOOD, HI_WOOD, +SHIELD("small shield", "wooden shield", + 0, 0, 0, 0, 6, 0, 30, 3, 9, 0, WOOD, HI_WOOD, SMALL_SHIELD), +SHIELD("shield of drain resistance", "wooden shield", + 0, 1, 0, DRAIN_RES, 12, 0, 30, 50, 9, 0, WOOD, HI_WOOD, + SHIELD_OF_DRAIN_RESISTANCE), +SHIELD("shield of shock resistance", "wooden shield", + 0, 1, 0, SHOCK_RES, 12, 0, 30, 50, 9, 0, WOOD, HI_WOOD, + SHIELD_OF_SHOCK_RESISTANCE), SHIELD("elven shield", "blue and green shield", - 0, 0, 0, 0, 2, 0, 40, 7, 8, 0, WOOD, CLR_GREEN, + 0, 0, 0, 0, 2, 0, 40, 7, 8, 0, WOOD, CLR_GREEN, ELVEN_SHIELD), SHIELD("Uruk-hai shield", "white-handed shield", - 0, 0, 0, 0, 2, 0, 50, 7, 9, 0, IRON, HI_METAL, + 0, 0, 0, 0, 2, 0, 50, 7, 9, 0, IRON, HI_METAL, URUK_HAI_SHIELD), SHIELD("orcish shield", "red-eyed shield", - 0, 0, 0, 0, 2, 0, 50, 7, 9, 0, IRON, CLR_RED, + 0, 0, 0, 0, 2, 0, 50, 7, 9, 0, IRON, CLR_RED, ORCISH_SHIELD), SHIELD("large shield", NoDes, - 1, 0, 1, 0, 7, 0, 100, 10, 8, 0, IRON, HI_METAL, + 1, 0, 1, 0, 4, 0, 100, 10, 8, 0, IRON, HI_METAL, LARGE_SHIELD), SHIELD("dwarvish roundshield", "large round shield", - 0, 0, 0, 0, 4, 0, 100, 10, 8, 0, IRON, HI_METAL, + 0, 0, 0, 0, 3, 0, 100, 10, 8, 0, IRON, HI_METAL, DWARVISH_ROUNDSHIELD), SHIELD("shield of reflection", "polished silver shield", - 0, 1, 0, REFLECTING, 3, 0, 50, 50, 8, 0, SILVER, HI_SILVER, + 0, 1, 0, REFLECTING, 7, 0, 50, 50, 8, 0, SILVER, HI_SILVER, SHIELD_OF_REFLECTION), /* gloves */ @@ -674,7 +684,7 @@ SHIELD("shield of reflection", "polished silver shield", * HI_METAL or CLR_BLACK. All have shuffled descriptions. */ GLOVES("leather gloves", "old gloves", - 0, 0, 0, 16, 1, 10, 8, 9, 0, LEATHER, HI_LEATHER, + 0, 0, 0, 15, 1, 10, 8, 9, 0, LEATHER, HI_LEATHER, LEATHER_GLOVES), GLOVES("gauntlets of fumbling", "padded gloves", 0, 1, FUMBLING, 8, 1, 10, 50, 9, 0, LEATHER, HI_LEATHER, @@ -688,11 +698,11 @@ GLOVES("gauntlets of dexterity", "fencing gloves", /* boots */ BOOTS("low boots", "walking shoes", - 0, 0, 0, 25, 2, 10, 8, 9, 0, LEATHER, HI_LEATHER, LOW_BOOTS), + 0, 0, 0, 23, 2, 10, 8, 9, 0, LEATHER, HI_LEATHER, LOW_BOOTS), BOOTS("iron shoes", "hard shoes", 0, 0, 0, 7, 2, 50, 16, 8, 0, IRON, HI_METAL, IRON_SHOES), BOOTS("high boots", "jackboots", - 0, 0, 0, 15, 2, 20, 12, 8, 0, LEATHER, HI_LEATHER, HIGH_BOOTS), + 0, 0, 0, 14, 2, 20, 12, 8, 0, LEATHER, HI_LEATHER, HIGH_BOOTS), /* with shuffled appearances... */ BOOTS("speed boots", "combat boots", 0, 1, FAST, 12, 2, 20, 50, 9, 0, LEATHER, HI_LEATHER, SPEED_BOOTS), @@ -1447,9 +1457,11 @@ WAND("create monster", "maple", 45, 200, 1, NODIR, WOOD, HI_WOOD, WAN_CREATE_MONSTER), WAND("wishing", "pine", 5, 500, 1, NODIR, WOOD, HI_WOOD, WAN_WISHING), +WAND("stasis", "redwood", 45, 150, 1, NODIR, WOOD, CLR_RED, + WAN_STASIS), WAND("nothing", "oak", 25, 100, 0, IMMEDIATE, WOOD, HI_WOOD, WAN_NOTHING), -WAND("striking", "ebony", 75, 150, 1, IMMEDIATE, WOOD, HI_WOOD, +WAND("striking", "ebony", 30, 150, 1, IMMEDIATE, WOOD, HI_WOOD, WAN_STRIKING), WAND("make invisible", "marble", 45, 150, 1, IMMEDIATE, MINERAL, HI_MINERAL, WAN_MAKE_INVISIBLE), diff --git a/include/optlist.h b/include/optlist.h index b0bc715f3..29f376dd8 100644 --- a/include/optlist.h +++ b/include/optlist.h @@ -4,10 +4,6 @@ #ifndef OPTLIST_H #define OPTLIST_H -#ifdef OPTIONS_C -static int optfn_boolean(int, int, boolean, char *, char *); -#endif - /* * NOTE: If you add (or delete) an option, please review: * doc/options.txt @@ -16,6 +12,10 @@ static int optfn_boolean(int, int, boolean, char *, char *); * updates that should accompany your change. */ +#define BACKWARD_COMPAT + +extern int optfn_boolean(int, int, boolean, char *, char *); + enum OptType { BoolOpt, CompOpt, OthrOpt }; enum Y_N { No, Yes }; enum Off_On { Off, On }; @@ -45,7 +45,7 @@ struct allopt_t { const char *alias; const char *descr; const char *prefixgw; - boolean initval, has_handler, dupdetected; + boolean initval, has_handler, dupdetected, disregarded; }; #endif /* OPTLIST_H */ @@ -74,16 +74,16 @@ static int optfn_##a(int, int, boolean, char *, char *); #elif defined(NHOPT_PARSE) #define NHOPTB(a, sec, b, c, s, i, n, v, d, al, bp, termp, desc) \ { #a, OptS_##sec, 0, b, opt_##a, s, BoolOpt, n, v, d, No, termp, c, \ - bp, &optfn_boolean, al, desc, (const char *) 0, i, 0, 0 }, + bp, &optfn_boolean, al, desc, (const char *) 0, i, 0, 0 , 0 }, #define NHOPTC(a, sec, b, c, s, n, v, d, h, al, z) \ { #a, OptS_##sec, 0, b, opt_##a, s, CompOpt, n, v, d, No, 0, c, \ - (boolean *) 0, &optfn_##a, al, z, (const char *) 0, Off, h, 0 }, + (boolean *) 0, &optfn_##a, al, z, (const char *) 0, Off, h, 0, 0 }, #define NHOPTP(a, sec, b, c, s, n, v, d, h, al, z) \ { #a, OptS_##sec, 0, b, pfx_##a, s, CompOpt, n, v, d, Yes, 0, c, \ - (boolean *) 0, &pfxfn_##a, al, z, #a, Off, h, 0 }, + (boolean *) 0, &pfxfn_##a, al, z, #a, Off, h, 0, 0 }, #define NHOPTO(m, sec, a, b, c, s, n, v, d, al, z) \ { m, OptS_##sec, 0, b, opt_##a, s, OthrOpt, n, v, d, No, 0, c, \ - (boolean *) 0, &optfn_##a, al, z, (const char *) 0, On, On, 0 }, + (boolean *) 0, &optfn_##a, al, z, (const char *) 0, On, On, 0, 0 }, /* this is not reliable because TILES_IN_GLYPHMAP might be defined * in a multi-interface binary but not apply to the current interface */ @@ -589,6 +589,9 @@ static int optfn_##a(int, int, boolean, char *, char *); NHOPTB(preload_tiles, Advanced, 0, opt_out, set_in_config, /* MSDOS only */ On, Yes, No, No, NoAlias, &iflags.wc_preload_tiles, Term_False, (char *)0) + NHOPTB(price_quotes, General, 0, opt_in, set_in_game, + Off, Yes, No, No, NoAlias, &iflags.pricequotes, Term_False, + "display prices you have seen for unidentified objects") NHOPTB(pushweapon, Behavior, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias, &flags.pushweapon, Term_False, "previous weapon goes to secondary slot") @@ -608,6 +611,9 @@ static int optfn_##a(int, int, boolean, char *, char *); Off, No, No, No, NoAlias, (boolean *) 0, Term_False, (char *)0) #endif + NHOPTB(reroll, Advanced, 0, opt_in, set_in_config, + Off, Yes, No, No, NoAlias, &u.uroleplay.reroll, Term_False, + "allow rerolling of starting inventory and items") NHOPTB(rest_on_space, Advanced, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias, &flags.rest_on_space, Term_False, "space bar is bound to the rest-command") diff --git a/include/patchlevel.h b/include/patchlevel.h index ff02f26bc..75e1d3311 100644 --- a/include/patchlevel.h +++ b/include/patchlevel.h @@ -17,7 +17,7 @@ * Incrementing EDITLEVEL can be used to force invalidation of old bones * and save files. */ -#define EDITLEVEL 130 +#define EDITLEVEL 140 /* * Development status possibilities. @@ -36,7 +36,7 @@ #define DEBUG #endif -#define COPYRIGHT_BANNER_A "NetHack, Copyright 1985-2025" +#define COPYRIGHT_BANNER_A "NetHack, Copyright 1985-2026" #define COPYRIGHT_BANNER_B \ " By Stichting Mathematisch Centrum and M. Stephenson." /* nomakedefs.copyright_banner_c is generated at runtime */ diff --git a/include/permonst.h b/include/permonst.h index 6df66b841..fbc135c41 100644 --- a/include/permonst.h +++ b/include/permonst.h @@ -64,8 +64,8 @@ struct permonst { aligntyp maligntyp; /* basic monster alignment */ unsigned short geno; /* creation/geno mask value */ struct attack mattk[NATTK]; /* attacks matrix */ - unsigned short cwt, /* weight of corpse */ - cnutrit; /* its nutritional value */ + unsigned cwt; /* weight of corpse */ + unsigned short cnutrit; /* its nutritional value */ uchar msound; /* noise it makes (6 bits) */ uchar msize; /* physical size (3 bits) */ uchar mresists; /* resistances */ diff --git a/include/quest.h b/include/quest.h index 28d5ebeb1..77af3049f 100644 --- a/include/quest.h +++ b/include/quest.h @@ -14,7 +14,7 @@ struct q_score { /* Quest "scorecard" */ Bitfield(killed_leader, 1); /* killed the quest leader */ Bitfield(first_locate, 1); /* only set the first time */ - Bitfield(met_intermed, 1); /* used if the locate is a person. */ + Bitfield(met_intermed, 1); /* used if the locate is a person */ Bitfield(got_final, 1); /* got the final quest assignment */ Bitfield(made_goal, 3); /* # of times on goal level */ diff --git a/include/region.h b/include/region.h index c712173c1..b9b7ced43 100644 --- a/include/region.h +++ b/include/region.h @@ -56,7 +56,7 @@ typedef struct { /* Should probably do the same thing about objects */ - boolean visible; /* Is the region visible ? */ + boolean visible; /* Is the region visible? */ int glyph; /* Which glyph to use if visible */ anything arg; /* Optional user argument (Ex: strength of * force field, damage of a fire zone, ...*/ diff --git a/include/rm.h b/include/rm.h index e22f48054..8811deae1 100644 --- a/include/rm.h +++ b/include/rm.h @@ -12,8 +12,8 @@ * building on Don G Kneller's MS-DOS implementation. See drawing.c for * the code that permits the user to set the contents of the symbol structure. * - * The door representation was changed by Ari - * Huttunen(ahuttune@niksula.hut.fi) + * The door representation was changed by + * Ari Huttunen(ahuttune@niksula.hut.fi). */ /* @@ -454,6 +454,7 @@ struct levelflags { Bitfield(stormy, 1); /* clouds create lightning bolts at random */ schar temperature; /* +1 == hot, -1 == cold */ + long stasis_until; /* wand of stasis effect lasts until when? */ }; typedef struct { @@ -476,7 +477,7 @@ typedef struct { #define fmon svl.level.monlist /* - * Covert a trap number into the defsym graphics array. + * Convert a trap number into the defsym graphics array. * Convert a defsym number into a trap number. * Assumes that arrow trap will always be the first trap. */ diff --git a/include/sp_lev.h b/include/sp_lev.h index 769c6a1c7..52e89978c 100644 --- a/include/sp_lev.h +++ b/include/sp_lev.h @@ -145,6 +145,7 @@ typedef struct { schar peaceful, asleep; short female, invis, cancelled, revived, avenge, fleeing, blinded, paralyzed, stunned, confused, waiting; + short m_lev_adj; long seentraps; short has_invent; mmflags_nht mm_flags; /* makemon flags */ diff --git a/include/trap.h b/include/trap.h index a0592f7e1..206951baa 100644 --- a/include/trap.h +++ b/include/trap.h @@ -26,7 +26,7 @@ struct trap { Bitfield(once, 1); Bitfield(madeby_u, 1); /* So monsters may take offence when you trap * them. Recognizing who made the trap isn't - * completely unreasonable, everybody has + * completely unreasonable; everybody has * their own style. This flag is also needed * when you untrap a monster. It would be too * easy to make a monster peaceful if you could diff --git a/include/winami.h b/include/winami.h index b9fbfb65c..1cfd85078 100644 --- a/include/winami.h +++ b/include/winami.h @@ -22,6 +22,7 @@ typedef struct amii_mi { char gselector; /* Group selector */ char canselect; /* Can user select this entry. */ char attr; /* Attribute for the line. */ + int color; /* Color for the line (from menucolors). */ char *str; /* The text of the item. */ } amii_menu_item; diff --git a/include/wincurs.h b/include/wincurs.h index 4c5b7f19c..ad1a333fa 100644 --- a/include/wincurs.h +++ b/include/wincurs.h @@ -26,8 +26,6 @@ extern WINDOW *activemenu; /* curses window for menu requesting a #define SCROLLBAR_BACK_COLOR CLR_BLACK #define HIGHLIGHT_COLOR CLR_WHITE #define MORECOLOR CLR_ORANGE -#define STAT_UP_COLOR CLR_GREEN -#define STAT_DOWN_COLOR CLR_RED #define MESSAGE_WIN 1 #define STATUS_WIN 2 #define MAP_WIN 3 diff --git a/include/windconf.h b/include/windconf.h index bd30f8a6b..81ad8baeb 100644 --- a/include/windconf.h +++ b/include/windconf.h @@ -32,6 +32,9 @@ #define OPTIONS_AT_RUNTIME /* build info done at runtime not text file */ +#define EARLY_CONFIGFILE_PASS +#define TTY_PERM_INVENT + /* * ----------------------------------------------------------------- * The remaining code shouldn't need modification. diff --git a/include/winprocs.h b/include/winprocs.h index c77288891..45463205e 100644 --- a/include/winprocs.h +++ b/include/winprocs.h @@ -11,10 +11,13 @@ #endif enum wp_ids { wp_tty = 1, wp_X11, wp_Qt, wp_mswin, wp_curses, - wp_chainin, wp_chainout, wp_safestartup, wp_shim, + wp_chainin, wp_chainout, wp_shim, wp_hup, wp_guistubs, wp_ttystubs, +#if defined(AMIGA) + wp_amii, wp_amiv, +#endif #ifdef OUTDATED_STUFF - wp_mac, wp_Gem, wp_Gnome, wp_amii, wp_amiv, + wp_mac, wp_Gem, wp_Gnome, #endif wp_trace // XXX do we need this? should chainin/out get an id? TBD }; @@ -412,79 +415,4 @@ struct chain_procs { }; #endif /* WINCHAIN */ -#ifdef SAFEPROCS -/* - * window port routines available in sys/share/safeproc.c - */ -extern struct window_procs *get_safe_procs(int); -extern void safe_init_nhwindows(int *, char **); -extern void safe_player_selection(void); -extern void safe_askname(void); -extern void safe_get_nh_event(void); -extern void safe_exit_nhwindows(const char *); -extern void safe_suspend_nhwindows(const char *); -extern void safe_resume_nhwindows(void); -extern winid safe_create_nhwindow(int); -extern void safe_clear_nhwindow(winid); -extern void safe_display_nhwindow(winid, boolean); -extern void safe_destroy_nhwindow(winid); -extern void safe_curs(winid, int, int); -extern void safe_putstr(winid, int, const char *); -extern void safe_putmixed(winid, int, const char *); -extern void safe_display_file(const char *, boolean); -extern void safe_start_menu(winid, unsigned long); -extern void safe_add_menu(winid, const glyph_info *, const ANY_P *, - char, char, int, int, const char *, - unsigned int); -extern void safe_end_menu(winid, const char *); -extern int safe_select_menu(winid, int, MENU_ITEM_P **); -extern char safe_message_menu(char, int, const char *); -extern void safe_mark_synch(void); -extern void safe_wait_synch(void); -#ifdef CLIPPING -extern void safe_cliparound(int, int); -#endif -#ifdef POSITIONBAR -extern void safe_update_positionbar(char *); -#endif -extern void safe_print_glyph(winid, coordxy, coordxy, - const glyph_info *, const glyph_info *); -extern void safe_raw_print(const char *); -extern void safe_raw_print_bold(const char *); -extern int safe_nhgetch(void); -extern int safe_nh_poskey(coordxy *, coordxy *, int *); -extern void safe_nhbell(void); -extern int safe_doprev_message(void); -extern char safe_yn_function(const char *, const char *, char); -extern void safe_getlin(const char *, char *); -extern int safe_get_ext_cmd(void); -extern void safe_number_pad(int); -extern void safe_delay_output(void); -#ifdef CHANGE_COLOR -extern void safe_change_color(int, long, int); -#ifdef MAC -extern void safe_change_background(int); -extern short safe_set_font_name(winid, char *); -#endif -extern char *safe_get_color_string(void); -#endif -extern void safe_outrip(winid, int, time_t); -extern void safe_preference_update(const char *); -extern char *safe_getmsghistory(boolean); -extern void safe_putmsghistory(const char *, boolean); -extern void safe_status_init(void); -extern void safe_status_finish(void); -extern void safe_status_enablefield(int, const char *, const char *, - boolean); -extern void safe_status_update(int, genericptr_t, int, int, int, - unsigned long *); -extern boolean safe_can_suspend(void); -extern void stdio_raw_print(const char *); -extern void stdio_nonl_raw_print(const char *); -extern void stdio_raw_print_bold(const char *); -extern void stdio_wait_synch(void); -extern void safe_update_inventory(int); -extern win_request_info *safe_ctrl_nhwindow(winid, int, win_request_info *); -extern int stdio_nhgetch(void); -#endif /* SAFEPROCS */ #endif /* WINPROCS_H */ diff --git a/include/you.h b/include/you.h index bbd679d8f..a073e5c57 100644 --- a/include/you.h +++ b/include/you.h @@ -163,11 +163,13 @@ struct u_conduct { /* number of times... */ }; struct u_roleplay { - boolean blind; /* permanently blind */ - boolean nudist; /* has not worn any armor, ever */ - boolean deaf; /* permanently deaf */ - boolean pauper; /* no starting inventory */ - long numbones; /* # of bones files loaded */ + boolean blind; /* permanently blind */ + boolean nudist; /* has not worn any armor, ever */ + boolean deaf; /* permanently deaf */ + boolean pauper; /* no starting inventory */ + boolean reroll; /* starting inventory/attr rerolling enabled */ + long numbones; /* # of bones files loaded */ + long numrerolls; /* # of rerolls used */ }; /*** Unified structure containing role information ***/ @@ -534,6 +536,7 @@ struct _hitmon_data { boolean needpoismsg; boolean poiskilled; boolean already_killed; + boolean offmap; boolean destroyed; boolean dryit; boolean doreturn; diff --git a/outdated/.travis.yml b/outdated/.travis.yml index 889051258..b0f0dfc3b 100644 --- a/outdated/.travis.yml +++ b/outdated/.travis.yml @@ -3,7 +3,7 @@ matrix: include: - name: linux-xenial-gcc-win-all os: linux - env: HINTS=linux.370 LUA_VERSION=5.4.6 + env: HINTS=linux.370 LUA_VERSION=5.4.8 compiler: gcc addons: apt: @@ -21,7 +21,7 @@ matrix: - make LUA_VERSION=$LUA_VERSION WANT_WIN_ALL=1 QT_SELECT=5 MOC=moc install - name: linux-bionic-gcc-win-all os: linux - env: HINTS=linux.370 LUA_VERSION=5.4.6 + env: HINTS=linux.370 LUA_VERSION=5.4.8 dist: bionic compiler: gcc addons: @@ -40,7 +40,7 @@ matrix: - make LUA_VERSION=$LUA_VERSION WANT_WIN_ALL=1 QT_SELECT=5 MOC=moc install - name: linux-focal-clang-win-all os: linux - env: HINTS=linux.370 LUA_VERSION=5.4.6 + env: HINTS=linux.370 LUA_VERSION=5.4.8 dist: focal compiler: clang addons: @@ -59,7 +59,7 @@ matrix: - make LUA_VERSION=$LUA_VERSION WANT_WIN_ALL=1 QT_SELECT=5 MOC=moc install - name: linux-xenial-gcc-nocommon os: linux - env: HINTS=linux.370 LUA_VERSION=5.4.6 + env: HINTS=linux.370 LUA_VERSION=5.4.8 dist: xenial compiler: gcc script: @@ -70,7 +70,7 @@ matrix: - make install - name: linux-focal-gcc9-win-all os: linux - env: HINTS=linux.370 LUA_VERSION=5.4.6 + env: HINTS=linux.370 LUA_VERSION=5.4.8 dist: focal compiler: gcc addons: @@ -91,7 +91,7 @@ matrix: - make LUA_VERSION=$LUA_VERSION WANT_WIN_ALL=1 QT_SELECT=5 MOC=moc install - name: linux-xenial-gcc-minimal os: linux - env: HINTS=linux-minimal LUA_VERSION=5.4.6 + env: HINTS=linux-minimal LUA_VERSION=5.4.8 compiler: gcc script: | cd sys/unix/ && sh setup.sh hints/$HINTS && cd ../../ @@ -128,7 +128,7 @@ matrix: script: - export ADD_CURSES=Y - export PDCURSES_TOP=../lib/pdcurses - - export LUA_VERSION=5.4.6 + - export LUA_VERSION=5.4.8 - sh sys/windows/travis-gcc.sh - test -d "lib/lua-$LUA_VERSION/src" || exit 0 - test -d "lib/pdcurses" || exit 0 @@ -137,7 +137,7 @@ matrix: - mingw32-make LUA_VERSION=$LUA_VERSION install - name: msdos-linux-focal-djgpp-crosscompile os: linux - env: HINTS=linux.370 LUA_VERSION=5.4.6 + env: HINTS=linux.370 LUA_VERSION=5.4.8 dist: focal compiler: gcc script: diff --git a/outdated/sys/unix/hints/include/cross-amiga-pre b/outdated/sys/unix/hints/include/cross-amiga-pre index 5c59f102d..2d5b24ede 100644 --- a/outdated/sys/unix/hints/include/cross-amiga-pre +++ b/outdated/sys/unix/hints/include/cross-amiga-pre @@ -24,9 +24,9 @@ endif ifdef BUILD_TARGET_LUA #===============-================================================= # LUA library -# Source from http://www.lua.org/ftp/lua-5.4.6.tar.gz +# Source from http://www.lua.org/ftp/lua-5.4.8.tar.gz #================================================================= -LUA_VERSION ?=5.4.6 +LUA_VERSION ?=5.4.8 LUATOP ?= ../lib/lua-$(LUA_VERSION) LUASRCDIR ?= $(LUATOP)/src LUAOBJFILES1 = $(TARGETPFX)lapi.o $(TARGETPFX)lauxlib.o \ diff --git a/outdated/sys/wince/mswproc.c b/outdated/sys/wince/mswproc.c index 8011991bc..44106e616 100644 --- a/outdated/sys/wince/mswproc.c +++ b/outdated/sys/wince/mswproc.c @@ -1147,7 +1147,7 @@ mswin_select_menu(winid wid, int how, MENU_ITEM_P **selected) /* -- Indicate to the window port that the inventory has been changed. - -- Merely calls display_inventory() for window-ports that leave the + -- Merely calls repopulate_perminvent() for window-ports that leave the window up, otherwise empty. */ void diff --git a/outdated/sys/windows/travis-gcc.sh b/outdated/sys/windows/travis-gcc.sh deleted file mode 100644 index c61137790..000000000 --- a/outdated/sys/windows/travis-gcc.sh +++ /dev/null @@ -1,8 +0,0 @@ -set -x -mkdir -p lib -cd lib -git clone --depth 1 https://github.com/wmcbrine/PDCurses.git pdcurses -#git clone --depth 1 https://github.com/universal-ctags/ctags.git ctags -curl -R -O http://www.lua.org/ftp/lua-5.4.6.tar.gz -tar zxf lua-5.4.6.tar.gz -cd ../ diff --git a/outdated/sys/windows/vs/travisci.sh b/outdated/sys/windows/vs/travisci.sh deleted file mode 100644 index f9f1b5080..000000000 --- a/outdated/sys/windows/vs/travisci.sh +++ /dev/null @@ -1,49 +0,0 @@ -set -x -export VSVER=2017 -export MSVER=14.16.27023 -export SDKVER=10.0.17763.0 -export FRAMEVER=4.0.30319 -export NETFXVER=4.6.1 -export WKITVER=10.0.17134.0 -#export TOOLSVER=Enterprise -export TOOLSVER=BuildTools -export PATH=/c/Program\ Files\ \(x86\)/Microsoft\ Visual\ Studio/$VSVER/$TOOLSVER/Common7/IDE/VC/VCPackages:$PATH -export PATH=/c/Program\ Files\ \(x86\)/Windows\ Kits/10/bin/$WKITVER/x64:$PATH -export PATH=/c/Program\ Files\ \(x86\)/Microsoft\ Visual\ Studio/$VSVER/$TOOLSVER/VC/Tools/MSVC/$MSVER/bin/HostX64/x64:$PATH -export PATH=/c/Program\ Files\ \(x86\)/Microsoft\ Visual\ Studio/$VSVER/$TOOLSVER/VC/Tools/MSVC/$MSVER/bin/HostX64/x86:$PATH -export PATH=$PATH:/c/Program\ Files\ \(x86\)/Microsoft\ Visual\ Studio/$VSVER/$TOOLSVER/Common7/IDE/CommonExtensions/Microsoft/TestWindow -export PATH=$PATH:/c/Program\ Files\ \(x86\)/Microsoft\ Visual\ Studio/$VSVER/$TOOLSVER/MSBuild/Current/bin/Roslyn -export INCLUDE=/c/Program\ Files\ \(x86\)/Microsoft\ Visual\ Studio/2017/$TOOLSVER/VC/Tools/MSVC/$MSVER/include -export INCLUDE=$INCLUDE:/c/Program\ Files\ \(x86\)/Windows\ Kits/10/Include/$WKITVER/ucrt -export INCLUDE=$INCLUDE:/c/Program\ Files\ \(x86\)/Windows\ Kits/10/include/$WKITVER/ucrt -export INCLUDE=$INCLUDE:/c/Program\ Files\ \(x86\)/Windows\ Kits/10/include/$WKITVER/shared -export INCLUDE=$INCLUDE:/c/Program\ Files\ \(x86\)/Windows\ Kits/10/include/$WKITVER/um -export INCLUDE=$INCLUDE:/c/Program\ Files\ \(x86\)/Windows\ Kits/10/include/$WKITVER/winrt -export INCLUDE=$INCLUDE:/c/Program\ Files\ \(x86\)/Windows\ Kits/10/include/$WKITVER/cppwinrt -export LIB=/c/Program\ Files\ \(x86\)/Microsoft\ Visual\ Studio/$VSVER/$TOOLSVER/VC/Tools/MSVC/$MSVER/ATLMFC/lib/x86 -export LIB=/c/Program\ Files\ \(x86\)/Microsoft\ Visual\ Studio/$VSVER/$TOOLSVER/VC/Tools/MSVC/$MSVER/lib/x86:$LIB -export LIB=/c/Program\ Files\ \(x86\)/Windows\ Kits/10/lib/$WKITVER/ucrt/x86:$LIB -export LIB=/c/Program\ Files\ \(x86\)/Windows\ Kits/10/lib/$WKITVER/um/x86:$LIB -export LUA_VERSION=5.4.3 -mkdir -p lib -cd lib -git clone --depth 1 https://github.com/wmcbrine/PDCurses.git pdcurses -#git clone --depth 1 https://github.com/universal-ctags/ctags.git ctags -curl -R -O http://www.lua.org/ftp/lua-$LUA_VERSION.tar.gz -tar zxf lua-$LUA_VERSION.tar.gz -#cd ctags -#nmake -f mk_mvc.mak -#cd ../../ -cd ../ -test -d "lib/lua-$LUA_VERSION/src" || echo "Lua-$LUA_VERSION fetch failed" -test -d "lib/pdcurses" || echo "pdcurses fetch failed" -test -d "lib/pdcurses" || exit 0 -test -d "lib/lua-$LUA_VERSION/src" || exit 0 -export ADD_CURSES=Y -export PDCURSES_TOP=../lib/pdcurses -export -cd src -cp ../sys/windows/Makefile.nmake ./Makefile -nmake install -cd .. -powershell -Command "Compress-Archive -U -Path binary/* -DestinationPath $TRAVIS_TAG.x86.zip" diff --git a/outdated/win/Qt3/qt3_win.cpp b/outdated/win/Qt3/qt3_win.cpp index 26e46c4bb..9adde0fe9 100644 --- a/outdated/win/Qt3/qt3_win.cpp +++ b/outdated/win/Qt3/qt3_win.cpp @@ -4850,7 +4850,7 @@ void NetHackQtBind::qt_update_inventory() main->updateInventory(); /* doesn't work yet if (program_state.something_worth_saving && iflags.perm_invent) - display_inventory(NULL, FALSE); + repopulate_perminvent(); */ } diff --git a/outdated/win/gnome/gnbind.c b/outdated/win/gnome/gnbind.c index a298af5a0..9c193c625 100644 --- a/outdated/win/gnome/gnbind.c +++ b/outdated/win/gnome/gnbind.c @@ -786,7 +786,7 @@ gnome_select_menu(winid wid, int how, MENU_ITEM_P **selected) /* -- Indicate to the window port that the inventory has been changed. - -- Merely calls display_inventory() for window-ports that leave the + -- Merely calls repopulate_perminvent() for window-ports that leave the window up, otherwise empty. */ void diff --git a/src/.gitignore b/src/.gitignore index 13097d000..30c020d13 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -16,6 +16,7 @@ nethack tiles.bmp *.moc *.lnk +*.rsp graphicschk nhdat o diff --git a/src/allmain.c b/src/allmain.c index 83a63b325..7c4fec3ae 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 allmain.c $NHDT-Date: 1744860497 2025/04/16 19:28:17 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.276 $ */ +/* NetHack 3.7 allmain.c $NHDT-Date: 1771213100 2026/02/15 19:38:20 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.286 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -13,6 +13,7 @@ staticfn void moveloop_preamble(boolean); staticfn void u_calc_moveamt(int); +staticfn void maybe_generate_rnd_mon(void); staticfn void maybe_do_tutorial(void); #ifdef POSITIONBAR staticfn void do_positionbar(void); @@ -20,10 +21,6 @@ staticfn void do_positionbar(void); staticfn void regen_pw(int); staticfn void regen_hp(int); staticfn void interrupt_multi(const char *); -staticfn void debug_fields(const char *); -#ifndef NODUMPENUMS -staticfn void dump_enums(void); -#endif #ifdef CRASHREPORT #define USED_FOR_CRASHREPORT @@ -90,7 +87,7 @@ moveloop_preamble(boolean resuming) fix_shop_damage(); } - (void) encumber_msg(); /* in case they auto-picked up something */ + encumber_msg(); /* in case they auto-picked up something */ if (gd.defer_see_monsters) { gd.defer_see_monsters = FALSE; see_monsters(); @@ -161,6 +158,16 @@ u_calc_moveamt(int wtcap) u.umovement = 0; } +/* small chance of generating a new random monster */ +staticfn void +maybe_generate_rnd_mon(void) +{ + if (!rn2(u.uevent.udemigod ? 25 + : (depth(&u.uz) > depth(&stronghold_level)) ? 50 + : 70)) + (void) makemon((struct permonst *) 0, 0, 0, NO_MM_FLAGS); +} + #if defined(MICRO) || defined(WIN32) static int mvl_abort_lev; #endif @@ -180,6 +187,8 @@ moveloop_core(void) #ifdef POSITIONBAR do_positionbar(); #endif + if (iflags.pending_customizations) + maybe_shuffle_customizations(); dobjsfree(); @@ -197,9 +206,10 @@ moveloop_core(void) u.umovement -= NORMAL_SPEED; do { /* hero can't move this turn loop */ - mvl_wtcap = encumber_msg(); + encumber_msg(); svc.context.mon_moving = TRUE; + gu.uhp_at_start_of_monster_turn = u.uhp; do { monscanmove = movemon(); if (u.umovement >= NORMAL_SPEED) @@ -207,6 +217,10 @@ moveloop_core(void) } while (monscanmove); svc.context.mon_moving = FALSE; + /* this needs to be after the monster movement loop in + case monster actions affected burden, e.g. rehumanize */ + mvl_wtcap = near_capacity(); + if (!monscanmove && u.umovement < NORMAL_SPEED) { /* both hero and monsters are out of steam this round */ struct monst *mtmp; @@ -224,11 +238,7 @@ moveloop_core(void) /* occasionally add another monster; since this takes place after movement has been allotted, the new monster effectively loses its first turn */ - if (!rn2(u.uevent.udemigod ? 25 - : (depth(&u.uz) > depth(&stronghold_level)) ? 50 - : 70)) - (void) makemon((struct permonst *) 0, 0, 0, - NO_MM_FLAGS); + maybe_generate_rnd_mon(); u_calc_moveamt(mvl_wtcap); settrack(); @@ -268,6 +278,8 @@ moveloop_core(void) if (u.ublesscnt) u.ublesscnt--; + gs.saving_grace_turn = FALSE; + /* One possible result of prayer is healing. Whether or * not you get healed depends on your current hit points. * If you are allowed to regenerate during the prayer, @@ -392,7 +404,7 @@ moveloop_core(void) inventory may have changed in, e.g., nh_timeout(); we do need two checks here so that the player gets feedback immediately if their own action encumbered them */ - (void) encumber_msg(); + encumber_msg(); #ifdef STATUS_HILITES if (iflags.hilite_delta) @@ -419,6 +431,8 @@ moveloop_core(void) else if (!u.umoved) (void) pooleffects(FALSE); + gs.saving_grace_turn = FALSE; + /* vision while buried or underwater is updated here */ if (Underwater) under_water(0); @@ -437,6 +451,8 @@ moveloop_core(void) /* the Amulet of Yendor gives a wish when initially picked up */ if (u.uhave.amulet && !u.uevent.amulet_wish) { u.uevent.amulet_wish = 1; + display_nhwindow(WIN_MESSAGE, TRUE); + urgent_pline("The Amulet is bestowing a wish upon you!"); makewish(); } @@ -500,11 +516,6 @@ moveloop_core(void) return; } -#ifdef CLIPPING - /* just before rhack */ - cliparound(u.ux, u.uy); -#endif - u.umoved = FALSE; if (gm.multi > 0) { @@ -536,6 +547,11 @@ moveloop_core(void) if (gv.vision_full_recalc) vision_recalc(0); /* vision! */ +#ifdef CLIPPING + /* after rhack() and vision_recalc() so that the map is redrawn + once with correct vision data, not twice (overshoot+correct) */ + cliparound(u.ux, u.uy); +#endif /* when running in non-tport mode, this gets done through domove() */ if ((!svc.context.run || flags.runmode == RUN_TPORT) && (gm.multi && (!svc.context.travel ? !(gm.multi % 7) @@ -597,6 +613,9 @@ regen_pw(int wtcap) / 6)))) || Energy_regeneration)) { int upper = (int) (ACURR(A_WIS) + ACURR(A_INT)) / 15 + 1; + if (EMagical_breathing) + upper += 2; + u.uen += rn1(upper, 1); if (u.uen > u.uenmax) u.uen = u.uenmax; @@ -748,7 +767,7 @@ init_sound_disp_gamewindows(void) if (iflags.perm_invent_pending) check_perm_invent_again(); #endif - } +} void newgame(void) @@ -778,7 +797,7 @@ newgame(void) * in hero's initial inventory */ init_artifacts(); /* before u_init() in case $WIZKIT specifies * any artifacts */ - u_init(); + u_init_misc(); l_nhcore_init(); /* create a Lua state that lasts until end of game */ reset_glyphmap(gm_newgame); @@ -793,18 +812,29 @@ newgame(void) mklev(); u_on_upstairs(); - if (wizard) - obj_delivery(FALSE); /* finish wizkit */ vision_reset(); /* set up internals for level (after mklev) */ check_special_room(FALSE); if (MON_AT(u.ux, u.uy)) mnexto(m_at(u.ux, u.uy), RLOC_NOMSG); (void) makedog(); + + u_init_inventory_attrs(); docrt(); + flush_screen(1); + bot(); + while (u.uroleplay.reroll && reroll_menu()) { + u_init_inventory_attrs(); + bot(); + } + u_init_skills_discoveries(); + + if (wizard) { + read_wizkit(); + obj_delivery(FALSE); /* finish wizkit */ + } if (flags.legacy) { - flush_screen(1); com_pager(u.uroleplay.pauper ? "pauper_legacy" : "legacy"); } @@ -939,218 +969,6 @@ interrupt_multi(const char *msg) } } -/* - * Argument processing helpers - for xxmain() to share - * and call. - * - * These should return TRUE if the argument matched, - * whether the processing of the argument was - * successful or not. - * - * Most of these do their thing, then after returning - * to xxmain(), the code exits without starting a game. - * - */ - -static const struct early_opt earlyopts[] = { - { ARG_DEBUG, "debug", 5, TRUE }, - { ARG_VERSION, "version", 4, TRUE }, - { ARG_SHOWPATHS, "showpaths", 8, FALSE }, -#ifndef NODUMPENUMS - { ARG_DUMPENUMS, "dumpenums", 9, FALSE }, -#endif - { ARG_DUMPGLYPHIDS, "dumpglyphids", 12, FALSE }, - { ARG_DUMPMONGEN, "dumpmongen", 10, FALSE }, - { ARG_DUMPWEIGHTS, "dumpweights", 11, FALSE }, -#ifdef WIN32 - { ARG_WINDOWS, "windows", 4, TRUE }, -#endif -#if defined(CRASHREPORT) - { ARG_BIDSHOW, "bidshow", 7, FALSE }, -#endif -}; - -#ifdef WIN32 -extern int windows_early_options(const char *); -#endif - -/* - * Returns: - * 0 = no match - * 1 = found and skip past this argument - * 2 = found and trigger immediate exit - */ -int -argcheck(int argc, char *argv[], enum earlyarg e_arg) -{ - int i, idx; - boolean match = FALSE; - char *userea = (char *) 0; - const char *dashdash = ""; - - for (idx = 0; idx < SIZE(earlyopts); idx++) { - if (earlyopts[idx].e == e_arg){ - break; - } - } - if (idx >= SIZE(earlyopts) || argc < 1) - return 0; - - for (i = 0; i < argc; ++i) { - if (argv[i][0] != '-') - continue; - if (argv[i][1] == '-') { - userea = &argv[i][2]; - dashdash = "-"; - } else { - userea = &argv[i][1]; - } - match = match_optname(userea, earlyopts[idx].name, - earlyopts[idx].minlength, - earlyopts[idx].valallowed); - if (match) - break; - } - - if (match) { - const char *extended_opt = strchr(userea, ':'); - - if (!extended_opt) - extended_opt = strchr(userea, '='); - switch(e_arg) { - case ARG_DEBUG: - if (extended_opt) { - extended_opt++; - debug_fields(extended_opt); - } - return 1; - case ARG_VERSION: { - boolean insert_into_pastebuf = FALSE; - - if (extended_opt) { - extended_opt++; - /* Deprecated in favor of "copy" - remove no later - than next major version */ - if (match_optname(extended_opt, "paste", 5, FALSE)) { - insert_into_pastebuf = TRUE; - } else if (match_optname(extended_opt, "copy", 4, FALSE)) { - insert_into_pastebuf = TRUE; - } else if (match_optname(extended_opt, "dump", 4, FALSE)) { - /* version number plus enabled features and sanity - values that the program compares against the same - thing recorded in save and bones files to check - whether they're being used compatibly */ - dump_version_info(); - return 2; /* done */ - } else if (!match_optname(extended_opt, "show", 4, FALSE)) { - raw_printf("-%sversion can only be extended with" - " -%sversion:copy or :dump or :show.\n", - dashdash, dashdash); - /* exit after we've reported bad command line argument */ - return 2; - } - } - early_version_info(insert_into_pastebuf); - return 2; - } - case ARG_SHOWPATHS: - return 2; -#ifndef NODUMPENUMS - case ARG_DUMPENUMS: - dump_enums(); - return 2; -#endif - case ARG_DUMPGLYPHIDS: - dump_glyphids(); - return 2; - case ARG_DUMPMONGEN: - dump_mongen(); - return 2; - case ARG_DUMPWEIGHTS: - dump_weights(); - return 2; -#ifdef CRASHREPORT - case ARG_BIDSHOW: - crashreport_bidshow(); - return 2; -#endif -#ifdef WIN32 - case ARG_WINDOWS: - if (extended_opt) { - extended_opt++; - return windows_early_options(extended_opt); - } - FALLTHROUGH; - /*FALLTHRU*/ -#endif - default: - break; - } - }; - return 0; -} - -/* - * These are internal controls to aid developers with - * testing and debugging particular aspects of the code. - * They are not player options and the only place they - * are documented is right here. No gameplay is altered. - * - * test - test whether this parser is working - * ttystatus - TTY: - * immediateflips - WIN32: turn off display performance - * optimization so that display output - * can be debugged without buffering. - * fuzzer - enable fuzzer without debugger intervention. - */ -staticfn void -debug_fields(const char *opts) -{ - char *op; - boolean negated = FALSE; - - while ((op = strchr(opts, ',')) != 0) { - *op++ = 0; - /* recurse */ - debug_fields(op); - } - if (strlen(opts) > BUFSZ / 2) - return; - - - /* strip leading and trailing white space */ - while (isspace((uchar) *opts)) - opts++; - op = eos((char *) opts); - while (--op >= opts && isspace((uchar) *op)) - *op = '\0'; - - if (!*opts) { - /* empty */ - return; - } - while ((*opts == '!') || !strncmpi(opts, "no", 2)) { - if (*opts == '!') - opts++; - else - opts += 2; - negated = !negated; - } - if (match_optname(opts, "test", 4, FALSE)) - iflags.debug.test = negated ? FALSE : TRUE; -#ifdef TTY_GRAPHICS - if (match_optname(opts, "ttystatus", 9, FALSE)) - iflags.debug.ttystatus = negated ? FALSE : TRUE; -#endif -#ifdef WIN32 - if (match_optname(opts, "immediateflips", 14, FALSE)) - iflags.debug.immediateflips = negated ? FALSE : TRUE; -#endif - if (match_optname(opts, "fuzzer", 4, FALSE)) - iflags.fuzzerpending = TRUE; - return; -} - /* convert from time_t to number of seconds */ long timet_to_seconds(time_t ttim) @@ -1169,177 +987,4 @@ timet_delta(time_t etim, time_t stim) /* end and start times */ return (long) difftime(etim, stim); } -#if !defined(NODUMPENUMS) -/* monsdump[] and objdump[] are also used in utf8map.c */ - -#define DUMP_ENUMS -#define UNPREFIXED_COUNT (5) -struct enum_dump monsdump[] = { -#include "monsters.h" - { NUMMONS, "NUMMONS" }, - { NON_PM, "NON_PM" }, - { LOW_PM, "LOW_PM" }, - { HIGH_PM, "HIGH_PM" }, - { SPECIAL_PM, "SPECIAL_PM" } -}; -struct enum_dump objdump[] = { -#include "objects.h" - { NUM_OBJECTS, "NUM_OBJECTS" }, -}; - -#define DUMP_ENUMS_PCHAR -static struct enum_dump defsym_cmap_dump[] = { -#include "defsym.h" - { MAXPCHARS, "MAXPCHARS" }, -}; -#undef DUMP_ENUMS_PCHAR - -#define DUMP_ENUMS_MONSYMS -static struct enum_dump defsym_mon_syms_dump[] = { -#include "defsym.h" - { MAXMCLASSES, "MAXMCLASSES" }, -}; -#undef DUMP_ENUMS_MONSYMS - -#define DUMP_ENUMS_MONSYMS_DEFCHAR -static struct enum_dump defsym_mon_defchars_dump[] = { -#include "defsym.h" -}; -#undef DUMP_ENUMS_MONSYMS_DEFCHAR - -#define DUMP_ENUMS_OBJCLASS_DEFCHARS -static struct enum_dump objclass_defchars_dump[] = { -#include "defsym.h" -}; -#undef DUMP_ENUMS_OBJCLASS_DEFCHARS - -#define DUMP_ENUMS_OBJCLASS_CLASSES -static struct enum_dump objclass_classes_dump[] = { -#include "defsym.h" - { MAXOCLASSES, "MAXOCLASSES" }, -}; -#undef DUMP_ENUMS_OBJCLASS_CLASSES - -#define DUMP_ENUMS_OBJCLASS_SYMS -static struct enum_dump objclass_syms_dump[] = { -#include "defsym.h" -}; -#undef DUMP_ENUMS_OBJCLASS_SYMS - -#define DUMP_ARTI_ENUM -static struct enum_dump arti_enum_dump[] = { -#include "artilist.h" - { AFTER_LAST_ARTIFACT, "AFTER_LAST_ARTIFACT" } -}; -#undef DUMP_ARTI_ENUM - -#undef DUMP_ENUMS - - -#ifndef NODUMPENUMS - -staticfn void -dump_enums(void) -{ - enum enum_dumps { - monsters_enum, - objects_enum, - objects_misc_enum, - defsym_cmap_enum, - defsym_mon_syms_enum, - defsym_mon_defchars_enum, - objclass_defchars_enum, - objclass_classes_enum, - objclass_syms_enum, - arti_enum, - NUM_ENUM_DUMPS - }; - -#define dump_om(om) { om, #om } - static const struct enum_dump omdump[] = { - dump_om(LAST_GENERIC), - dump_om(OBJCLASS_HACK), - dump_om(FIRST_OBJECT), - dump_om(FIRST_AMULET), - dump_om(LAST_AMULET), - dump_om(FIRST_SPELL), - dump_om(LAST_SPELL), - dump_om(MAXSPELL), - dump_om(FIRST_REAL_GEM), - dump_om(LAST_REAL_GEM), - dump_om(FIRST_GLASS_GEM), - dump_om(LAST_GLASS_GEM), - dump_om(NUM_REAL_GEMS), - dump_om(NUM_GLASS_GEMS), - dump_om(MAX_GLYPH), - }; -#undef dump_om - - static const struct enum_dump *const ed[NUM_ENUM_DUMPS] = { - monsdump, objdump, omdump, - defsym_cmap_dump, defsym_mon_syms_dump, - defsym_mon_defchars_dump, - objclass_defchars_dump, - objclass_classes_dump, - objclass_syms_dump, - arti_enum_dump, - }; - - static const struct de_params { - const char *const title; - const char *const pfx; - int unprefixed_count; - int dumpflgs; /* 0 = dump numerically only, 1 = add 'char' comment */ - int szd; - } edmp[NUM_ENUM_DUMPS] = { - { "monnums", "PM_", UNPREFIXED_COUNT, 0, SIZE(monsdump) }, - { "objects_nums", "", 1, 0, SIZE(objdump) }, - { "misc_object_nums", "", 1, 0, SIZE(omdump) }, - { "cmap_symbols", "", 1, 0, SIZE(defsym_cmap_dump) }, - { "mon_syms", "", 1, 0, SIZE(defsym_mon_syms_dump) }, - { "mon_defchars", "", 1, 1, SIZE(defsym_mon_defchars_dump) }, - { "objclass_defchars", "", 1, 1, SIZE(objclass_defchars_dump) }, - { "objclass_classes", "", 1, 0, SIZE(objclass_classes_dump) }, - { "objclass_syms", "", 1, 0, SIZE(objclass_syms_dump) }, - { "artifacts_nums", "", 1, 0, SIZE(arti_enum_dump) }, - }; - - const char *nmprefix; - int i, j, nmwidth; - char comment[BUFSZ]; - - for (i = 0; i < NUM_ENUM_DUMPS; ++ i) { - raw_printf("enum %s = {", edmp[i].title); - for (j = 0; j < edmp[i].szd; ++j) { - nmprefix = (j >= edmp[i].szd - edmp[i].unprefixed_count) - ? "" : edmp[i].pfx; /* "" or "PM_" */ - nmwidth = 27 - (int) strlen(nmprefix); /* 27 or 24 */ - if (edmp[i].dumpflgs > 0) { - Snprintf(comment, sizeof comment, - " /* '%c' */", - (ed[i][j].val >= 32 && ed[i][j].val <= 126) - ? ed[i][j].val : ' '); - } else { - comment[0] = '\0'; - } - raw_printf(" %s%*s = %3d,%s", - nmprefix, -nmwidth, - ed[i][j].nm, ed[i][j].val, - comment); - } - raw_print("};"); - raw_print(""); - } - raw_print(""); -} -#undef UNPREFIXED_COUNT -#endif /* NODUMPENUMS */ - -void -dump_glyphids(void) -{ - dump_all_glyphids(stdout); -} -#endif /* !NODUMPENUMS */ - /*allmain.c*/ diff --git a/src/apply.c b/src/apply.c index 4e3788dd4..0fa915a42 100644 --- a/src/apply.c +++ b/src/apply.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 apply.c $NHDT-Date: 1753856387 2025/07/29 22:19:47 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.472 $ */ +/* NetHack 3.7 apply.c $NHDT-Date: 1769342601 2026/01/25 04:03:21 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.475 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -52,7 +52,7 @@ staticfn boolean check_jump(genericptr_t, coordxy, coordxy); staticfn boolean is_valid_jump_pos(coordxy, coordxy, int, boolean); staticfn boolean get_valid_jump_position(coordxy, coordxy); staticfn boolean get_valid_polearm_position(coordxy, coordxy); -staticfn boolean find_poleable_mon(coord *, int, int); +staticfn boolean find_poleable_mon(coord *); static const char no_elbow_room[] = "don't have enough elbow-room to maneuver."; @@ -500,7 +500,7 @@ use_magic_whistle(struct obj *obj) You("produce a %shigh-%s.", Underwater ? "very " : "", Deaf ? "frequency vibration" : "pitched humming noise"); wake_nearby(TRUE); - if (!rn2(2)) + if (!rn2(2) && !noteleport_level(&gy.youmonst)) tele_to_rnd_pet(); } else { /* it's magic! it works underwater too (at a higher pitch) */ @@ -526,6 +526,10 @@ magic_whistled(struct obj *obj) already_discovered = objects[obj->otyp].oc_name_known != 0; int omx, omy, shift = 0, appear = 0, disappear = 0, trapped = 0; + /* stasis prevents magic-whistling */ + if (svl.level.flags.stasis_until >= svm.moves) + return; + /* need to copy (up to 3) names as they're collected rather than just save pointers to them, otherwise churning through every mbuf[] might clobber the ones we care about */ @@ -2583,6 +2587,8 @@ grease_ok(struct obj *obj) if (!obj) return GETOBJ_SUGGEST; + /* note: if changing the list of ungreasable objects, also change + special_throne_effect in sit.c */ if (obj->oclass == COIN_CLASS) return GETOBJ_EXCLUDE; @@ -2683,7 +2689,7 @@ use_stone(struct obj *tstone) /* in case it was acquired while blinded */ if (!Blind) - tstone->dknown = 1; + observe_object(tstone); known = (tstone->otyp == TOUCHSTONE && tstone->dknown && objects[TOUCHSTONE].oc_name_known); Sprintf(stonebuf, "rub on the stone%s", plur(tstone->quan)); @@ -3274,7 +3280,7 @@ static const char /* find pos of monster in range, if only one monster */ staticfn boolean -find_poleable_mon(coord *pos, int min_range, int max_range) +find_poleable_mon(coord *pos) { struct monst *mtmp; coord mpos = { 0, 0 }; /* no candidate location yet */ @@ -3283,13 +3289,12 @@ find_poleable_mon(coord *pos, int min_range, int max_range) int glyph; impaired = (Confusion || Stunned || Hallucination); - rt = isqrt(max_range); + rt = isqrt(gp.polearm_range_max); lo_x = max(u.ux - rt, 1), hi_x = min(u.ux + rt, COLNO - 1); lo_y = max(u.uy - rt, 0), hi_y = min(u.uy + rt, ROWNO - 1); for (x = lo_x; x <= hi_x; ++x) { for (y = lo_y; y <= hi_y; ++y) { - if (distu(x, y) < min_range || distu(x, y) > max_range - || !isok(x, y) || !cansee(x, y)) + if (!get_valid_polearm_position(x, y)) continue; glyph = glyph_at(x, y); if (!impaired @@ -3395,7 +3400,7 @@ could_pole_mon(void) cc.x = u.ux; cc.y = u.uy; - if (!find_poleable_mon(&cc, min_range, max_range)) { + if (!find_poleable_mon(&cc)) { if (hitm && !DEADMONSTER(hitm) && sensemon(hitm) && mdistu(hitm) <= max_range && mdistu(hitm) >= min_range) return TRUE; @@ -3448,7 +3453,7 @@ use_pole(struct obj *obj, boolean autohit) pline(where_to_hit); cc.x = u.ux; cc.y = u.uy; - if (!find_poleable_mon(&cc, min_range, max_range) && hitm + if (!find_poleable_mon(&cc) && hitm && !DEADMONSTER(hitm) && sensemon(hitm) && mdistu(hitm) <= max_range && mdistu(hitm) >= min_range) { cc.x = hitm->mx; @@ -3982,6 +3987,7 @@ do_break_wand(struct obj *obj) case WAN_PROBING: case WAN_ENLIGHTENMENT: case WAN_SECRET_DOOR_DETECTION: + case WAN_STASIS: pline(nothing_else_happens); discard_broken_wand(); return ECMD_TIME; diff --git a/src/artifact.c b/src/artifact.c index e4f4c89bf..e0e3df173 100644 --- a/src/artifact.c +++ b/src/artifact.c @@ -40,6 +40,8 @@ staticfn int invoke_banish(struct obj *) NONNULLARG1; staticfn int invoke_fling_poison(struct obj *) NONNULLARG1; staticfn int invoke_storm_spell(struct obj *) NONNULLARG1; staticfn int invoke_blinding_ray(struct obj *) NONNULLARG1; +staticfn int arti_invoke_cost_pw(struct obj *) NONNULLARG1; +staticfn boolean arti_invoke_cost(struct obj *) NONNULLARG1; staticfn int arti_invoke(struct obj *); staticfn boolean Mb_hit(struct monst * magr, struct monst *mdef, struct obj *, int *, int, boolean, char *); @@ -1570,7 +1572,7 @@ artifact_hit( } *dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER; pline("%s cuts %s in half!", wepdesc, mon_nam(mdef)); - otmp->dknown = TRUE; + observe_object(otmp); return TRUE; } else { if (bigmonst(gy.youmonst.data)) { @@ -1587,7 +1589,7 @@ artifact_hit( */ *dmgptr = 2 * (Upolyd ? u.mh : u.uhp) + FATAL_DAMAGE_MODIFIER; pline("%s cuts you in half!", wepdesc); - otmp->dknown = TRUE; + observe_object(otmp); return TRUE; } } else if (is_art(otmp, ART_VORPAL_BLADE) @@ -1617,7 +1619,7 @@ artifact_hit( mon_nam(mdef)); if (Hallucination && !flags.female) pline("Good job Henry, but that wasn't Anne."); - otmp->dknown = TRUE; + observe_object(otmp); return TRUE; } else { if (!has_head(gy.youmonst.data)) { @@ -1634,7 +1636,7 @@ artifact_hit( } *dmgptr = 2 * (Upolyd ? u.mh : u.uhp) + FATAL_DAMAGE_MODIFIER; pline(ROLL_FROM(behead_msg), wepdesc, "you"); - otmp->dknown = TRUE; + observe_object(otmp); /* Should amulets fall off? */ return TRUE; } @@ -2083,6 +2085,48 @@ invoke_blinding_ray(struct obj *obj) return ECMD_TIME; } +/* return the amount of Pw invoking an object costs. + return a negative value, if obj invoking cannot be paid with Pw */ +staticfn int +arti_invoke_cost_pw(struct obj *obj) +{ + const struct artifact *oart = get_artifact(obj); + + if (oart->inv_prop == FLING_POISON + || oart->inv_prop == BLINDING_RAY) { + /* pretend it's a level 5 spell */ + return SPELL_LEV_PW(5); + } + + return -1; +} + +/* return TRUE if artifact object's invoke cost can be paid (and pay it) */ +staticfn boolean +arti_invoke_cost(struct obj *obj) +{ + if (obj->age > svm.moves) { + int pw_cost = arti_invoke_cost_pw(obj); + + if (pw_cost < 0 || u.uen < pw_cost) { + /* the artifact is tired :-) */ + You_feel("that %s %s ignoring you.", the(xname(obj)), + otense(obj, "are")); + /* and just got more so; patience is essential... */ + obj->age += (long) d(3, 10); + return FALSE; + } else { + /* you pay invoke cost with your own magic */ + You_feel("drained..."); + u.uen -= pw_cost; + disp.botl = TRUE; + } + } else { + obj->age = svm.moves + rnz(100); + } + return TRUE; +} + staticfn int arti_invoke(struct obj *obj) { @@ -2102,17 +2146,10 @@ arti_invoke(struct obj *obj) return ECMD_TIME; } + /* It's a special power, not "just" a property */ if (oart->inv_prop > LAST_PROP) { - /* It's a special power, not "just" a property */ - if (obj->age > svm.moves) { - /* the artifact is tired :-) */ - You_feel("that %s %s ignoring you.", the(xname(obj)), - otense(obj, "are")); - /* and just got more so; patience is essential... */ - obj->age += (long) d(3, 10); + if (!arti_invoke_cost(obj)) return ECMD_TIME; - } - obj->age = svm.moves + rnz(100); switch (oart->inv_prop) { case TAMING: res = invoke_taming(obj); break; diff --git a/src/attrib.c b/src/attrib.c index fc6a7aebf..23e8117c7 100644 --- a/src/attrib.c +++ b/src/attrib.c @@ -187,11 +187,14 @@ adjattrib( return FALSE; } + /* Any successful change also resets abuse / exercise level */ + AEXE(ndx) = 0; + disp.botl = TRUE; if (msgflg <= 0) You_feel("%s%s!", (incr > 1 || incr < -1) ? "very " : "", attrstr); if (program_state.in_moveloop && (ndx == A_STR || ndx == A_CON)) - (void) encumber_msg(); + encumber_msg(); return TRUE; } @@ -401,7 +404,7 @@ poisoned( /* "Poisoned by a poisoned ___" is redundant */ done(strstri(pkiller, "poison") ? DIED : POISONING); } - (void) encumber_msg(); + encumber_msg(); } void @@ -477,7 +480,7 @@ restore_attrib(void) } } if (disp.botl) - (void) encumber_msg(); + encumber_msg(); } #define AVAL 50 /* tune value for exercise gains */ @@ -511,7 +514,7 @@ exercise(int i, boolean inc_or_dec) (inc_or_dec) ? "inc" : "dec", AEXE(i)); } if (svm.moves > 0 && (i == A_STR || i == A_CON)) - (void) encumber_msg(); + encumber_msg(); } staticfn void @@ -753,7 +756,7 @@ redist_attr(void) if (ABASE(i) < ATTRMIN(i)) ABASE(i) = ATTRMIN(i); } - /* (void) encumber_msg(); -- caller needs to do this */ + /* encumber_msg(); -- caller needs to do this */ } /* apply minor variation to attributes */ diff --git a/src/ball.c b/src/ball.c index ebacb7e83..069a71ce4 100644 --- a/src/ball.c +++ b/src/ball.c @@ -34,7 +34,7 @@ ballrelease(boolean showmsg) /* [this used to test 'if (uwep != uball)' but that always passes after the setuwep() above] */ freeinv(uball); /* remove from inventory but don't place on floor */ - (void) encumber_msg(); + encumber_msg(); } } diff --git a/src/botl.c b/src/botl.c index f3ad98aa5..934d9e7ee 100644 --- a/src/botl.c +++ b/src/botl.c @@ -1,12 +1,9 @@ -/* NetHack 3.7 botl.c $NHDT-Date: 1742207239 2025/03/17 02:27:19 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.274 $ */ +/* NetHack 3.7 botl.c $NHDT-Date: 1769839231 2026/01/30 22:00:31 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.277 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" -#ifndef LONG_MAX -#include -#endif extern const char *const hu_stat[]; /* defined in eat.c */ @@ -19,9 +16,8 @@ const char *const enc_stat[] = { staticfn const char *rank(void); staticfn void bot_via_windowport(void); staticfn void stat_update_time(void); -staticfn char *get_strength_str(void); -staticfn char * +char * get_strength_str(void) { static char buf[32]; @@ -61,7 +57,7 @@ do_statusline1(void) Strcpy(newbot1, svp.plname); if ('a' <= newbot1[0] && newbot1[0] <= 'z') newbot1[0] += 'A' - 'a'; - newbot1[10] = 0; + newbot1[BOTL_NSIZ] = 0; Sprintf(nb = eos(newbot1), " the "); if (Upolyd) { @@ -773,12 +769,12 @@ bot_via_windowport(void) nb[0] = highc(nb[0]); titl = !Upolyd ? rank() : pmname(&mons[u.umonnum], Ugender); i = (int) (strlen(buf) + sizeof " the " + strlen(titl) - sizeof ""); - /* if "Name the Rank/monster" is too long, we truncate the name - but always keep at least 10 characters of it; when hitpointbar is + /* if "Name the Rank/monster" is too long, we truncate the name but + always keep at least BOTL_NSIZ characters of it; when hitpointbar is enabled, anything beyond 30 (long monster name) will be truncated */ if (i > 30) { i = 30 - (int) (sizeof " the " + strlen(titl) - sizeof ""); - nb[max(i, 10)] = '\0'; + nb[max(i, BOTL_NSIZ)] = '\0'; } Strcpy(nb = eos(nb), " the "); Strcpy(nb = eos(nb), titl); @@ -3641,6 +3637,7 @@ status_hilite_menu_add(int origfld) unsigned long cond = 0UL; char colorqry[BUFSZ]; char attrqry[BUFSZ]; + int retry = 0; choose_field: fld = origfld; @@ -3676,6 +3673,10 @@ status_hilite_menu_add(int origfld) hilite.behavior = behavior; choose_value: + if (retry++ > 5) { + pline("That's enough tries."); + return FALSE; + } if (behavior == BL_TH_VAL_PERCENTAGE || behavior == BL_TH_VAL_ABSOLUTE) { char inbuf[BUFSZ], buf[BUFSZ]; @@ -4302,7 +4303,9 @@ status_hilite_menu(void) countall = status_hilite_linestr_countfield(BL_FLUSH); status_hilite_linestr_done(); - if (redo) + /* fuzzer is unlikely to pick something useful within nested menus; + limit it to one try */ + if (redo && !iflags.debug_fuzzer) goto shlmenu_redo; /* hilite_delta=='statushilites' does double duty: it is the diff --git a/src/cfgfiles.c b/src/cfgfiles.c index 3a3c0ff06..2ba0c2fde 100644 --- a/src/cfgfiles.c +++ b/src/cfgfiles.c @@ -34,7 +34,7 @@ staticfn void free_config_sections(void); staticfn char *is_config_section(char *); staticfn boolean handle_config_section(char *); boolean parse_config_line(char *); -staticfn char *find_optparam(const char *); +staticfn char *find_optparam(char *); #ifndef SFCTOOL staticfn boolean cnf_line_OPTIONS(char *); staticfn boolean cnf_line_AUTOPICKUP_EXCEPTION(char *); @@ -110,6 +110,8 @@ staticfn void parse_conf_buf(struct _cnf_parser_state *parser, boolean (*proc)(char *arg)); /* next one is in extern.h; why here too? */ boolean parse_conf_str(const char *str, boolean (*proc)(char *arg)); +static boolean ignore_errors_on_unmatched = FALSE, + ignore_statement_errors = FALSE; #ifdef SFCTOOL #ifdef wait_synch @@ -242,6 +244,13 @@ fopen_config_file(const char *filename, int src) if (filename && *filename) { set_configfile_name(filename); #ifdef UNIX + if (!strncmp(configfile, "~/", 2) && (envp = nh_getenv("HOME")) != 0) { + /* support for command line '--nethackrc=~/path' (or for + NETHACKOPTIONS='@~/path'; we don't support ~user/path) */ + Snprintf(tmp_config, sizeof tmp_config, "%s/%s", + envp, configfile + 2); /* insert $HOME/ and remove ~/ */ + set_configfile_name(tmp_config); + } if (access(configfile, 4) == -1) { /* 4 is R_OK on newer systems */ /* nasty sneaky attempt to read file through * NetHack's setuid permissions -- this is the only @@ -576,7 +585,7 @@ handle_config_section(char *buf) /* find the '=' or ':' */ staticfn char * -find_optparam(const char *buf) +find_optparam(char *buf) { char *bufp, *altp; @@ -1196,7 +1205,8 @@ cnf_line_SYMBOLS(char *bufp) switch_symbols(TRUE); return TRUE; } - config_error_add("Error in SYMBOLS definition '%s'", bufp); + if (!config_unmatched_ignored()) + config_error_add("Error in SYMBOLS definition '%s'", bufp); return FALSE; } @@ -1288,7 +1298,7 @@ typedef boolean (*config_line_stmt_func)(char *); /* normal, alias */ #define CNFL_NA(n, l, f) { #n, l, FALSE, FALSE, cnf_line_##f } /* sysconf only */ -#define CNFL_S(n, l) { #n, l, TRUE, FALSE, cnf_line_##n } +#define CNFL_S(n, l) { #n, l, TRUE, FALSE, cnf_line_##n } static const struct match_config_line_stmt { const char *name; @@ -1373,6 +1383,8 @@ static const struct match_config_line_stmt { #undef CNFL_NA #undef CNFL_S +static boolean disregarded_config_lines[SIZE(config_line_stmt)]; + boolean parse_config_line(char *origbuf) { @@ -1398,7 +1410,8 @@ parse_config_line(char *origbuf) /* find the '=' or ':' */ bufp = find_optparam(buf); if (!bufp) { - config_error_add("Not a config statement, missing '='"); + if (!ignore_statement_errors) + config_error_add("Not a config statement, missing '='"); return FALSE; } /* skip past '=', then space between it and value, if any */ @@ -1415,11 +1428,13 @@ parse_config_line(char *origbuf) config_line_stmt[i].len)) { char *parm = config_line_stmt[i].origbuf ? origbuf : bufp; - return config_line_stmt[i].fn(parm); + if (!disregarded_config_lines[i]) + return config_line_stmt[i].fn(parm); } } - config_error_add("Unknown config statement"); + if (!ignore_errors_on_unmatched) + config_error_add("Unknown config statement"); return FALSE; } @@ -1874,6 +1889,142 @@ vconfig_error_add(const char *str, va_list the_args) config_erradd(buf); } +#ifndef SFCTOOL +void +rcfile(void) +{ + char *opts = 0, *xtraopts = 0; + const char *envname, *namesrc, *nameval; + + go.opt_phase = environ_opt; + /* getenv() instead of nhgetenv(): let total length of options be long; + parseoptions() will check each individually */ + envname = "NETHACKOPTIONS"; + opts = getenv(envname); + if (!opts) { + /* fall back to original name; discouraged */ + envname = "HACKOPTIONS"; + opts = getenv(envname); + } + + if (gc.cmdline_rcfile) { + namesrc = "command line"; + nameval = gc.cmdline_rcfile; + xtraopts = opts; + if (opts && (*opts == '/' || *opts == '\\' || *opts == '@')) + xtraopts = 0; /* NETHACKOPTIONS is a file name; ignore it */ + } else if (opts && (*opts == '/' || *opts == '\\' || *opts == '@')) { + /* NETHACKOPTIONS is a file name; use that instead of the default */ + if (*opts == '@') + ++opts; /* @filename */ + namesrc = envname; + nameval = opts; + xtraopts = 0; + } else { + /* either no NETHACKOPTIONS or it wasn't a file name; + read the default configuration file */ + nameval = namesrc = 0; + xtraopts = opts; + } + + go.opt_phase = rc_file_opt; + /* seemingly arbitrary name length restriction is to prevent error + messages, if any were to be delivered while accessing the file, + from potentially overflowing buffers */ + if (nameval && (int) strlen(nameval) >= BUFSZ / 2) { + config_error_init(TRUE, namesrc, FALSE); + config_error_add( + "nethackrc file name \"%.40s\"... too long; using default", + nameval); + config_error_done(); + nameval = namesrc = 0; /* revert to default nethackrc */ + } + + config_error_init(TRUE, nameval, nameval ? CONFIG_ERROR_SECURE : FALSE); + (void) read_config_file(nameval, set_in_config); + config_error_done(); + if (xtraopts) { + /* NETHACKOPTIONS is present and not a file name */ + go.opt_phase = environ_opt; + config_error_init(FALSE, envname, FALSE); + (void) parseoptions(xtraopts, TRUE, FALSE); + config_error_done(); + } + + if (gc.cmdline_rcfile) + free((genericptr_t) gc.cmdline_rcfile), gc.cmdline_rcfile = 0; + /*[end of nethackrc handling]*/ +} + +void +rcfile_interface_options(void) +{ + allopt_array_init(); + disregard_all_options(); + disregard_all_config_statements(); + heed_this_option(opt_windowtype); + heed_this_option(opt_soundlib); + set_ignore_errors_on_unmatched(); + ignore_statement_errors = TRUE; + rcfile(); + heed_all_config_statements(); + heed_all_options(); + disregard_this_option(opt_windowtype); + disregard_this_option(opt_soundlib); + clear_ignore_errors_on_unmatched(); + ignore_statement_errors = FALSE; +} + +void +heed_all_config_statements(void) +{ + int i; + + for (i = 0; i < SIZE(disregarded_config_lines); i++) { + disregarded_config_lines[i] = FALSE; + } +} +void +disregard_all_config_statements(void) +{ + int i; + + for (i = 0; i < SIZE(disregarded_config_lines); i++) { + disregarded_config_lines[i] = TRUE; + } +} +void +heed_this_config_statement(int statement_idx) +{ + if (statement_idx >= 0 && statement_idx < SIZE(disregarded_config_lines)) + disregarded_config_lines[statement_idx] = FALSE; +} +void +disregard_this_config_statement(int statement_idx) +{ + if (statement_idx >= 0 && statement_idx < SIZE(disregarded_config_lines)) + disregarded_config_lines[statement_idx] = TRUE; +} + +void +clear_ignore_errors_on_unmatched(void) +{ + ignore_errors_on_unmatched = FALSE; +} +void +set_ignore_errors_on_unmatched(void) +{ + ignore_errors_on_unmatched = TRUE; +} +boolean +config_unmatched_ignored(void) +{ + if (ignore_errors_on_unmatched) + return TRUE; + return FALSE; +} +#endif /* SFCTOOL */ + #ifdef SYSCF #ifdef SYSCF_FILE void @@ -1900,9 +2051,9 @@ assure_syscf_file(void) #else fd = open(SYSCF_FILE, O_RDONLY); #endif -#else +#else /* VMS */ fd = open(SYSCF_FILE, O_RDONLY, 0); -#endif +#endif /* VMS */ if (fd >= 0) { /* readable */ close(fd); diff --git a/src/cmd.c b/src/cmd.c index 3eb8a634e..de20c283b 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 cmd.c $NHDT-Date: 1736401574 2025/01/08 21:46:14 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.744 $ */ +/* NetHack 3.7 cmd.c $NHDT-Date: 1762680996 2025/11/09 01:36:36 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.755 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -94,6 +94,10 @@ extern int dozap(void); /**/ extern int doorganize(void); /**/ #endif /* DUMB */ +staticfn struct Cmd_bind *cmdbind_get(uchar); +staticfn void cmdbind_add(uchar, const struct ext_func_tab *, boolean); +staticfn void cmdbind_remove(uchar); +staticfn void cmdbind_swapkeys(uchar, uchar); staticfn int dosuspend_core(void); staticfn int dosh_core(void); staticfn int doherecmdmenu(void); @@ -1364,6 +1368,21 @@ dolookaround(void) return ECMD_OK; } +/* #toggle extended command + + BIND=':toggle(price_quotes) + BIND=@:toggle(autopickup) */ +int +dotoggleoption(void) +{ + if (gc.cmd_bind && gc.cmd_bind->param) { + return toggle_bool_option(gc.cmd_bind->param); + } else { + pline("Use #optionsfull to set any option instead."); + return ECMD_OK; + } +} + void set_move_cmd(int dir, int run) { @@ -1652,26 +1671,26 @@ struct ext_func_tab extcmdlist[] = { doextlist, IFBURIED | AUTOCOMPLETE | GENERALCMD | CMD_M_PREFIX, NULL }, { M('a'), "adjust", "adjust inventory letters", - doorganize, IFBURIED | AUTOCOMPLETE, NULL }, + doorganize, IFBURIED | AUTOCOMPLETE | GENERALCMD, NULL }, { M('A'), "annotate", "name current level", - donamelevel, IFBURIED | AUTOCOMPLETE | GENERALCMD, NULL }, + donamelevel, IFBURIED | AUTOCOMPLETE | GENERALCMD | CMD_M_PREFIX, NULL }, { 'a', "apply", "apply (use) a tool (pick-axe, key, lamp...)", doapply, CMD_M_PREFIX, NULL }, { C('x'), "attributes", "show your attributes", - doattributes, IFBURIED, NULL }, + doattributes, IFBURIED | GENERALCMD, NULL }, { '@', "autopickup", "toggle the 'autopickup' option on/off", - dotogglepickup, IFBURIED, NULL }, + dotogglepickup, IFBURIED | GENERALCMD, NULL }, #ifdef CRASHREPORT { '\0', "bugreport", "file a bug report", dobugreport, GENERALCMD | NOFUZZERCMD, NULL }, #endif { 'C', "call", "name a monster, specific object, or type of object", - docallcmd, IFBURIED, NULL }, + docallcmd, IFBURIED | GENERALCMD, NULL }, { 'Z', "cast", "zap (cast) a spell", docast, IFBURIED, NULL }, { M('c'), "chat", "talk to someone", dotalk, IFBURIED | AUTOCOMPLETE, NULL }, - { '\0', "chronicle", "show journal of major events", + { 'v', "chronicle", "show journal of major events", do_gamelog, IFBURIED | AUTOCOMPLETE | GENERALCMD, NULL }, { 'c', "close", "close a door", doclose, 0, NULL }, @@ -1695,7 +1714,7 @@ struct ext_func_tab extcmdlist[] = { { 'E', "engrave", "engrave writing on the floor", doengrave, 0, NULL }, { M('e'), "enhance", "advance or check weapon and spell skills", - enhance_weapon_skill, IFBURIED | AUTOCOMPLETE, NULL }, + enhance_weapon_skill, IFBURIED | AUTOCOMPLETE | GENERALCMD, NULL }, /* #exploremode should be flagged AUTOCOMPETE but that would negatively impact frequently used #enhance by making #e become ambiguous */ { M('X'), "exploremode", "enter explore (discovery) mode", @@ -1716,12 +1735,12 @@ struct ext_func_tab extcmdlist[] = { dohelp, IFBURIED | GENERALCMD, NULL }, { '\0', "herecmdmenu", "show menu of commands you can do here", doherecmdmenu, IFBURIED | AUTOCOMPLETE | GENERALCMD, NULL }, - { 'V', "history", "show long version and game history", + { '\0', "history", "show long version and game history", dohistory, IFBURIED | GENERALCMD, NULL }, { 'i', "inventory", "show your inventory", - ddoinv, IFBURIED, NULL }, + ddoinv, IFBURIED | GENERALCMD, NULL }, { 'I', "inventtype", "show inventory of one specific item class", - dotypeinv, IFBURIED, NULL }, + dotypeinv, IFBURIED | GENERALCMD, NULL }, { M('i'), "invoke", "invoke an object's special powers", doinvoke, IFBURIED | AUTOCOMPLETE, NULL }, { M('j'), "jump", "jump to another location", @@ -1752,7 +1771,7 @@ struct ext_func_tab extcmdlist[] = { { M('m'), "monster", "use monster's special ability", domonability, IFBURIED | AUTOCOMPLETE, NULL }, { M('n'), "name", "same as call; name a monster or object or object type", - docallcmd, IFBURIED | AUTOCOMPLETE, NULL }, + docallcmd, IFBURIED | AUTOCOMPLETE | GENERALCMD, NULL }, { M('o'), "offer", "offer a sacrifice to the gods", dosacrifice, AUTOCOMPLETE | CMD_M_PREFIX, NULL }, { 'o', "open", "open a door", @@ -1827,17 +1846,17 @@ struct ext_func_tab extcmdlist[] = { { 's', "search", "search for traps and secret doors", dosearch, IFBURIED | CMD_M_PREFIX, "searching" }, { '*', "seeall", "show all equipment in use", - doprinuse, IFBURIED | CMD_M_PREFIX, NULL }, + doprinuse, IFBURIED | GENERALCMD | CMD_M_PREFIX, NULL }, { AMULET_SYM, "seeamulet", "show the amulet currently worn", - dopramulet, IFBURIED | CMD_M_PREFIX, NULL }, + dopramulet, IFBURIED | GENERALCMD | CMD_M_PREFIX, NULL }, { ARMOR_SYM, "seearmor", "show the armor currently worn", - doprarm, IFBURIED | CMD_M_PREFIX, NULL }, + doprarm, IFBURIED | GENERALCMD | CMD_M_PREFIX, NULL }, { RING_SYM, "seerings", "show the ring(s) currently worn", - doprring, IFBURIED | CMD_M_PREFIX, NULL }, + doprring, IFBURIED | GENERALCMD | CMD_M_PREFIX, NULL }, { TOOL_SYM, "seetools", "show the tools currently in use", - doprtool, IFBURIED | CMD_M_PREFIX, NULL }, + doprtool, IFBURIED | GENERALCMD | CMD_M_PREFIX, NULL }, { WEAPON_SYM, "seeweapon", "show the weapon currently wielded", - doprwep, IFBURIED | CMD_M_PREFIX, NULL }, + doprwep, IFBURIED | GENERALCMD | CMD_M_PREFIX, NULL }, { '!', "shell", "leave game to enter a sub-shell ('exit' to come back)", dosh_core, (IFBURIED | GENERALCMD | NOFUZZERCMD @@ -1847,11 +1866,11 @@ struct ext_func_tab extcmdlist[] = { ), NULL }, /* $ is like ),=,&c but is not included with *, so not called "seegold" */ { GOLD_SYM, "showgold", "show gold, possibly shop credit or debt", - doprgold, IFBURIED | CMD_M_PREFIX, NULL }, + doprgold, IFBURIED | GENERALCMD | CMD_M_PREFIX, NULL }, { SPBOOK_SYM, "showspells", "list and reorder known spells", - dovspell, IFBURIED, NULL }, + dovspell, IFBURIED | GENERALCMD, NULL }, { '^', "showtrap", "describe an adjacent, discovered trap", - doidtrap, IFBURIED, NULL }, + doidtrap, IFBURIED | GENERALCMD, NULL }, { M('s'), "sit", "sit down", dosit, AUTOCOMPLETE, NULL }, { '\0', "stats", "show memory statistics", @@ -1875,7 +1894,7 @@ struct ext_func_tab extcmdlist[] = { it may or may not actually invoke the #terrain command */ { '\177', "terrain", "view map without monsters or objects obstructing it", - doterrain, IFBURIED | AUTOCOMPLETE, NULL }, + doterrain, IFBURIED | GENERALCMD | AUTOCOMPLETE, NULL }, { '\0', "therecmdmenu", "menu of commands you can do from here to adjacent spot", dotherecmdmenu, AUTOCOMPLETE | GENERALCMD | MOUSECMD, NULL }, @@ -1885,6 +1904,8 @@ struct ext_func_tab extcmdlist[] = { wiz_timeout_queue, IFBURIED | AUTOCOMPLETE | WIZMODECMD, NULL }, { M('T'), "tip", "empty a container", dotip, AUTOCOMPLETE | CMD_M_PREFIX, NULL }, + { '\0', "toggle", "toggle boolean option", + dotoggleoption, IFBURIED | GENERALCMD | CMD_PARAM, NULL }, { '_', "travel", "travel to a specific location on the map", dotravel, CMD_M_PREFIX, NULL }, { M('t'), "turn", "turn undead away", @@ -1902,8 +1923,8 @@ struct ext_func_tab extcmdlist[] = { { M('v'), "version", "list compile time options for this version of NetHack", doextversion, IFBURIED | AUTOCOMPLETE | GENERALCMD, NULL }, - { 'v', "versionshort", "show version and date+time program was built", - doversion, IFBURIED | GENERALCMD, NULL }, + { 'V', "versionshort", "show version and date+time program was built", + doversion, IFBURIED | GENERALCMD | CMD_M_PREFIX, NULL }, { '\0', "vision", "show vision array", wiz_show_vision, IFBURIED | AUTOCOMPLETE | WIZMODECMD, NULL }, { '.', "wait", "rest one move while doing nothing", @@ -1911,7 +1932,7 @@ struct ext_func_tab extcmdlist[] = { { 'W', "wear", "wear a piece of armor", dowear, 0, NULL }, { '&', "whatdoes", "tell what a command does", - dowhatdoes, IFBURIED, NULL }, + dowhatdoes, IFBURIED | GENERALCMD, NULL }, { '/', "whatis", "show what type of thing a symbol corresponds to", dowhatis, IFBURIED | GENERALCMD, NULL }, { 'w', "wield", "wield (put in use) a weapon", @@ -1952,6 +1973,10 @@ struct ext_func_tab extcmdlist[] = { wiz_load_splua, IFBURIED | WIZMODECMD | NOFUZZERCMD, NULL }, { '\0', "wizloadlua", "load and execute a lua script", wiz_load_lua, IFBURIED | WIZMODECMD | NOFUZZERCMD, NULL }, +#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) + { '\0', "wizobjprobs", "list object generation probabilities", + wiz_objprobs, IFBURIED | WIZMODECMD, NULL }, +#endif { '\0', "wizmakemap", "recreate the current level", wiz_makemap, IFBURIED | WIZMODECMD, NULL }, { C('f'), "wizmap", "map the level", @@ -1971,7 +1996,7 @@ struct ext_func_tab extcmdlist[] = { { '\0', "wizwhere", "show locations of special levels", wiz_where, IFBURIED | AUTOCOMPLETE | WIZMODECMD, NULL }, { C('w'), "wizwish", "wish for something", - wiz_wish, IFBURIED | WIZMODECMD, NULL }, + wiz_wish, IFBURIED | CMD_M_PREFIX | WIZMODECMD, NULL }, { '\0', "wmode", "show wall modes", wiz_show_wmodes, IFBURIED | AUTOCOMPLETE | WIZMODECMD, NULL }, { 'z', "zap", "zap a wand", @@ -2032,8 +2057,9 @@ struct ext_func_tab extcmdlist[] = { /* internal commands: only used by game core, not available for user */ { '\0', "clicklook", NULL, doclicklook, INTERNALCMD | MOUSECMD, NULL }, { '\0', "mouseaction", NULL, domouseaction, INTERNALCMD | MOUSECMD, NULL }, - { '\0', "altdip", NULL, dip_into, INTERNALCMD, NULL }, { '\0', "altadjust", NULL, adjust_split, INTERNALCMD, NULL }, + { '\0', "altdip", NULL, dip_into, INTERNALCMD, NULL }, + { '\0', "alttakeoff", NULL, ia_dotakeoff, INTERNALCMD, NULL }, { '\0', "altunwield", NULL, remarm_swapwep, INTERNALCMD, NULL }, { '\0', (char *) 0, (char *) 0, donull, 0, (char *) 0 } /* sentinel */ }; @@ -2077,17 +2103,128 @@ extcmds_getentry(int i) return &extcmdlist[i]; } +/* get the command bound to a key */ +staticfn struct Cmd_bind * +cmdbind_get(uchar key) +{ + struct Cmd_bind *bind = gc.Cmd.cmdbinds; + + if (!key) + return NULL; + + while (bind) { + if (bind->key == key) + return bind; + bind = bind->next; + } + return bind; +} + +staticfn void +cmdbind_add(uchar key, const struct ext_func_tab *extcmd, boolean user) +{ + struct Cmd_bind *bind = cmdbind_get(key); + + if (!key) + return; + if (!extcmd && bind) { + cmdbind_remove(key); + return; + } + + /* binding exists, set it to this command */ + if (bind) { + bind->cmd = extcmd; + bind->userbind = user; + if (bind->param) { + free(bind->param); + bind->param = NULL; + } + return; + } else { + bind = (struct Cmd_bind *) alloc(sizeof(struct Cmd_bind)); + bind->key = key; + bind->userbind = user; + bind->param = NULL; + bind->cmd = extcmd; + bind->next = gc.Cmd.cmdbinds; + gc.Cmd.cmdbinds = bind; + } +} + +staticfn void +cmdbind_remove(uchar key) +{ + struct Cmd_bind *bind = gc.Cmd.cmdbinds; + struct Cmd_bind *prev = (struct Cmd_bind *) 0; + + while (bind) { + if (bind->key == key) { + if (prev) + prev->next = bind->next; + else + gc.Cmd.cmdbinds = bind->next; + if (bind->param) + free(bind->param); + free(bind); + return; + } + prev = bind; + bind = bind->next; + } +} + +void +cmdbind_freeall(void) +{ + struct Cmd_bind *next; + + while (gc.Cmd.cmdbinds) { + next = gc.Cmd.cmdbinds->next; + if (gc.Cmd.cmdbinds->param) + free(gc.Cmd.cmdbinds->param); + free(gc.Cmd.cmdbinds); + gc.Cmd.cmdbinds = next; + } +} + +/* swap key bindings for key1 and key2. both bindings must exist. */ +staticfn void +cmdbind_swapkeys(uchar key1, uchar key2) +{ + struct Cmd_bind *bind1 = cmdbind_get(key1); + struct Cmd_bind *bind2 = cmdbind_get(key2); + + if (bind1 && bind2) { + bind1->key = key2; + bind2->key = key1; + } +} + /* return number of extended commands bound to a non-default key */ int count_bind_keys(void) { - int nbinds = 0; - int i; + struct Cmd_bind *bind = gc.Cmd.cmdbinds; + int i, nbinds = 0; + uchar keys[256]; - for (i = 0; i < extcmdlist_length; i++) - if (extcmdlist[i].key - && gc.Cmd.commands[extcmdlist[i].key] != &extcmdlist[i]) + (void) memset(keys, 0, sizeof(uchar) * 256); + + /* commands bound to different key */ + while (bind) { + keys[bind->key] = 1; + if (bind->userbind && bind->cmd && bind->cmd->key != bind->key) { nbinds++; + } + bind = bind->next; + } + + /* commands which should be bound to a key, but aren't */ + for (i = 0; i < extcmdlist_length; i++) + if (extcmdlist[i].key && !keys[extcmdlist[i].key]) + nbinds++; + return nbinds; } @@ -2099,16 +2236,41 @@ get_changed_key_binds(strbuf_t *sbuf) int i; char buf[BUFSZ]; char buf2[QBUFSZ]; + struct Cmd_bind *bind = gc.Cmd.cmdbinds; + uchar keys[256]; + + (void) memset(keys, 0, sizeof(uchar) * 256); if (!sbuf) win = create_nhwindow(NHW_TEXT); + + /* commands bound to different key */ + while (bind) { + keys[bind->key] = 1; + if (bind->userbind && bind->cmd && bind->cmd->key != bind->key) { + if ((bind->cmd->flags & CMD_PARAM) != 0) + Sprintf(buf, "BIND=%s:%s(%s)%s", key2txt(bind->key, buf2), + bind->cmd->ef_txt, + bind->param, + sbuf ? "\n" : ""); + else + Sprintf(buf, "BIND=%s:%s%s", key2txt(bind->key, buf2), + bind->cmd->ef_txt, + sbuf ? "\n" : ""); + if (sbuf) + strbuf_append(sbuf, buf); + else + putstr(win, 0, buf); + } + bind = bind->next; + } + + /* commands which should be bound to a key, but aren't */ for (i = 0; i < extcmdlist_length; i++) { struct ext_func_tab *ec = &extcmdlist[i]; - if (ec->key && gc.Cmd.commands[ec->key] - && gc.Cmd.commands[ec->key] != ec) { - Sprintf(buf, "BIND=%s:%s%s", key2txt(ec->key, buf2), - gc.Cmd.commands[ec->key]->ef_txt, + if (ec->key && !keys[ec->key]) { + Sprintf(buf, "BIND=%s:nothing%s", key2txt(ec->key, buf2), sbuf ? "\n" : ""); if (sbuf) strbuf_append(sbuf, buf); @@ -2149,9 +2311,11 @@ handler_rebind_keys_add(boolean keyfirst) any = cg.zeroany; if (key) { - if (gc.Cmd.commands[key]) { + struct Cmd_bind *bind = cmdbind_get(key); + + if (bind && bind->cmd) { Sprintf(buf, "Key '%s' is currently bound to \"%s\".", - key2txt(key, buf2), gc.Cmd.commands[key]->ef_txt); + key2txt(key, buf2), bind->cmd->ef_txt); } else { Sprintf(buf, "Key '%s' is not bound to anything.", key2txt(key, buf2)); @@ -2186,19 +2350,32 @@ handler_rebind_keys_add(boolean keyfirst) npick = select_menu(win, PICK_ONE, &picks); destroy_nhwindow(win); if (npick > 0) { - const struct ext_func_tab *prevec; - const char *cmdstr; + struct Cmd_bind *prevcmd; + char cmdstr[BUFSZ]; i = picks->item.a_int; free((genericptr_t) picks); if (i == -1) { ec = NULL; - cmdstr = "nothing"; + Strcat(cmdstr, "nothing"); goto bindit; } else { ec = &extcmdlist[i-1]; - cmdstr = ec->ef_txt; + + if ((ec->flags & CMD_PARAM) != 0) { + char parambuf[BUFSZ]; + char querybuf[BUFSZ]; + + parambuf[0] = '\0'; + Sprintf(querybuf, "Command %s requires a parameter:", ec->ef_txt); + getlin(querybuf, parambuf); + (void) mungspaces(parambuf); + Snprintf(cmdstr, BUFSZ-1, "%s(%s)", ec->ef_txt, parambuf); + cmdstr[BUFSZ-1] = '\0'; + } else { + Strcat(cmdstr, ec->ef_txt); + } } bindit: if (!key) { @@ -2209,13 +2386,13 @@ handler_rebind_keys_add(boolean keyfirst) return; } - prevec = gc.Cmd.commands[key]; + prevcmd = cmdbind_get(key); - if (bind_key(key, cmdstr)) { - if (prevec && prevec != ec) { + if (bind_key(key, cmdstr, TRUE)) { + if (prevcmd && prevcmd->cmd != ec) { pline("Changed key '%s' from \"%s\" to \"%s\".", - key2txt(key, buf2), prevec->ef_txt, cmdstr); - } else if (!prevec) { + key2txt(key, buf2), prevcmd->cmd->ef_txt, cmdstr); + } else if (!prevcmd) { pline("Bound key '%s' to \"%s\".", key2txt(key, buf2), cmdstr); } @@ -2385,6 +2562,7 @@ key2extcmddesc(uchar key) const char *txt; int k, i, j; uchar M_5 = (uchar) M('5'), M_0 = (uchar) M('0'); + struct Cmd_bind *cmdbind; /* need to check for movement commands before checking the extended commands table because it contains entries for number_pad commands @@ -2417,8 +2595,10 @@ key2extcmddesc(uchar key) return misc_keys[i].desc; } /* finally, check whether 'key' is a command */ - if (gc.Cmd.commands[key] && (txt = gc.Cmd.commands[key]->ef_txt) != 0) { - Sprintf(key2cmdbuf, "%s (#%s)", gc.Cmd.commands[key]->ef_desc, txt); + if ((cmdbind = cmdbind_get(key)) != 0 + && cmdbind->cmd + && (txt = cmdbind->cmd->ef_txt) != 0) { + Sprintf(key2cmdbuf, "%s (#%s)", cmdbind->cmd->ef_desc, txt); /* special case: for reqmenu prefix (normally 'm'), replace "prefix: request menu or modify command (#reqmenu)" @@ -2477,22 +2657,58 @@ bind_mousebtn(int btn, const char *command) } boolean -bind_key(uchar key, const char *command) +bind_key(uchar key, const char *command, boolean user) { struct ext_func_tab *extcmd; + long len; + char *buf, *p = NULL, *lastp = NULL; /* special case: "nothing" is reserved for unbinding */ if (!strcmpi(command, "nothing")) { - gc.Cmd.commands[key] = (struct ext_func_tab *) 0; + cmdbind_remove(key); return TRUE; } + /* copy command to buf for modification */ + len = strlen(command) + 1; + buf = (char *)alloc(len); + (void) strncpy(buf, command, len); + + /* does buf have a parameter in parenthesis? */ + if ((p = strchr(buf, '(')) != 0 + && (lastp = strrchr(buf, ')')) != 0 + && (lastp > p)) { + *p = '\0'; + *lastp = '\0'; + /* p points to the parameter */ + p++; + } + for (extcmd = extcmdlist; extcmd->ef_txt; extcmd++) { - if (strcmpi(command, extcmd->ef_txt)) + if (strcmpi(buf, extcmd->ef_txt)) continue; if ((extcmd->flags & INTERNALCMD) != 0) continue; - gc.Cmd.commands[key] = extcmd; + cmdbind_add(key, extcmd, user); + + if ((extcmd->flags & CMD_PARAM) != 0) { + if (!p) { + config_error_add("'%s' requires a parameter", buf); + } else { + struct Cmd_bind *bind = cmdbind_get(key); + int maxlen = min(30, strlen(p)) + 1; + + if (maxlen <= 1) { + config_error_add("Required parameter cannot be empty"); + } else { + bind->param = (char *) alloc(maxlen); + (void) strncpy(bind->param, p, maxlen); + bind->param[maxlen-1] = '\0'; + } + } + } else if (p && strlen(p) > 0) + config_error_add("'%s' does not take a parameter", buf); + #if 0 /* silently accept key binding for unavailable command (!SHELL,&c) */ if ((extcmd->flags & CMD_NOT_AVAILABLE) != 0) { char buf[BUFSZ]; @@ -2501,9 +2717,11 @@ bind_key(uchar key, const char *command) config_error_add("%s", buf); } #endif + free(buf); return TRUE; } + free(buf); return FALSE; } @@ -2518,7 +2736,7 @@ bind_key_fn(uchar key, int (*fn)(void)) continue; if ((extcmd->flags & INTERNALCMD) != 0) continue; - gc.Cmd.commands[key] = extcmd; + cmdbind_add(key, extcmd, FALSE); return TRUE; } @@ -2533,31 +2751,31 @@ commands_init(void) for (extcmd = extcmdlist; extcmd->ef_txt; extcmd++) if (extcmd->key) - gc.Cmd.commands[extcmd->key] = extcmd; + cmdbind_add(extcmd->key, extcmd, FALSE); (void) bind_mousebtn(1, "therecmdmenu"); (void) bind_mousebtn(2, "clicklook"); /* number_pad */ - (void) bind_key(C('l'), "redraw"); - (void) bind_key('h', "help"); - (void) bind_key('j', "jump"); - (void) bind_key('k', "kick"); - (void) bind_key('l', "loot"); - (void) bind_key(C('n'), "annotate"); - (void) bind_key('N', "name"); - (void) bind_key('u', "untrap"); - (void) bind_key('5', "run"); - (void) bind_key(M('5'), "rush"); - (void) bind_key('-', "fight"); + (void) bind_key(C('l'), "redraw", FALSE); + (void) bind_key('h', "help", FALSE); + (void) bind_key('j', "jump", FALSE); + (void) bind_key('k', "kick", FALSE); + (void) bind_key('l', "loot", FALSE); + (void) bind_key(C('n'), "annotate", FALSE); + (void) bind_key('N', "name", FALSE); + (void) bind_key('u', "untrap", FALSE); + (void) bind_key('5', "run", FALSE); + (void) bind_key(M('5'), "rush", FALSE); + (void) bind_key('-', "fight", FALSE); /* alt keys: */ - (void) bind_key(M('O'), "overview"); - (void) bind_key(M('2'), "twoweapon"); - (void) bind_key(M('N'), "name"); + (void) bind_key(M('O'), "overview", FALSE); + (void) bind_key(M('2'), "twoweapon", FALSE); + (void) bind_key(M('N'), "name", FALSE); #if 0 /* don't do this until the rest_on_space option is set or cleared */ - (void) bind_key(' ', "wait"); + (void) bind_key(' ', "wait", FALSE); #endif } @@ -2566,12 +2784,13 @@ keylist_func_has_key(const struct ext_func_tab *extcmd, boolean *skip_keys_used) /* boolean keys_used[256] */ { int i; + struct Cmd_bind *bind; for (i = 0; i < 256; ++i) { if (skip_keys_used[i]) continue; - if (gc.Cmd.commands[i] == extcmd) + if (((bind = cmdbind_get(i)) != 0) && (bind->cmd == extcmd)) return TRUE; } return FALSE; @@ -2587,6 +2806,7 @@ keylist_putcmds(winid datawin, boolean docount, char buf[BUFSZ], buf2[QBUFSZ]; boolean keys_already_used[256]; /* copy of keys_used[] before updates */ int count = 0; + struct Cmd_bind *bind; for (i = 0; i < 256; i++) { uchar key = (uchar) i; @@ -2596,16 +2816,22 @@ keylist_putcmds(winid datawin, boolean docount, continue; if (key == ' ' && !flags.rest_on_space) continue; - if ((extcmd = gc.Cmd.commands[i]) != (struct ext_func_tab *) 0) { - if ((incl_flags && !(extcmd->flags & incl_flags)) - || (excl_flags && (extcmd->flags & excl_flags))) + bind = cmdbind_get(key); + if (bind && bind->cmd != (struct ext_func_tab *) 0) { + if ((incl_flags && !(bind->cmd->flags & incl_flags)) + || (excl_flags && (bind->cmd->flags & excl_flags))) continue; if (docount) { count++; continue; } - Sprintf(buf, "%-7s %-13s %s", key2txt(key, buf2), - extcmd->ef_txt, extcmd->ef_desc); + if ((bind->cmd->flags & CMD_PARAM) != 0) + Sprintf(buf, "%-7s %-13s %s \"%s\"", key2txt(key, buf2), + bind->cmd->ef_txt, bind->cmd->ef_desc, + bind->param); + else + Sprintf(buf, "%-7s %-13s %s", key2txt(key, buf2), + bind->cmd->ef_txt, bind->cmd->ef_desc); putstr(datawin, 0, buf); keys_used[i] = TRUE; } @@ -2809,9 +3035,10 @@ cmd_from_func(int (*fn)(void)) { int i; char ret = '\0'; + struct Cmd_bind *bind; - /* skip NUL; allowing it would wreak havoc */ - for (i = 1; i < 256; ++i) { + for (bind = gc.Cmd.cmdbinds; bind; bind = bind->next) { + i = bind->key; /* skip space; we'll use it below as last resort if no other keystroke invokes space's command */ if (i == ' ') @@ -2822,7 +3049,7 @@ cmd_from_func(int (*fn)(void)) && !gc.Cmd.num_pad) continue; - if (gc.Cmd.commands[i] && gc.Cmd.commands[i]->ef_funct == fn) { + if (bind->cmd && bind->cmd->ef_funct == fn) { if (i >= ' ' && i <= '~') return (char) i; else { @@ -2830,7 +3057,8 @@ cmd_from_func(int (*fn)(void)) } } } - if (gc.Cmd.commands[' '] && gc.Cmd.commands[' ']->ef_funct == fn) + if ((bind = cmdbind_get(' ')) != 0 && bind->cmd + && bind->cmd->ef_funct == fn) return ' '; return ret; } @@ -3123,7 +3351,6 @@ reset_commands(boolean initial) static struct ext_func_tab *back_dir_cmd[N_DIRS][N_MOVEMODES]; static uchar back_dir_key[N_DIRS][N_MOVEMODES]; static boolean backed_dir_cmd = FALSE; - const struct ext_func_tab *cmdtmp; boolean flagtemp; int c, i, updated = 0; int dir, mode; @@ -3139,8 +3366,7 @@ reset_commands(boolean initial) if (backed_dir_cmd) { for (dir = 0; dir < N_DIRS; dir++) { for (mode = 0; mode < N_MOVEMODES; mode++) { - gc.Cmd.commands[back_dir_key[dir][mode]] - = back_dir_cmd[dir][mode]; + cmdbind_add(back_dir_key[dir][mode], back_dir_cmd[dir][mode], FALSE); } } } @@ -3162,9 +3388,7 @@ reset_commands(boolean initial) perform the swap (or reverse previous one) */ for (i = 0; i < SIZE(ylist); i++) { c = ylist[i] & 0xff; - cmdtmp = gc.Cmd.commands[c]; /* tmp = [y] */ - gc.Cmd.commands[c] = gc.Cmd.commands[c + 1]; /* [y] = [z] */ - gc.Cmd.commands[c + 1] = cmdtmp; /* [z] = tmp */ + cmdbind_swapkeys(c, c + 1); } } /* MSDOS compatibility mode (only applicable for num_pad) */ @@ -3181,8 +3405,10 @@ reset_commands(boolean initial) #endif /* FIXME: NHKF_DOINV2 ought to be implemented instead of this */ c = M('0') & 0xff; - gc.Cmd.commands[c] = gc.Cmd.pcHack_compat ? gc.Cmd.commands['I'] - : 0; + if (gc.Cmd.pcHack_compat) + cmdbind_add(c, ext_func_tab_from_func(dotypeinv), FALSE); + else + cmdbind_remove(c); } /* phone keypad layout (only applicable for num_pad) */ flagtemp = (iflags.num_pad_mode & 2) ? gc.Cmd.num_pad : FALSE; @@ -3192,13 +3418,9 @@ reset_commands(boolean initial) /* phone_layout has been toggled */ for (i = 0; i < 3; i++) { c = '1' + i; /* 1,2,3 <-> 7,8,9 */ - cmdtmp = gc.Cmd.commands[c]; /* tmp = [1] */ - gc.Cmd.commands[c] = gc.Cmd.commands[c + 6]; /* [1] = [7] */ - gc.Cmd.commands[c + 6] = cmdtmp; /* [7] = tmp */ + cmdbind_swapkeys(c, c + 6); c = (M('1') & 0xff) + i; /* M-1,M-2,M-3 <-> M-7,M-8,M-9 */ - cmdtmp = gc.Cmd.commands[c]; /* tmp = [M-1] */ - gc.Cmd.commands[c] = gc.Cmd.commands[c + 6]; /* [M-1]=[M-7] */ - gc.Cmd.commands[c + 6] = cmdtmp; /* [M-7] = tmp */ + cmdbind_swapkeys(c, c + 6); } } } /*?initial*/ @@ -3215,6 +3437,7 @@ reset_commands(boolean initial) for (dir = 0; dir < N_DIRS; dir++) { for (mode = MV_WALK; mode < N_MOVEMODES; mode++) { uchar di = (uchar) gc.Cmd.dirchars[dir]; + struct Cmd_bind *bind; if (!gc.Cmd.num_pad) { if (mode == MV_RUN) di = highc(di); @@ -3224,9 +3447,11 @@ reset_commands(boolean initial) else if (mode == MV_RUSH) di = M(di); } back_dir_key[dir][mode] = di; - back_dir_cmd[dir][mode] - = (struct ext_func_tab *) gc.Cmd.commands[di]; - gc.Cmd.commands[di] = (struct ext_func_tab *) 0; + if ((bind = cmdbind_get(di)) != 0) + back_dir_cmd[dir][mode] = (struct ext_func_tab *) bind->cmd; + else + back_dir_cmd[dir][mode] = (struct ext_func_tab *) 0; + cmdbind_remove(di); } } backed_dir_cmd = TRUE; @@ -3264,15 +3489,15 @@ update_rest_on_space(void) donull, (IFBURIED | CMD_M_PREFIX), "waiting" }; static const struct ext_func_tab *unrestonspace = 0; - const struct ext_func_tab *bound_f = gc.Cmd.commands[' ']; + struct Cmd_bind *bind = cmdbind_get(' '); /* when 'rest_on_space' is On, will run the #wait command; when it is Off, will use 'unrestonspace' which will either be Null and elicit "Unknown command ' '." or have some non-Null command bound in player's RC file */ - if (bound_f != 0 && bound_f != &restonspace) - unrestonspace = bound_f; - gc.Cmd.commands[' '] = flags.rest_on_space ? &restonspace : unrestonspace; + if (bind && bind->cmd != &restonspace) + unrestonspace = bind->cmd; + cmdbind_add(' ', flags.rest_on_space ? &restonspace : unrestonspace, FALSE); } /* commands which accept 'm' prefix to request menu operation or other @@ -3449,11 +3674,13 @@ rhack(int key) const struct ext_func_tab *tlist; int res; + gc.cmd_bind = cmdbind_get(key & 0xFF); + do_cmdq_extcmd: if (cmdq_ec) tlist = cmdq_ec; else - tlist = gc.Cmd.commands[key & 0xff]; + tlist = gc.cmd_bind ? gc.cmd_bind->cmd : NULL; /* current - use key to directly index cmdlist array */ if (tlist != 0) { @@ -3477,7 +3704,8 @@ rhack(int key) * the former call to help_dir() (for 'bad_command' below). */ if (was_m_prefix) { - pline("The %s command does not accept '%s' prefix.", + custompline(SUPPRESS_HISTORY, + "The %s command does not accept '%s' prefix.", tlist->ef_txt, which); } else { uchar ch = tlist->key; @@ -3614,7 +3842,7 @@ rhack(int key) /* convert an x,y pair into a direction code */ int -xytod(coordxy x, coordxy y) +xytodir(int x, int y) { int dd; @@ -3626,7 +3854,7 @@ xytod(coordxy x, coordxy y) /* convert a direction code into an x,y pair */ void -dtoxy(coord *cc, int dd) +dirtocoord(coord *cc, int dd) { if (dd > DIR_ERR && dd < N_DIRS_Z) { cc->x = xdir[dd]; @@ -3639,9 +3867,10 @@ int movecmd(char sym, int mode) { int d = DIR_ERR; + struct Cmd_bind *bind = cmdbind_get(sym); - if (gc.Cmd.commands[(uchar) sym]) { - int (*fnc)(void) = gc.Cmd.commands[(uchar) sym]->ef_funct; + if (bind && bind->cmd) { + int (*fnc)(void) = bind->cmd->ef_funct; if (mode == MV_ANY) { for (d = N_DIRS_Z - 1; d > DIR_ERR; d--) @@ -3680,9 +3909,10 @@ boolean redraw_cmd(char c) { uchar uc = (uchar) c; - const struct ext_func_tab *cmd = gc.Cmd.commands[uc]; + struct Cmd_bind *bind = cmdbind_get(uc); - return (boolean) (cmd && cmd->ef_funct == doredraw); + return (boolean) (bind && bind->cmd + && bind->cmd->ef_funct == doredraw); } /* @@ -3732,7 +3962,7 @@ getdir(const char *s) if (cmdq) { if (cmdq->typ == CMDQ_DIR) { if (!cmdq->dirz) { - dirsym = gc.Cmd.dirchars[xytod(cmdq->dirx, cmdq->diry)]; + dirsym = gc.Cmd.dirchars[xytodir(cmdq->dirx, cmdq->diry)]; } else { dirsym = gc.Cmd.dirchars[(cmdq->dirz > 0) ? DIR_DOWN : DIR_UP]; @@ -4500,7 +4730,7 @@ act_on_act( cmdq_add_dir(CQ_CANNED, dx, dy, 0); break; case MCMD_MOVE_DIR: - dir = xytod(dx, dy); + dir = xytodir(dx, dy); cmdq_add_ec(CQ_CANNED, move_funcs[dir][MV_WALK]); break; case MCMD_RIDE: @@ -4522,7 +4752,7 @@ act_on_act( } break; case MCMD_ATTACK_NEXT2U: - dir = xytod(dx, dy); + dir = xytodir(dx, dy); cmdq_add_ec(CQ_CANNED, move_funcs[dir][MV_WALK]); break; case MCMD_TALK: @@ -4632,7 +4862,7 @@ there_cmd_menu(coordxy x, coordxy y, int mod) if (!K) { /* no menu options, try to move */ if (next2u(x, y) && test_move(u.ux, u.uy, dx, dy, TEST_MOVE)) { - int dir = xytod(dx, dy); + int dir = xytodir(dx, dy); cmdq_add_ec(CQ_CANNED, move_funcs[dir][MV_WALK]); } else if (flags.travelcmd) { @@ -4726,7 +4956,7 @@ domouseaction(void) /* directional commands */ - dir = xytod(x, y); + dir = xytodir(x, y); if (!m_at(u.ux + x, u.uy + y) && !test_move(u.ux, u.uy, x, y, TEST_MOVE)) { if (IS_DOOR(levl[u.ux + x][u.uy + y].typ)) { @@ -4765,7 +4995,7 @@ domouseaction(void) cmdq_add_ec(CQ_CANNED, donull); return ECMD_OK; } - dir = xytod(x, y); + dir = xytodir(x, y); } /* move, attack, etc. */ @@ -4864,6 +5094,7 @@ staticfn int parse(void) { int foo; + struct Cmd_bind *bind; iflags.in_parse = TRUE; gc.command_count = 0; @@ -4893,13 +5124,14 @@ parse(void) gl.last_command_count = 0; } else if (gi.in_doagain) { gc.command_count = gl.last_command_count; - } else if (foo && gc.Cmd.commands[foo & 0xff] + } else if (foo && (bind = cmdbind_get(foo & 0xFF)) != 0 /* these shouldn't go into the do-again buffer */ - && (gc.Cmd.commands[foo & 0xff]->ef_funct == do_repeat - || gc.Cmd.commands[foo & 0xff]->ef_funct == doprev_message + && bind && bind->cmd + && (bind->cmd->ef_funct == do_repeat + || bind->cmd->ef_funct == doprev_message /* this one might get put into the do-again buffer but only if the interface code tells the core to do it */ - || gc.Cmd.commands[foo & 0xff]->ef_funct == doextcmd)) { + || bind->cmd->ef_funct == doextcmd)) { /* gc.command_count will be set again when we re-enter with gi.in_doagain set true */ gc.command_count = gl.last_command_count; @@ -5259,7 +5491,7 @@ yn_function( query = qbuf; } - if ((cmdq = cmdq_pop()) != 0) { + if (addcmdq && (cmdq = cmdq_pop()) != 0) { cq = *cmdq; free(cmdq); } else { @@ -5322,7 +5554,7 @@ yn_function( it is most likely caused by saving a keystroke that was just used to answer a context-sensitive prompt, then using the do-again command with context that has changed */ - if (resp && res && !strchr(resp, res)) { + if (resp && *resp && res && !strchr(resp, res)) { /* this probably needs refinement since caller is expecting something within 'resp' and ESC won't be (it could be present, but as a flag for unshown possibilities rather than as acceptable input) */ @@ -5404,9 +5636,11 @@ paranoid_ynq( /* for empty input, return value c will already be 'n' */ } while (ParanoidConfirm && strcmpi(ans, "no") && --trylimit); } else if (accept_q) { - c = ynq(prompt); /* 'y', 'n', or 'q' */ + /* 'y', 'n', or 'q' */ + c = yn_function(prompt, ynqchars, 'n', FALSE); } else { - c = y_n(prompt); /* 'y' or 'n' */ + /* 'y' or 'n' */ + c = yn_function(prompt, ynchars, 'n', FALSE); } if (c != 'y' && (c != 'q' || !accept_q)) c = 'n'; diff --git a/src/dbridge.c b/src/dbridge.c index 5e739d4fa..72d39be82 100644 --- a/src/dbridge.c +++ b/src/dbridge.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 dbridge.c $NHDT-Date: 1702349063 2023/12/12 02:44:23 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.62 $ */ +/* NetHack 3.7 dbridge.c $NHDT-Date: 1772771734 2026/03/05 20:35:34 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.70 $ */ /* Copyright (c) 1989 by Jean-Christophe Collet */ /* NetHack may be freely redistributed. See license for details. */ @@ -399,7 +399,9 @@ e_survives_at(struct entity *etmp, coordxy x, coordxy y) } staticfn void -e_died(struct entity *etmp, int xkill_flags, int how) +e_died( + struct entity *etmp, + int xkill_flags, int how) { if (is_u(etmp)) { if (how == DROWNING) { @@ -444,6 +446,26 @@ e_died(struct entity *etmp, int xkill_flags, int how) mk_message(xkill_flags), mk_corpse(xkill_flags)); else /* you caused it */ xkilled(etmp->emon, xkill_flags); + + /* if etmp gets life-saved, kill it again; otherwise we might end up + trying to place another monster (probably a xorn) on same spot */ + if (!DEADMONSTER(etmp->emon)) { + int seeit = canspotmon(etmp->emon); + + xkill_flags |= XKILL_NOMSG | XKILL_NOCONDUCT; + if (svc.context.mon_moving) + monkilled(etmp->emon, "", mk_corpse(xkill_flags)); + else /* you caused it */ + xkilled(etmp->emon, xkill_flags); + + if (DEADMONSTER(etmp->emon)) { + if (seeit) + pline("Unfortunately for %s, %s is still crushed.", + mon_nam(etmp->emon), mhe(etmp->emon)); + } else { + ; /* FIXME: still not dead? What should we do now? */ + } + } etmp->edata = (struct permonst *) 0; /* dead long worm handling */ diff --git a/src/decl.c b/src/decl.c index a7e52dd80..f229de737 100644 --- a/src/decl.c +++ b/src/decl.c @@ -247,6 +247,7 @@ static const struct instance_globals_c g_init_c = { /* decl.c */ UNDEFINED_VALUES, /* chosen_windowtype */ 0, /* cmd_key */ + NULL, /* cmd_bind */ 0L, /* command_count */ UNDEFINED_PTR, /* current_wand */ #ifdef DEF_PAGER @@ -413,6 +414,7 @@ static const struct instance_globals_i g_init_i = { /* invent.c */ NULL, /* invbuf */ 0U, /* invbufsize */ + FALSE, /* item_action_in_progress */ 0, /* in_sync_perminvent */ /* mon.c */ NULL, /* itermonarr */ @@ -590,7 +592,7 @@ static const struct instance_globals_o g_init_o = { /* o_init.c */ DUMMY, /* oclass_prob_totals */ /* options.c */ - 0, /* opt_phase */ + phase_not_set, /* opt_phase */ FALSE, /* opt_initial */ FALSE, /* opt_from_file */ FALSE, /* opt_need_redraw */ @@ -673,6 +675,8 @@ static const struct instance_globals_r g_init_r = { }; static const struct instance_globals_s g_init_s = { + /* allmain.c */ + FALSE, /* saving_grace_turn */ /* artifact.c */ 0, /* spec_dbon_applies */ /* decl.c */ @@ -763,6 +767,8 @@ static const struct instance_globals_t g_init_t = { }; static const struct instance_globals_u g_init_u = { + /* allmain.c */ + 0, /* uhp_at_start_of_monster_turn */ /* botl.c */ FALSE, /* update_all */ /* decl.c */ diff --git a/src/detect.c b/src/detect.c index 61e5dd3d6..bf536e127 100644 --- a/src/detect.c +++ b/src/detect.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 detect.c $NHDT-Date: 1745114235 2025/04/19 17:57:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.190 $ */ +/* NetHack 3.7 detect.c $NHDT-Date: 1763708572 2025/11/20 23:02:52 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.191 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2018. */ /* NetHack may be freely redistributed. See license for details. */ @@ -26,7 +26,7 @@ staticfn void reconstrain_map(void); staticfn void map_redisplay(void); staticfn void browse_map(unsigned, const char *); staticfn void map_monst(struct monst *, boolean); -staticfn void do_dknown_of(struct obj *); +staticfn void observe_recursively(struct obj *); staticfn boolean check_map_spot(coordxy, coordxy, char, unsigned); staticfn boolean clear_stale_map(char, unsigned); staticfn void sense_trap(struct trap *, coordxy, coordxy, int); @@ -246,14 +246,14 @@ o_material(struct obj *obj, unsigned material) } staticfn void -do_dknown_of(struct obj *obj) +observe_recursively(struct obj *obj) { struct obj *otmp; - obj->dknown = 1; + observe_object(obj); if (Has_contents(obj)) { for (otmp = obj->cobj; otmp; otmp = otmp->nobj) - do_dknown_of(otmp); + observe_recursively(otmp); } } @@ -638,7 +638,7 @@ object_detect(struct obj *detector, /* object doing the detecting */ if (do_dknown) for (obj = gi.invent; obj; obj = obj->nobj) - do_dknown_of(obj); + observe_recursively(obj); for (obj = fobj; obj; obj = obj->nobj) { if ((!class && !boulder) || o_in(obj, class) || o_in(obj, boulder)) { @@ -648,7 +648,7 @@ object_detect(struct obj *detector, /* object doing the detecting */ ct++; } if (do_dknown) - do_dknown_of(obj); + observe_recursively(obj); } for (obj = svl.level.buriedobjlist; obj; obj = obj->nobj) { @@ -659,7 +659,7 @@ object_detect(struct obj *detector, /* object doing the detecting */ ct++; } if (do_dknown) - do_dknown_of(obj); + observe_recursively(obj); } if (u.usteed) @@ -673,7 +673,7 @@ object_detect(struct obj *detector, /* object doing the detecting */ || o_in(obj, boulder)) ct++; if (do_dknown) - do_dknown_of(obj); + observe_recursively(obj); } if ((is_cursed && M_AP_TYPE(mtmp) == M_AP_OBJECT && (!class || class == objects[mtmp->mappearance].oc_class)) @@ -932,7 +932,7 @@ detect_obj_traps( } if (Is_box(otmp) && otmp->otrapped) { otmp->tknown = 1; - otmp->dknown = 1; + observe_object(otmp); result |= u_at(x, y) ? OTRAP_HERE : OTRAP_THERE; if (ft) { flash_glyph_at(x, y, trapglyph, FOUND_FLASH_COUNT); @@ -1508,7 +1508,7 @@ do_vicinity_map( unlike object detection, we don't notice buried items */ otmp = svl.level.objects[zx][zy]; if (extended) - otmp->dknown = 1; + observe_object(otmp); map_object(otmp, TRUE); newglyph = glyph_at(zx, zy); /* if otmp is underwater, we'll need to redisplay the water */ @@ -2179,6 +2179,12 @@ reveal_terrain_getglyph( keep_mons = (which_subset & TER_MON) != 0, full = (which_subset & TER_FULL) != 0; + /* + * FIXME: + * travel treats discovered vibrating square as if it were terrain + * rather than a trap so this should do so too. + */ + /* for 'full', show the actual terrain for the entire level, otherwise what the hero remembers for seen locations with monsters, objects, and/or traps removed as caller dictates */ diff --git a/src/dig.c b/src/dig.c index 579a97013..5e5fc1a74 100644 --- a/src/dig.c +++ b/src/dig.c @@ -1256,7 +1256,7 @@ use_pick_axe2(struct obj *obj) && (trap_with_u = t_at(u.ux, u.uy)) && is_pit(trap->ttyp) && !conjoined_pits(trap, trap_with_u, FALSE)) { - int idx = xytod(u.dx, u.dy); + int idx = xytodir(u.dx, u.dy); if (idx != DIR_ERR) { int adjidx = DIR_180(idx); @@ -1617,7 +1617,7 @@ zap_dig(void) if (u.utrap && u.utraptype == TT_PIT && (trap_with_u = t_at(u.ux, u.uy))) { pitdig = TRUE; - diridx = xytod(u.dx, u.dy); + diridx = xytodir(u.dx, u.dy); } digdepth = rn1(18, 8); tmp_at(DISP_BEAM, cmap_to_glyph(S_digbeam)); diff --git a/src/display.c b/src/display.c index ba1841cca..5f854c64d 100644 --- a/src/display.c +++ b/src/display.c @@ -346,7 +346,7 @@ map_object(struct obj *obj, int show) neardist = (r * r) * 2 - r; /* same as r*r + r*(r-1) */ if (distu(x, y) <= neardist) { - obj->dknown = 1; + observe_object(obj); glyph = obj_to_glyph(obj, newsym_rn2); } } @@ -1585,7 +1585,7 @@ see_nearby_objects(void) if (!cansee(ix, iy) || distu(ix, iy) > neardist) continue; - obj->dknown = 1; /* near enough to see it */ + observe_object(obj); /* operate on remembered glyph rather than current one */ glyph = levl[ix][iy].glyph; if (glyph_is_generic_object(glyph)) @@ -2656,7 +2656,7 @@ const int altarcolors[] = { altar_color_unaligned, altar_color_chaotic, altar_color_neutral, altar_color_lawful, altar_color_other }; -const int explodecolors[7] = { +const int explodecolors[EXPL_MAX] = { explode_color_dark, explode_color_noxious, explode_color_muddy, explode_color_wet, explode_color_magical, explode_color_fiery, explode_color_frosty, diff --git a/src/do.c b/src/do.c index ae26e7158..cacab11c7 100644 --- a/src/do.c +++ b/src/do.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 do.c $NHDT-Date: 1737287889 2025/01/19 03:58:09 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.399 $ */ +/* NetHack 3.7 do.c $NHDT-Date: 1774269965 2026/03/23 04:46:05 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.404 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -839,7 +839,7 @@ dropz(struct obj *obj, boolean with_impact) map_object(obj, 0); newsym(u.ux, u.uy); /* remap location under self */ } - (void) encumber_msg(); + encumber_msg(); } /* when swallowed, move dropped object from OBJ_FREE to u.ustuck's inventory; @@ -1942,8 +1942,18 @@ goto_level( if (new) { char dloc[QBUFSZ]; /* Astral is excluded as a major event here because entry to it - is already one due to that being an achievement */ - boolean major = In_endgame(&u.uz) && !Is_astralevel(&u.uz); + is already one such due to that being an achievement; + for the quest, listing the start, locate, and goal levels would + seem reasonable but all quest levels are included for simplicity-- + level 2 (or 3 if hero level teleports after obtaining permission + to enter) is useful to show since it indicates that hero has + actually entered the quest rather than just received permission + to do so, and listing the goal level could be used to figure out + whether level 5 is the end or there's another level (ESP reveals + the same thing, but is part of normal game play as opposed to + #chronicle leaking information that hero hasn't discovered yet) */ + boolean major = ((In_endgame(&u.uz) && !Is_astralevel(&u.uz)) + || In_quest(&u.uz)); (void) describe_level(dloc, 2); livelog_printf(major ? LL_ACHIEVE : LL_DEBUG, "entered %s", dloc); @@ -2248,7 +2258,8 @@ revive_mon(anything *arg, long timeout UNUSED) /* corpse will revive somewhere else if there is a monster in the way; Riders get a chance to try to bump the obstacle out of their way */ if (is_displacer(mptr) && body->where == OBJ_FLOOR - && get_obj_location(body, &x, &y, 0) && (mtmp = m_at(x, y)) != 0) { + && get_obj_location(body, &x, &y, 0) && (mtmp = m_at(x, y)) != 0 && + svl.level.flags.stasis_until < svm.moves) { boolean notice_it = canseemon(mtmp); /* before rloc() */ char *monname = Monnam(mtmp); @@ -2431,7 +2442,7 @@ set_wounded_legs(long side, int timex) direct assignment instead of bitwise-OR so getting wounded in one leg mysteriously healed the other */ EWounded_legs |= side; - (void) encumber_msg(); + encumber_msg(); } void @@ -2470,7 +2481,7 @@ heal_legs( more when steed becomes healthy, then possible floor feedback, then able to carry less when back on foot]. */ if (how == 0) - (void) encumber_msg(); + encumber_msg(); } } diff --git a/src/do_name.c b/src/do_name.c index 9a2d2f54c..c27396343 100644 --- a/src/do_name.c +++ b/src/do_name.c @@ -669,8 +669,10 @@ docall(struct obj *obj) undiscover_object(obj->otyp); } else { *uname_p = dupstr(buf); - discover_object(obj->otyp, FALSE, TRUE); /* possibly add to disco[] */ + discover_object(obj->otyp, FALSE, TRUE, TRUE); /* possibly add to disco[] */ } + if (obj->where == OBJ_INVENT || carrying(obj->otyp)) + update_inventory(); } staticfn void diff --git a/src/do_wear.c b/src/do_wear.c index 058d903d7..382b17741 100644 --- a/src/do_wear.c +++ b/src/do_wear.c @@ -52,6 +52,7 @@ staticfn int takeoff_ok(struct obj *); /* maybe_destroy_armor() may return NULL */ staticfn struct obj *maybe_destroy_armor(struct obj *, struct obj *, boolean *) NONNULLARG3; +staticfn int obj_erode_type(struct obj *) NONNULLARG1; staticfn boolean better_not_take_that_off(struct obj *) NONNULLARG1; /* plural "fingers" or optionally "gloves" */ @@ -671,7 +672,7 @@ Gloves_off(void) } setworn((struct obj *) 0, W_ARMG); svc.context.takeoff.cancelled_don = FALSE; - (void) encumber_msg(); /* immediate feedback for GoP */ + encumber_msg(); /* immediate feedback for GoP */ /* usually can't remove gloves when they're slippery but it can be done by having them fall off (polymorph), stolen, or @@ -705,10 +706,12 @@ Shield_on(void) { /* no shield currently requires special handling when put on, but we keep this uncommented in case somebody adds a new one which does - [reflection is handled by setting u.uprops[REFLECTION].extrinsic + [the magical shields are handled by setting u.uprops[*].extrinsic in setworn() called by armor_or_accessory_on() before Shield_on()] */ switch (uarms->otyp) { case SMALL_SHIELD: + case SHIELD_OF_DRAIN_RESISTANCE: + case SHIELD_OF_SHOCK_RESISTANCE: case ELVEN_SHIELD: case URUK_HAI_SHIELD: case ORCISH_SHIELD: @@ -735,6 +738,8 @@ Shield_off(void) keep this uncommented in case somebody adds a new one which does */ switch (uarms->otyp) { case SMALL_SHIELD: + case SHIELD_OF_DRAIN_RESISTANCE: + case SHIELD_OF_SHOCK_RESISTANCE: case ELVEN_SHIELD: case URUK_HAI_SHIELD: case ORCISH_SHIELD: @@ -1196,12 +1201,12 @@ learnring(struct obj *ring, boolean observed) mark this ring as having been seen (no need for makeknown); otherwise if we have seen this ring, discover its type */ if (objects[ringtype].oc_name_known) - ring->dknown = 1; + observe_object(ring); else if (ring->dknown) makeknown(ringtype); #if 0 /* see learnwand() */ else - ring->eknown = 1; + observe_object(ring); #endif } @@ -1783,7 +1788,7 @@ armor_or_accessory_off(struct obj *obj) Strcat(what, " and "); Strcat(what, suit_simple_name(uarm)); } - Snprintf(why, sizeof(why), " without taking off your %s first", + Snprintf(why, sizeof why, " without taking off your %s first", what); } else { Strcpy(why, "; it's embedded"); @@ -1841,7 +1846,7 @@ dotakeoff(void) pline("Not wearing any armor or accessories."); return ECMD_OK; } - if (Narmorpieces != 1 || ParanoidRemove || cmdq_peek(CQ_CANNED)) + if (Narmorpieces != 1 || ParanoidRemove || gi.item_action_in_progress) otmp = getobj("take off", takeoff_ok, GETOBJ_NOFLAGS); if (!otmp) return ECMD_CANCEL; @@ -1849,6 +1854,21 @@ dotakeoff(void) return armor_or_accessory_off(otmp); } +/* 'i' or 'I[' followed by and then 'T'; + plain dotakeoff() would not give any feedback when picking suit + covered by cloak, or shirt covered by suit and/or cloak, due to the + default behavior of equip_ok() (skipping inaccessible items) */ +int +ia_dotakeoff(void) +{ + int res; + + gi.item_action_in_progress = TRUE; + res = dotakeoff(); + gi.item_action_in_progress = FALSE; + return res; +} + /* the #remove command - take off ring or other accessory */ int doremring(void) @@ -3176,9 +3196,9 @@ maybe_destroy_armor(struct obj *armor, struct obj *atmp, boolean *resisted) return (struct obj *) 0; } -/* hit by destroy armor scroll/black dragon breath/monster spell */ +/* hit by destroy armor scroll/black dragon breath */ int -destroy_arm(struct obj *atmp) +disintegrate_arm(struct obj *atmp) { struct obj *otmp = (struct obj *) 0; boolean losing_gloves = FALSE, resisted = FALSE, @@ -3235,6 +3255,66 @@ destroy_arm(struct obj *atmp) return 1; } +/* return ERODE_foo erosion type which can apply to object */ +staticfn int +obj_erode_type(struct obj *otmp) +{ + if (is_flammable(otmp)) + return ERODE_BURN; + else if (is_rustprone(otmp)) + return ERODE_RUST; + else if (is_crackable(otmp)) + return ERODE_CRACK; + else if (is_rottable(otmp)) + return ERODE_ROT; + else if (is_corrodeable(otmp)) + return ERODE_CORRODE; + return ERODE_NONE; +} + +/* erode a number of worn armor(s). + if the armor is hit when max eroded, destroys it. */ +int +destroy_arm(void) +{ + struct obj *armors[7] = { NULL }; + struct obj *otmp; + int i, idx = 0, hits = rn2(4) + 1; + int ret = 0; + + /* gather worn armor; include non-erodeable ones */ + if (uarm) armors[idx++] = uarm; + if (uarmc) armors[idx++] = uarmc; + if (uarmh) armors[idx++] = uarmh; + if (uarms) armors[idx++] = uarms; + if (uarmg) armors[idx++] = uarmg; + if (uarmf) armors[idx++] = uarmf; + if (uarmu) armors[idx++] = uarmu; + if (!idx) + return 0; + + for (i = 0; i < hits; i++) { + otmp = armors[rn2(idx)]; + + if (erosion_matters(otmp) && is_damageable(otmp) && !otmp->oerodeproof) { + int erosion = obj_erode_type(otmp); + + if (erosion != ERODE_NONE) { + int r = erode_obj(otmp, xname(otmp), erosion, EF_PAY|EF_DESTROY); + + if (r != ER_NOTHING) + ret = 1; + if (r == ER_DESTROYED) + break; + } + } + } + + if (ret) + stop_occupation(); + return ret; +} + void adj_abon(struct obj *otmp, schar delta) { @@ -3256,14 +3336,14 @@ adj_abon(struct obj *otmp, schar delta) } /* decide whether a worn item is covered up by some other worn item, - used for dipping into liquid and applying grease; + used for dipping into liquid and applying grease and takeoff_ok(); some criteria are different than select_off()'s */ boolean -inaccessible_equipment(struct obj *obj, - const char *verb, /* "dip" or "grease", or null to - avoid messages */ - boolean only_if_known_cursed) /* ignore covering unless - known to be cursed */ +inaccessible_equipment( + struct obj *obj, + const char *verb, /* "dip" or "grease", or null to avoid messages */ + boolean only_if_known_cursed) /* ignore covering unless it is known to + * be cursed */ { static NEARDATA const char need_to_take_off_outer_armor[] = "need to take off %s to %s %s."; @@ -3315,6 +3395,8 @@ inaccessible_equipment(struct obj *obj, } /* item is not inaccessible */ return FALSE; + +#undef BLOCKSACCESS } /* not a getobj callback - unifies code among the other 4 getobj callbacks */ @@ -3354,9 +3436,11 @@ equip_ok(struct obj *obj, boolean removing, boolean accessory) * can't be worn because the slot is filled with something else. */ /* removing inaccessible equipment */ - if (removing && inaccessible_equipment(obj, (const char *) 0, - (obj->oclass == RING_CLASS))) - return GETOBJ_EXCLUDE_INACCESS; + if (removing && !gi.item_action_in_progress) { + if (inaccessible_equipment(obj, (const char *) 0, + (obj->oclass == RING_CLASS))) + return GETOBJ_EXCLUDE_INACCESS; + } /* all good to go */ return GETOBJ_SUGGEST; diff --git a/src/dogmove.c b/src/dogmove.c index 50f40f135..1218c305e 100644 --- a/src/dogmove.c +++ b/src/dogmove.c @@ -313,12 +313,19 @@ dog_eat(struct monst *mtmp, /* It's a reward if it's DOGFOOD and the player dropped/threw it. We know the player had it if invlet is set. -dlc */ if (dogfood(mtmp, obj) == DOGFOOD && obj->invlet) { + int prior_apport = edog->apport; + edog->apport += (int) (200L / ((long) edog->dropdist + svm.moves - edog->droptime)); if (edog->apport <= 0) { - impossible("dog_eat: pet apport <= 0 (%d, %d, %ld, %ld)", + impossible("dog_eat: pet apport <= 0 (%d, %d, %ld, %ld, %d, %u, %u)", edog->apport, edog->dropdist, edog->droptime, - svm.moves); + svm.moves, + prior_apport, + /* check whether edog struct got clobbered; + these two values should always match if + edog content is still intact */ + mtmp->m_id, edog->parentmid); edog->apport = 1; } } @@ -983,8 +990,8 @@ dog_move( coordxy nx, ny; /* temporary coordinates */ xint16 cnt, uncursedcnt, chcnt; int chi = -1, nidist, ndist; - coord poss[9]; - long info[9], allowflags; + long allowflags; + struct mfndposdata mfp; #define GDIST(x, y) (dist2(x, y, gg.gx, gg.gy)) /* @@ -1020,7 +1027,6 @@ dog_move( nix = omx; /* set before newdogpos */ niy = omy; cursemsg[0] = FALSE; /* lint suppression */ - info[0] = 0; /* ditto */ if (edog) { j = dog_invent(mtmp, edog, udist); @@ -1054,7 +1060,7 @@ dog_move( } #endif allowflags = mon_allowflags(mtmp); - cnt = mfndpos(mtmp, poss, info, allowflags); + cnt = mfndpos(mtmp, &mfp, allowflags); /* Normally dogs don't step on cursed items, but if they have no * other choice they will. This requires checking ahead of time @@ -1062,16 +1068,16 @@ dog_move( */ uncursedcnt = 0; for (i = 0; i < cnt; i++) { - nx = poss[i].x; - ny = poss[i].y; - if (MON_AT(nx, ny) && !((info[i] & ALLOW_M) || info[i] & ALLOW_MDISP)) + nx = mfp.poss[i].x; + ny = mfp.poss[i].y; + if (MON_AT(nx, ny) && !((mfp.info[i] & ALLOW_M) || mfp.info[i] & ALLOW_MDISP)) continue; if (cursed_object_at(nx, ny)) continue; uncursedcnt++; } - better_with_displacing = should_displace(mtmp, poss, info, cnt, + better_with_displacing = should_displace(mtmp, &mfp, gg.gx, gg.gy); chcnt = 0; @@ -1079,8 +1085,8 @@ dog_move( nidist = GDIST(nix, niy); for (i = 0; i < cnt; i++) { - nx = poss[i].x; - ny = poss[i].y; + nx = mfp.poss[i].x; + ny = mfp.poss[i].y; cursemsg[i] = FALSE; /* if leashed, we drag him along. */ @@ -1093,7 +1099,7 @@ dog_move( ranged_only = FALSE; - if ((info[i] & ALLOW_M) && MON_AT(nx, ny)) { + if ((mfp.info[i] & ALLOW_M) && MON_AT(nx, ny)) { int mstatus; struct monst *mtmp2 = m_at(nx, ny); /* weight the audacity of the pet to attack a differently-leveled @@ -1162,7 +1168,7 @@ dog_move( } return MMOVE_DONE; } - if ((info[i] & ALLOW_MDISP) && MON_AT(nx, ny) + if ((mfp.info[i] & ALLOW_MDISP) && MON_AT(nx, ny) && better_with_displacing && !undesirable_disp(mtmp, nx, ny)) { int mstatus; struct monst *mtmp2 = m_at(nx, ny); @@ -1189,7 +1195,7 @@ dog_move( */ struct trap *trap; - if ((info[i] & ALLOW_TRAPS) && (trap = t_at(nx, ny))) { + if ((mfp.info[i] & ALLOW_TRAPS) && (trap = t_at(nx, ny))) { if (mtmp->mleashed) { if (!Deaf) whimper(mtmp); @@ -1271,7 +1277,7 @@ dog_move( if (nix != omx || niy != omy) { boolean wasseen; - if (info[chi] & ALLOW_U) { + if (mfp.info[chi] & ALLOW_U) { if (mtmp->mleashed) { /* play it safe */ pline_mon(mtmp, "%s breaks loose of %s leash!", Monnam(mtmp), mhis(mtmp)); @@ -1326,14 +1332,14 @@ dog_move( if (goodpos(cc.x, cc.y, mtmp, 0)) goto dognext; - i = xytod(nx, ny); + i = xytodir(nx, ny); for (j = DIR_LEFT(i); j < DIR_RIGHT(i); j++) { - dtoxy(&cc, j); + dirtocoord(&cc, j); if (goodpos(cc.x, cc.y, mtmp, 0)) goto dognext; } for (j = DIR_LEFT2(i); j < DIR_RIGHT2(i); j++) { - dtoxy(&cc, j); + dirtocoord(&cc, j); if (goodpos(cc.x, cc.y, mtmp, 0)) goto dognext; } diff --git a/src/dothrow.c b/src/dothrow.c index df019f6e4..952913a43 100644 --- a/src/dothrow.c +++ b/src/dothrow.c @@ -268,7 +268,7 @@ throw_obj(struct obj *obj, int shotlimit) } freeinv(otmp); throwit(otmp, wep_mask, twoweap, oldslot); - (void) encumber_msg(); + encumber_msg(); } gm.m_shot.n = gm.m_shot.i = 0; gm.m_shot.o = STRANGE_OBJECT; @@ -1268,7 +1268,14 @@ toss_up(struct obj *obj, boolean hitsroof) if (breaktest(obj)) { pline("%s hits the %s.", Doname2(obj), ceiling(u.ux, u.uy)); breakmsg(obj, !Blind); - return breakobj(obj, u.ux, u.uy, TRUE, TRUE) ? FALSE : TRUE; + /* crackable armor will return True for breaktest() but will + usually return False for breakobj() */ + if (!breakobj(obj, u.ux, u.uy, TRUE, TRUE)) { + hitfloor(obj, FALSE); + gt.thrownobj = 0; + return TRUE; + } + return FALSE; } action = "hits"; } else { @@ -1467,6 +1474,37 @@ swallowit(struct obj *obj) throwit_return(TRUE); } +/* thrown object hits a monster. + mon may be NULL. + returns TRUE if shopkeeper caught the object. + may delete object, clearing gt.thrownobj */ +boolean +throwit_mon_hit(struct obj *obj, struct monst *mon) +{ + if (mon) { + boolean obj_gone; + + if (mon->isshk && obj->where == OBJ_MINVENT && obj->ocarry == mon) { + return TRUE; + } + (void) snuff_candle(obj); + gn.notonhead = (gb.bhitpos.x != mon->mx || gb.bhitpos.y != mon->my); + obj_gone = thitmonst(mon, obj); + /* Monster may have been tamed; this frees old mon [obsolete] */ + mon = m_at(gb.bhitpos.x, gb.bhitpos.y); + + /* [perhaps this should be moved into thitmonst or hmon] */ + if (mon && mon->isshk + && (!inside_shop(u.ux, u.uy) + || !strchr(in_rooms(mon->mx, mon->my, SHOPBASE), *u.ushops))) + hot_pursuit(mon); + + if (obj_gone) + gt.thrownobj = (struct obj *) 0; + } + return FALSE; +} + /* throw an object, NB: obj may be consumed in the process */ void throwit( @@ -1600,8 +1638,13 @@ throwit( range = BOLT_LIM; else range++; - } else if (obj->oclass != GEM_CLASS) + } else if (obj->oclass != GEM_CLASS) { range /= 2; + pline("You aren't wielding %s, so you throw your %s by %s.", + an(skill_name(weapon_type(obj))), + weapon_descr(obj), + body_part(HAND)); + } } if (Is_airlevel(&u.uz) || Levitation) { @@ -1649,27 +1692,9 @@ throwit( } } - if (mon) { - boolean obj_gone; - - if (mon->isshk && obj->where == OBJ_MINVENT && obj->ocarry == mon) { - throwit_return(TRUE); /* alert shk caught it */ - return; - } - (void) snuff_candle(obj); - gn.notonhead = (gb.bhitpos.x != mon->mx || gb.bhitpos.y != mon->my); - obj_gone = thitmonst(mon, obj); - /* Monster may have been tamed; this frees old mon [obsolete] */ - mon = m_at(gb.bhitpos.x, gb.bhitpos.y); - - /* [perhaps this should be moved into thitmonst or hmon] */ - if (mon && mon->isshk - && (!inside_shop(u.ux, u.uy) - || !strchr(in_rooms(mon->mx, mon->my, SHOPBASE), *u.ushops))) - hot_pursuit(mon); - - if (obj_gone) - gt.thrownobj = (struct obj *) 0; + if (throwit_mon_hit(obj, mon)) { + throwit_return(TRUE); /* alert shk caught it */ + return; } if (!gt.thrownobj) { @@ -1692,7 +1717,7 @@ throwit( if (!impaired && rn2(100)) { pline("%s to your hand!", Tobjnam(obj, "return")); obj = addinv_before(obj, oldslot); - (void) encumber_msg(); + encumber_msg(); /* addinv autoquivers an aklys if quiver is empty; if obj is quivered, remove it before wielding */ if (obj->owornmask & W_QUIVER) @@ -1879,7 +1904,7 @@ return_throw_to_inv( set_twoweap(TRUE); /* u.twoweap = TRUE */ } - (void) encumber_msg(); + encumber_msg(); return obj; } @@ -2117,7 +2142,7 @@ thitmonst( sho_obj_return_to_u(obj); obj = addinv(obj); /* back into your inventory */ nhUse(obj); - (void) encumber_msg(); + encumber_msg(); } return 1; /* caller doesn't need to place it */ } diff --git a/src/dungeon.c b/src/dungeon.c index 5b172b815..e0bd72d08 100644 --- a/src/dungeon.c +++ b/src/dungeon.c @@ -2564,6 +2564,8 @@ query_annotation(d_level *lev) int donamelevel(void) { + if (iflags.menu_requested) + return dooverview(); query_annotation((d_level *) 0); return ECMD_OK; } diff --git a/src/earlyarg.c b/src/earlyarg.c new file mode 100755 index 000000000..78feae94a --- /dev/null +++ b/src/earlyarg.c @@ -0,0 +1,807 @@ +/* NetHack 3.7 earlyarg.c $NHDT-Date: 1771213100 2026/02/15 19:38:20 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.286 $ */ +/* Copyright (c) Robert Patrick Rankin, 2012. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "dlb.h" + +staticfn void debug_fields(char *); +#ifndef NODUMPENUMS +staticfn void dump_enums(void); +#endif +ATTRNORETURN staticfn void opt_terminate(void) NORETURN; +ATTRNORETURN staticfn void opt_usage(const char *) NORETURN; +ATTRNORETURN staticfn void scores_only(int, char **, const char *) NORETURN; +staticfn char *lopt(char *, int, const char *, const char *, int *, char ***); +staticfn void consume_arg(int, int *, char ***); +staticfn void consume_two_args(int, int *, char ***); + +#ifdef UNIX +extern boolean whoami(void); +#endif + +/* + * Argument processing helpers - for xxmain() to share + * and call. + * + * These should return TRUE if the argument matched, + * whether the processing of the argument was + * successful or not. + * + * Most of these do their thing, then after returning + * to xxmain(), the code exits without starting a game. + * + */ + +static const struct early_opt earlyopts[] = { + { ARG_DEBUG, "debug", 5, TRUE }, + { ARG_VERSION, "version", 4, TRUE }, + { ARG_SHOWPATHS, "showpaths", 8, FALSE }, +#ifndef NODUMPENUMS + { ARG_DUMPENUMS, "dumpenums", 9, FALSE }, +#endif + { ARG_DUMPGLYPHIDS, "dumpglyphids", 12, FALSE }, + { ARG_DUMPMONGEN, "dumpmongen", 10, FALSE }, + { ARG_DUMPWEIGHTS, "dumpweights", 11, FALSE }, +#ifdef WIN32 + { ARG_WINDOWS, "windows", 4, TRUE }, +#endif +#if defined(CRASHREPORT) + { ARG_BIDSHOW, "bidshow", 7, FALSE }, +#endif +}; + +static char ArgVal_novalue[] = "[nothing]"; /* note: not 'const' */ + +enum cmdlinearg { + ArgValRequired = 0, + ArgValOptional = 1, + ArgValDisallowed = 2, + ArgVal_mask = (1 | 2), + ArgNamOneLetter = 4, + ArgNam_mask = 4, + ArgErrSilent = 0, + ArgErrComplain = 8, + ArgErr_mask = 8 +}; + +/* approximate 'getopt_long()' for one option; all the comments refer to + "-windowtype" but the code isn't specific to that */ +staticfn char * +lopt(char *arg, /* command line token; beginning matches 'optname' */ + int lflags, /* cmdlinearg | errorhandling */ + const char *optname, /* option's name; "-windowtype" in examples below */ + const char *origarg, /* 'arg' might have had a dash prefix removed */ + int *argc_p, /* argc that can have changes passed to caller */ + char ***argv_p) /* argv[] ditto */ +{ + int argc = *argc_p; + char **argv = *argv_p; + char *p, *nextarg = (argc > 1 && argv[1][0] != '-') ? argv[1] : 0; + int l, opttype = (lflags & ArgVal_mask); + boolean oneletterok = ((lflags & ArgNam_mask) == ArgNamOneLetter), + complain = ((lflags & ArgErr_mask) == ArgErrComplain); + + /* first letter must match */ + if (arg[1] != optname[1]) { + loptbail: + if (complain) + config_error_add("Unknown option: %.60s", origarg); + return (char *) 0; + loptnotallowed: + if (complain) + config_error_add("Value not allowed: %.60s", origarg); + return (char *) 0; + loptrequired: + if (complain) + config_error_add("Missing required value: %.60s", origarg); + return (char *) 0; + } + + if ((p = strchr(arg, '=')) == 0) + p = strchr(arg, ':'); + if (p && opttype == ArgValDisallowed) + goto loptnotallowed; + + l = (int) (p ? (long) (p - arg) : (long) strlen(arg)); + if ((l > 2 || oneletterok) && !strncmp(arg, optname, l)) { + /* "-windowtype[=foo]" */ + if (p) + ++p; /* past '=' or ':' */ + else if (opttype == ArgValRequired) + p = eos(arg); /* we have "-w[indowtype]" w/o "=foo" + * so we'll take foo from next element */ + else + return ArgVal_novalue; + } else if (oneletterok) { + /* "-w..." but not "-w[indowtype[=foo]]" */ + if (!p) { + p = &arg[2]; /* past 'w' of "-wfoo" */ +#if 0 /* -x:value could work but is not supported (callers don't expect it) \ + */ + } else if (p == arg + 2) { + ++p; /* past ':' of "-w:foo" */ +#endif + } else { + /* "-w...=foo" but not "-w[indowtype]=foo" */ + goto loptbail; + } + } else { + goto loptbail; + } + if (!p || !*p) { + /* "-w[indowtype]" w/o '='/':' if there is a next element, use + it for "foo"; if not, supply a non-Null bogus value */ + if (nextarg + && (opttype == ArgValRequired || opttype == ArgValOptional)) + p = nextarg, --(*argc_p), ++(*argv_p); + else if (opttype == ArgValRequired) + goto loptrequired; + else + p = ArgVal_novalue; /* there is no next element */ + } + return p; +} +/* move argv[ndx] to end of argv[] array, then reduce argc to hide it; + prevents process_options() from encountering it after early_options() + has processed it; elements get reordered but all remain intact */ +staticfn void +consume_arg(int ndx, int *ac_p, char ***av_p) +{ + char *gone, **av = *av_p; + int i, ac = *ac_p; + + /* "-one -two -three -four" -> "-two -three -four -one" */ + if (ac > 2) { + gone = av[ndx]; + for (i = ndx + 1; i < ac; ++i) + av[i - 1] = av[i]; + av[ac - 1] = gone; + } + --(*ac_p); +} + +/* consume two tokens for '-argname value' w/o '=' or ':' */ +staticfn void +consume_two_args(int ndx, int *ac_p, char ***av_p) +{ + /* when consuming "-two arg" from "-two arg -three -four", + the *ac_p manipulation results in "-three -four -two arg" + rather than the "-three -four arg -two" that would happen + with just two ordinary consume_arg() calls */ + consume_arg(ndx, ac_p, av_p); + ++(*ac_p); /* bring the final slot back into view */ + consume_arg(ndx, ac_p, av_p); + --(*ac_p); /* take away restored slot */ +} + +/* process some command line arguments before loading options */ +void +early_options(int *argc_p, char ***argv_p, char **hackdir_p) +{ + char **argv, *arg, *origarg; + int argc, oldargc, ndx = 0, consumed = 0; + + config_error_init(FALSE, "command line", FALSE); + + /* treat "nethack ?" as a request for usage info; due to shell + processing, player likely has to use "nethack \?" or "nethack '?'" + [won't work if used as "nethack -dpath ?" or "nethack -d path ?"] */ + if (*argc_p > 1 && !strcmp((*argv_p)[1], "?")) + opt_usage(*hackdir_p); /* doesn't return */ + + /* + * Both *argc_p and *argv_p account for the program name as (*argv_p)[0]; + * local argc and argv implicitly discard that (by starting 'ndx' at 1). + * argcheck() doesn't mind, prscore() (via scores_only()) does (for the + * number of args it gets passed, not for the value of argv[0]). + */ + for (ndx = 1; ndx < *argc_p; ndx += (consumed ? 0 : 1)) { + consumed = 0; + argc = *argc_p - ndx; + argv = *argv_p + ndx; + + arg = origarg = argv[0]; + /* skip any args intended for deferred options */ + if (*arg != '-') + continue; + /* allow second dash if arg name is longer than one character */ + if (arg[0] == '-' && arg[1] == '-' && arg[2] != '\0' + && (arg[3] != '\0' && arg[3] != '=' && arg[3] != ':')) + ++arg; + + switch (arg[1]) { /* char after leading dash */ + case 'b': +#ifdef CRASHREPORT + // --bidshow + if (argcheck(argc, argv, ARG_BIDSHOW) == 2) { + opt_terminate(); + /*NOTREACHED*/ + } +#endif + break; + case 'd': + if (argcheck(argc, argv, ARG_DEBUG) == 1) { + consume_arg(ndx, argc_p, argv_p), consumed = 1; +#ifndef NODUMPENUMS + } else if (argcheck(argc, argv, ARG_DUMPENUMS) == 2) { + opt_terminate(); + /*NOTREACHED*/ +#endif + } else if (argcheck(argc, argv, ARG_DUMPMONGEN) == 2) { + opt_terminate(); + /*NOTREACHED*/ + } else if (argcheck(argc, argv, ARG_DUMPWEIGHTS) == 2) { + opt_terminate(); + /*NOTREACHED*/ + } else { +#ifdef CHDIR + oldargc = argc; + arg = lopt(arg, + (ArgValRequired | ArgNamOneLetter | ArgErrSilent), + "-directory", origarg, &argc, &argv); + if (!arg) + error("Flag -d must be followed by a directory name."); + if (*arg != 'e') { /* avoid matching -decgraphics or -debug */ + *hackdir_p = arg; + if (oldargc == argc) + consume_arg(ndx, argc_p, argv_p), consumed = 1; + else + consume_two_args(ndx, argc_p, argv_p), consumed = 2; + } +#endif /* CHDIR */ + } + break; + case 'h': + case '?': + if (lopt(arg, ArgValDisallowed, "-help", origarg, &argc, &argv) + || lopt(arg, ArgValDisallowed | ArgNamOneLetter, "-?", + origarg, &argc, &argv)) + opt_usage(*hackdir_p); /* doesn't return */ + break; + case 'n': + oldargc = argc; + if (!strcmp(arg, "-no-nethackrc")) /* no abbreviation allowed */ + arg = nhStr("/dev/null"); + else + arg = lopt(arg, (ArgValRequired | ArgErrComplain), + "-nethackrc", origarg, &argc, &argv); + if (arg) { + gc.cmdline_rcfile = dupstr(arg); + if (oldargc == argc) + consume_arg(ndx, argc_p, argv_p), consumed = 1; + else + consume_two_args(ndx, argc_p, argv_p), consumed = 2; + } + break; + case 's': + if (argcheck(argc, argv, ARG_SHOWPATHS) == 2) { + gd.deferred_showpaths = TRUE; + gd.deferred_showpaths_dir = *hackdir_p; + config_error_done(); + return; + } + /* check for "-s" request to show scores */ + if (lopt(arg, + ((ArgValDisallowed | ArgErrComplain) + /* only accept one-letter if there is just one + dash; reject "--s" because prscore() via + scores_only() doesn't understand it */ + | ((origarg[1] != '-') ? ArgNamOneLetter : 0)), + /* [ought to omit val-disallowed and accept + --scores=foo since -s foo and -sfoo are + allowed, but -s form can take more than one + space-separated argument and --scores=foo + isn't suited for that] */ + "-scores", origarg, &argc, &argv)) { + /* at this point, argv[0] contains "-scores" or a leading + substring of it; prscore() (via scores_only()) expects + that to be in argv[1] so we adjust the pointer to make + that be the case; if there are any non-early args waiting + to be passed along to process_options(), the resulting + argv[0] will be one of those rather than the program + name but prscore() doesn't care */ + scores_only(argc + 1, argv - 1, *hackdir_p); + /*NOTREACHED*/ + } + break; + case 'u': +#if defined(UNIX) + if (lopt(arg, ArgValDisallowed, "-usage", origarg, &argc, &argv)) + opt_usage(*hackdir_p); +#elif defined(WIN32) || defined(MSDOS) || defined(AMIGA) + if (arg[2]) { + (void) strncpy(svp.plname, arg + 2, sizeof(svp.plname) - 1); + } else if (ndx + 1 < *argc_p) { + const char *nextarg = (*argv_p)[ndx + 1]; + + if (nextarg[0] != '-') { + (void) strncpy(svp.plname, nextarg, sizeof(svp.plname) - 1); + } else { + raw_print("Player name expected after -u\n"); + } + } +#endif + break; + case 'v': + if (argcheck(argc, argv, ARG_VERSION) == 2) { + opt_terminate(); + /*NOTREACHED*/ + } + break; + case 'w': /* windowtype: "-wfoo" or "-w[indowtype]=foo" + * or "-w[indowtype]:foo" or "-w[indowtype] foo" */ + arg = + lopt(arg, (ArgValRequired | ArgNamOneLetter | ArgErrComplain), + "-windowtype", origarg, &argc, &argv); + if (gc.cmdline_windowsys) + free((genericptr_t) gc.cmdline_windowsys); + gc.cmdline_windowsys = arg ? dupstr(arg) : NULL; + break; + #if !defined(UNIX) && !defined(VMS) + case 'D': + wizard = TRUE, discover = FALSE; + break; + case 'X': + discover = TRUE, wizard = FALSE; + break; +#endif + default: + break; + } + } + /* empty or "N errors on command line" */ + config_error_done(); + return; +} +/* for command-line options that perform some immediate action and then + terminate the program without starting play, like 'nethack --version' + or 'nethack -s Zelda'; do some cleanup before that termination */ +ATTRNORETURN staticfn void +opt_terminate(void) +{ + program_state.early_options = 0; + config_error_done(); /* free memory allocated by config_error_init() */ + + nh_terminate(EXIT_SUCCESS); + /*NOTREACHED*/ +} + +ATTRNORETURN staticfn void +opt_usage(const char *hackdir) +{ +#ifdef CHDIR + chdirx(hackdir, TRUE); +#else + nhUse(hackdir); +#endif + dlb_init(); + + genl_display_file(USAGEHELP, TRUE); + opt_terminate(); +} +/* show the sysconf file name, playground directory, run-time configuration + file name, dumplog file name if applicable, and some other things */ +ATTRNORETURN void +after_opt_showpaths(const char *dir) +{ +#ifdef CHDIR + chdirx(dir, FALSE); +#else + nhUse(dir); +#endif + opt_terminate(); + /*NOTREACHED*/ +} + +/* handle "-s [character-names]" to show all the entries + in the high scores file ('record') belonging to particular characters; + nethack will end after doing so without starting play */ +ATTRNORETURN staticfn void +scores_only(int argc, char **argv, const char *dir) +{ + /* do this now rather than waiting for final termination, in case there + is an error summary coming */ + config_error_done(); + +#ifdef CHDIR + chdirx(dir, FALSE); +#else + nhUse(dir); +#endif +#ifdef SYSCF + iflags.initoptions_noterminate = TRUE; + initoptions(); /* sysconf options affect whether panictrace is enabled */ + iflags.initoptions_noterminate = FALSE; +#endif +#ifdef PANICTRACE + ARGV0 = gh.hname; /* save for possible stack trace */ +#ifndef NO_SIGNAL + panictrace_setsignals(TRUE); +#endif +#endif +#ifdef UNIX + (void) whoami(); /* set up default plname[] */ +#endif + prscore(argc, argv); +#ifdef MSWIN_GRAPHICS + /* NetHackW can also support WINDOWPORT(curses) now, so check */ + if (WINDOWPORT(mswin)) { + wait_synch(); + } +#endif + + nh_terminate(EXIT_SUCCESS); /* bypass opt_terminate() */ + /*NOTREACHED*/ +} + +/* + * Returns: + * 0 = no match + * 1 = found and skip past this argument + * 2 = found and trigger immediate exit + */ +int +argcheck(int argc, char *argv[], enum earlyarg e_arg) +{ + int i, idx; + boolean match = FALSE; + char *userea = (char *) 0; + const char *dashdash = ""; + + for (idx = 0; idx < SIZE(earlyopts); idx++) { + if (earlyopts[idx].e == e_arg){ + break; + } + } + if (idx >= SIZE(earlyopts) || argc < 1) + return 0; + + for (i = 0; i < argc; ++i) { + if (argv[i][0] != '-') + continue; + if (argv[i][1] == '-') { + userea = &argv[i][2]; + dashdash = "-"; + } else { + userea = &argv[i][1]; + } + match = match_optname(userea, earlyopts[idx].name, + earlyopts[idx].minlength, + earlyopts[idx].valallowed); + if (match) + break; + } + + if (match) { + const char *extended_opt = strchr(userea, ':'); + + if (!extended_opt) + extended_opt = strchr(userea, '='); + switch(e_arg) { + case ARG_DEBUG: + if (extended_opt) { + char *cpy_extended_opt; + + cpy_extended_opt = dupstr(extended_opt); + debug_fields(cpy_extended_opt + 1); + free((genericptr_t) cpy_extended_opt); + } + return 1; + case ARG_VERSION: { + boolean insert_into_pastebuf = FALSE; + + if (extended_opt) { + extended_opt++; + /* Deprecated in favor of "copy" - remove no later + than next major version */ + if (match_optname(extended_opt, "paste", 5, FALSE)) { + insert_into_pastebuf = TRUE; + } else if (match_optname(extended_opt, "copy", 4, FALSE)) { + insert_into_pastebuf = TRUE; + } else if (match_optname(extended_opt, "dump", 4, FALSE)) { + /* version number plus enabled features and sanity + values that the program compares against the same + thing recorded in save and bones files to check + whether they're being used compatibly */ + dump_version_info(); + return 2; /* done */ + } else if (!match_optname(extended_opt, "show", 4, FALSE)) { + raw_printf("-%sversion can only be extended with" + " -%sversion:copy or :dump or :show.\n", + dashdash, dashdash); + /* exit after we've reported bad command line argument */ + return 2; + } + } + early_version_info(insert_into_pastebuf); + return 2; + } + case ARG_SHOWPATHS: + return 2; +#ifndef NODUMPENUMS + case ARG_DUMPENUMS: + dump_enums(); + return 2; +#endif + case ARG_DUMPGLYPHIDS: + dump_glyphids(); + return 2; + case ARG_DUMPMONGEN: + dump_mongen(); + return 2; + case ARG_DUMPWEIGHTS: + dump_weights(); + return 2; +#ifdef CRASHREPORT + case ARG_BIDSHOW: + crashreport_bidshow(); + return 2; +#endif +#ifdef WIN32 + case ARG_WINDOWS: + if (extended_opt) { + extended_opt++; + return windows_early_options(extended_opt); + } + FALLTHROUGH; + /*FALLTHRU*/ +#endif + default: + break; + } + }; + return 0; +} + +/* + * These are internal controls to aid developers with + * testing and debugging particular aspects of the code. + * They are not player options and the only place they + * are documented is right here. No gameplay is altered. + * + * test - test whether this parser is working + * ttystatus - TTY: + * immediateflips - WIN32: turn off display performance + * optimization so that display output + * can be debugged without buffering. + * fuzzer - enable fuzzer without debugger intervention. + */ +staticfn void +debug_fields(char *opts) +{ + char *op; + boolean negated = FALSE; + + while ((op = strchr(opts, ',')) != 0) { + *op++ = 0; + /* recurse */ + debug_fields(op); + } + if (strlen(opts) > BUFSZ / 2) + return; + + + /* strip leading and trailing white space */ + while (isspace((uchar) *opts)) + opts++; + op = eos((char *) opts); + while (--op >= opts && isspace((uchar) *op)) + *op = '\0'; + + if (!*opts) { + /* empty */ + return; + } + while ((*opts == '!') || !strncmpi(opts, "no", 2)) { + if (*opts == '!') + opts++; + else + opts += 2; + negated = !negated; + } + if (match_optname(opts, "test", 4, FALSE)) + iflags.debug.test = negated ? FALSE : TRUE; +#ifdef TTY_GRAPHICS + if (match_optname(opts, "ttystatus", 9, FALSE)) + iflags.debug.ttystatus = negated ? FALSE : TRUE; +#endif +#ifdef WIN32 + if (match_optname(opts, "immediateflips", 14, FALSE)) + iflags.debug.immediateflips = negated ? FALSE : TRUE; +#endif + if (match_optname(opts, "fuzzer", 4, FALSE)) + iflags.fuzzerpending = TRUE; + return; +} + +#if !defined(NODUMPENUMS) +/* monsdump[] and objdump[] are also used in utf8map.c */ + +#define DUMP_ENUMS +#define UNPREFIXED_COUNT (5) +struct enum_dump monsdump[] = { +#include "monsters.h" + { NUMMONS, "NUMMONS" }, + { NON_PM, "NON_PM" }, + { LOW_PM, "LOW_PM" }, + { HIGH_PM, "HIGH_PM" }, + { SPECIAL_PM, "SPECIAL_PM" } +}; +struct enum_dump objdump[] = { +#include "objects.h" + { NUM_OBJECTS, "NUM_OBJECTS" }, +}; + +#define DUMP_ENUMS_PCHAR +static struct enum_dump defsym_cmap_dump[] = { +#include "defsym.h" + { MAXPCHARS, "MAXPCHARS" }, +}; +#undef DUMP_ENUMS_PCHAR + +#define DUMP_ENUMS_MONSYMS +static struct enum_dump defsym_mon_syms_dump[] = { +#include "defsym.h" + { MAXMCLASSES, "MAXMCLASSES" }, +}; +#undef DUMP_ENUMS_MONSYMS + +#define DUMP_ENUMS_MONSYMS_DEFCHAR +static struct enum_dump defsym_mon_defchars_dump[] = { +#include "defsym.h" +}; +#undef DUMP_ENUMS_MONSYMS_DEFCHAR + +#define DUMP_ENUMS_OBJCLASS_DEFCHARS +static struct enum_dump objclass_defchars_dump[] = { +#include "defsym.h" +}; +#undef DUMP_ENUMS_OBJCLASS_DEFCHARS + +#define DUMP_ENUMS_OBJCLASS_CLASSES +static struct enum_dump objclass_classes_dump[] = { +#include "defsym.h" + { MAXOCLASSES, "MAXOCLASSES" }, +}; +#undef DUMP_ENUMS_OBJCLASS_CLASSES + +#define DUMP_ENUMS_OBJCLASS_SYMS +static struct enum_dump objclass_syms_dump[] = { +#include "defsym.h" +}; +#undef DUMP_ENUMS_OBJCLASS_SYMS + +#define DUMP_ARTI_ENUM +static struct enum_dump arti_enum_dump[] = { +#include "artilist.h" + { AFTER_LAST_ARTIFACT, "AFTER_LAST_ARTIFACT" } +}; +#undef DUMP_ARTI_ENUM + +/* the enums are not part of hack.h for this one */ +#define DUMP_MCASTU_ENUM1 +enum mcast_dumpenum_spells { + #include "mcastu.h" +}; +#undef DUMP_MCASTU_ENUM1 + +#define DUMP_MCASTU_ENUM2 +static struct enum_dump mcastu_enum_dump[] = { +#include "mcastu.h" +}; +#undef DUMP_MCASTU_ENUM2 + +#undef DUMP_ENUMS + + +#ifndef NODUMPENUMS + +staticfn void +dump_enums(void) +{ + enum enum_dumps { + monsters_enum, + objects_enum, + objects_misc_enum, + defsym_cmap_enum, + defsym_mon_syms_enum, + defsym_mon_defchars_enum, + objclass_defchars_enum, + objclass_classes_enum, + objclass_syms_enum, + arti_enum, + mcastu_enum, + NUM_ENUM_DUMPS + }; + +#define dump_om(om) { om, #om } + static const struct enum_dump omdump[] = { + dump_om(LAST_GENERIC), + dump_om(OBJCLASS_HACK), + dump_om(FIRST_OBJECT), + dump_om(FIRST_AMULET), + dump_om(LAST_AMULET), + dump_om(FIRST_SPELL), + dump_om(LAST_SPELL), + dump_om(MAXSPELL), + dump_om(FIRST_REAL_GEM), + dump_om(LAST_REAL_GEM), + dump_om(FIRST_GLASS_GEM), + dump_om(LAST_GLASS_GEM), + dump_om(NUM_REAL_GEMS), + dump_om(NUM_GLASS_GEMS), + dump_om(MAX_GLYPH), + }; +#undef dump_om + + static const struct enum_dump *const ed[NUM_ENUM_DUMPS] = { + monsdump, objdump, omdump, + defsym_cmap_dump, defsym_mon_syms_dump, + defsym_mon_defchars_dump, + objclass_defchars_dump, + objclass_classes_dump, + objclass_syms_dump, + arti_enum_dump, + mcastu_enum_dump, + }; + + static const struct de_params { + const char *const title; + const char *const pfx; + int unprefixed_count; + int dumpflgs; /* 0 = dump numerically only, 1 = add 'char' comment */ + int szd; + } edmp[NUM_ENUM_DUMPS] = { + { "monnums", "PM_", UNPREFIXED_COUNT, 0, SIZE(monsdump) }, + { "objects_nums", "", 1, 0, SIZE(objdump) }, + { "misc_object_nums", "", 1, 0, SIZE(omdump) }, + { "cmap_symbols", "", 1, 0, SIZE(defsym_cmap_dump) }, + { "mon_syms", "", 1, 0, SIZE(defsym_mon_syms_dump) }, + { "mon_defchars", "", 1, 1, SIZE(defsym_mon_defchars_dump) }, + { "objclass_defchars", "", 1, 1, SIZE(objclass_defchars_dump) }, + { "objclass_classes", "", 1, 0, SIZE(objclass_classes_dump) }, + { "objclass_syms", "", 1, 0, SIZE(objclass_syms_dump) }, + { "artifacts_nums", "", 1, 0, SIZE(arti_enum_dump) }, + { "mcast_spells", "MCAST_", 0, 0, SIZE(mcastu_enum_dump) }, + }; + + const char *nmprefix; + int i, j, nmwidth; + char comment[BUFSZ]; + + for (i = 0; i < NUM_ENUM_DUMPS; ++ i) { + raw_printf("enum %s = {", edmp[i].title); + for (j = 0; j < edmp[i].szd; ++j) { + nmprefix = (j >= edmp[i].szd - edmp[i].unprefixed_count) + ? "" : edmp[i].pfx; /* "" or "PM_" */ + nmwidth = 27 - (int) strlen(nmprefix); /* 27 or 24 */ + if (edmp[i].dumpflgs > 0) { + Snprintf(comment, sizeof comment, + " /* '%c' */", + (ed[i][j].val >= 32 && ed[i][j].val <= 126) + ? ed[i][j].val : ' '); + } else { + comment[0] = '\0'; + } + raw_printf(" %s%*s = %3d,%s", + nmprefix, -nmwidth, + ed[i][j].nm, ed[i][j].val, + comment); + } + raw_print("};"); + raw_print(""); + } + raw_print(""); +} +#undef UNPREFIXED_COUNT +#endif /* NODUMPENUMS */ + +void +dump_glyphids(void) +{ + dump_all_glyphids(stdout); +} +#endif /* !NODUMPENUMS */ + +/*allmain.c*/ diff --git a/src/eat.c b/src/eat.c index b15f36ade..e62a2ffea 100644 --- a/src/eat.c +++ b/src/eat.c @@ -130,7 +130,7 @@ init_uhunger(void) u.uhs = NOT_HUNGRY; if (ATEMP(A_STR) < 0) { ATEMP(A_STR) = 0; - (void) encumber_msg(); + encumber_msg(); } } @@ -1546,8 +1546,14 @@ consume_tin(const char *mesg) if (r != SPINACH_TIN) { mnum = tin->corpsenm; if (mnum == NON_PM) { - pline("It turns out to be empty."); - tin->dknown = tin->known = 1; + if (Hallucination) + pline("It's full of %s.", + rn2(2) ? "air elemental souffle" + : "dehydrated water"); + else + pline("It turns out to be empty."); + observe_object(tin); + tin->known = 1; tin = costly_tin(COST_OPEN); use_up_tin(tin); if (always_eat) @@ -1579,8 +1585,10 @@ consume_tin(const char *mesg) if (y_n("Eat it?") == 'n') { if (flags.verbose) You("discard the open tin."); - if (!Hallucination) - tin->dknown = tin->known = 1; + if (!Hallucination) { + observe_object(tin); + tin->known = 1; + } tin = costly_tin(COST_OPEN); use_up_tin(tin); return; @@ -1594,7 +1602,8 @@ consume_tin(const char *mesg) eating_conducts(&mons[mnum]); - tin->dknown = tin->known = 1; + observe_object(tin); + tin->known = 1; /* charge for one at pre-eating cost */ tin = svc.context.tin.tin = costly_tin(COST_OPEN); @@ -1641,7 +1650,8 @@ consume_tin(const char *mesg) Blind ? "" : " ", Blind ? "" : hcolor(NH_GREEN)); } else { pline("It contains spinach."); - tin->dknown = tin->known = 1; + observe_object(tin); + tin->known = 1; } if (!always_eat && y_n("Eat it?") == 'n') { @@ -2265,7 +2275,8 @@ eataccessory(struct obj *otmp) if (u.uhp <= 0) return; /* died from sink fall */ } - otmp->known = otmp->dknown = 1; /* by taste */ + observe_object(otmp); + otmp->known = 1; /* by taste */ if (!rn2(otmp->oclass == RING_CLASS ? 3 : 5)) { switch (otmp->otyp) { default: diff --git a/src/end.c b/src/end.c index ba2ea4f19..389d8f548 100644 --- a/src/end.c +++ b/src/end.c @@ -9,9 +9,6 @@ #ifndef NO_SIGNAL #include #endif -#ifndef LONG_MAX -#include -#endif #include "dlb.h" #ifndef SFCTOOL @@ -98,7 +95,8 @@ done2(void) && y_n("Switch from the tutorial back to regular play?") == 'y') abandon_tutorial = TRUE; - if (abandon_tutorial || !paranoid_query(ParanoidQuit, "Really quit?")) { + if (abandon_tutorial || !paranoid_query( + ParanoidQuit, "Really quit without saving?")) { #ifndef NO_SIGNAL (void) signal(SIGINT, (SIG_RET_TYPE) done1); #endif @@ -478,7 +476,8 @@ staticfn boolean should_query_disclose_option(int category, char *defquery) { int idx; - char disclose, *dop; + char disclose; + const char *dop; *defquery = 'n'; if ((dop = strchr(disclosure_options, category)) != 0) { @@ -599,14 +598,16 @@ dump_everything( /* overview of the game up to this point */ show_gamelog((how >= PANICKED) ? ENL_GAMEOVERALIVE : ENL_GAMEOVERDEAD); putstr(0, 0, ""); - list_vanquished('d', FALSE); /* 'd' => 'y' */ - putstr(0, 0, ""); - list_genocided('d', FALSE); /* 'd' => 'y' */ - putstr(0, 0, ""); + show_spells(); /* ends with a blank line */ + show_skills(); /* ends with a blank line */ show_conduct((how >= PANICKED) ? 1 : 2); putstr(0, 0, ""); show_overview((how >= PANICKED) ? 1 : 2, how); putstr(0, 0, ""); + list_vanquished('d', FALSE); /* 'd' => 'y' */ + putstr(0, 0, ""); + list_genocided('d', FALSE); /* 'd' => 'y' */ + putstr(0, 0, ""); dump_redirect(FALSE); #else nhUse(how); @@ -921,7 +922,8 @@ artifact_score( if (counting) { u.urexp = nowrap_add(u.urexp, points); } else { - discover_object(otmp->otyp, TRUE, FALSE); + discover_object(otmp->otyp, TRUE, TRUE, FALSE); + /* not observe_object; dead characters don't observe */ otmp->known = otmp->dknown = otmp->bknown = otmp->rknown = 1; /* assumes artifacts don't have quan > 1 */ Sprintf(pbuf, "%s%s (worth %ld %s and %ld points)", @@ -1254,7 +1256,8 @@ really_done(int how) */ for (obj = gi.invent; obj; obj = nextobj) { nextobj = obj->nobj; - discover_object(obj->otyp, TRUE, FALSE); + discover_object(obj->otyp, TRUE, TRUE, FALSE); + /* observe_object not necessary after discover_object */ obj->known = obj->bknown = obj->dknown = obj->rknown = 1; set_cknown_lknown(obj); /* set flags when applicable */ /* we resolve Schroedinger's cat now in case of both @@ -1496,9 +1499,10 @@ really_done(int how) if (objects[typ].oc_class != GEM_CLASS || typ <= LAST_REAL_GEM) { otmp = mksobj(typ, FALSE, FALSE); - discover_object(otmp->otyp, TRUE, FALSE); - otmp->known = 1; /* for fake amulets */ + discover_object(otmp->otyp, TRUE, TRUE, FALSE); otmp->dknown = 1; /* seen it (blindness fix) */ + /* observe_object not necessary after discover_object */ + otmp->known = 1; /* for fake amulets */ if (has_oname(otmp)) free_oname(otmp); otmp->quan = count; @@ -1634,9 +1638,9 @@ container_contents( (boolean (*)(OBJ_P)) 0); for (srtc = sortedcobj; (obj = srtc->obj) != 0; ++srtc) { if (identified) { - discover_object(obj->otyp, TRUE, FALSE); - obj->known = obj->bknown = obj->dknown - = obj->rknown = 1; + discover_object(obj->otyp, TRUE, TRUE, FALSE); + obj->dknown = 1; /* observe_object unnecessary */ + obj->known = obj->bknown = obj->rknown = 1; if (Is_container(obj) || obj->otyp == STATUE) obj->cknown = obj->lknown = 1; } @@ -1759,7 +1763,7 @@ save_killers(NHFILE *nhfp) if (update_file(nhfp)) { for (kptr = &svk.killer; kptr; kptr = kptr->next) { - Sfo_kinfo(nhfp, kptr, "kinfo"); + Sfo_kinfo(nhfp, kptr, "kinfo"); } } if (release_data(nhfp)) { diff --git a/src/engrave.c b/src/engrave.c index e029e87dc..22ca30aaf 100644 --- a/src/engrave.c +++ b/src/engrave.c @@ -370,18 +370,31 @@ read_engr_at(coordxy x, coordxy y) if (sensed) { char *et, buf[BUFSZ]; + const char *endpunct; int maxelen = (int) (sizeof buf /* sizeof "literal" counts terminating \0 */ - - sizeof "You feel the words: \"\"."); + - sizeof "You feel the words: \"\"."), + elen = (int) strlen(ep->engr_txt[actual_text]), + off = (int) (ep->engr_txt[actual_text] - engr_text_space(ep)); - if ((int) strlen(ep->engr_txt[actual_text]) > maxelen) { + if (elen > maxelen) { (void) strncpy(buf, ep->engr_txt[actual_text], maxelen); buf[maxelen] = '\0'; et = buf; + elen = maxelen; } else { et = ep->engr_txt[actual_text]; } - You("%s: \"%s\".", (Blind) ? "feel the words" : "read", et); + endpunct = ""; + if (elen < 2 + /* only skip if punctuation is original, not degraded char */ + || !((ep->engr_txt[pristine_text][off + elen - 1] + == et[elen - 1]) + && strchr(".!?", et[elen - 1]))) { + endpunct = "."; + } + You("%s: \"%s\"%s", (Blind) ? "feel the words" : "read", et, + endpunct); Strcpy(ep->engr_txt[remembered_text], ep->engr_txt[actual_text]); ep->eread = 1; ep->erevealed = 1; @@ -419,7 +432,7 @@ make_engr_at( head_engr = ep; ep->engr_x = x; ep->engr_y = y; - ep->engr_txt[actual_text] = (char *) (ep + 1); + ep->engr_txt[actual_text] = engr_text_space(ep); ep->engr_txt[remembered_text] = ep->engr_txt[actual_text] + smem; ep->engr_txt[pristine_text] = ep->engr_txt[remembered_text] + smem; for(i = 0; i < text_states; ++i) @@ -576,6 +589,7 @@ doengrave_sfx_item_WAN(struct _doengrave_ctx *de) /* NODIR wands */ case WAN_LIGHT: case WAN_SECRET_DOOR_DETECTION: + case WAN_STASIS: case WAN_CREATE_MONSTER: case WAN_WISHING: case WAN_ENLIGHTENMENT: @@ -1548,7 +1562,7 @@ save_engravings(NHFILE *nhfp) szeach = ep->engr_szeach; Sfo_unsigned(nhfp, &engr_alloc, "engraving-engr_alloc"); Sfo_engr(nhfp, ep, "engraving"); - ep->engr_txt[actual_text] = (char *)(ep + 1); + ep->engr_txt[actual_text] = engr_text_space(ep); ep->engr_txt[remembered_text] = ep->engr_txt[actual_text] + szeach; ep->engr_txt[pristine_text] = ep->engr_txt[remembered_text] + szeach; Sfo_char(nhfp, ep->engr_txt[actual_text], "engraving-actual_text", szeach); @@ -1583,7 +1597,7 @@ rest_engravings(NHFILE *nhfp) szeach = ep->engr_szeach; ep->nxt_engr = head_engr; head_engr = ep; - ep->engr_txt[actual_text] = (char *) (ep + 1); /* Andreas Bormann */ + ep->engr_txt[actual_text] = engr_text_space(ep); /* Andreas Bormann */ ep->engr_txt[remembered_text] = ep->engr_txt[actual_text] + szeach; ep->engr_txt[pristine_text] = ep->engr_txt[remembered_text] + szeach; Sfi_char(nhfp, ep->engr_txt[actual_text], diff --git a/src/files.c b/src/files.c index c984c673e..ae45f6c07 100644 --- a/src/files.c +++ b/src/files.c @@ -1174,7 +1174,7 @@ create_savefile(void) if (nhfp->fd >= 0) (void) setmode(nhfp->fd, O_BINARY); #endif - } + } } #if defined(VMS) && !defined(SECURE) /* @@ -1218,7 +1218,7 @@ open_savefile(void) #ifdef SAVEFILE_DEBUGGING nhfp->fplog = fopen("open-savefile.log", "w"); #endif - } + } #ifdef MAC nhfp->fd = macopen(fq_save, O_RDONLY | O_BINARY, SAVE_TYPE); #else @@ -2046,13 +2046,15 @@ doconvert_file(const char *filename, int sfstatus, boolean unconvert) } /* convert file */ -void nh_sfconvert(const char *filename) +void +nh_sfconvert(const char *filename) { (void) doconvert_file(filename, 0, FALSE); } /* unconvert file if it exists */ -void nh_sfunconvert(const char *filename) +void +nh_sfunconvert(const char *filename) { (void) doconvert_file(filename, 0, TRUE); } @@ -2140,7 +2142,8 @@ delete_convertedfile(const char *basefilename) return 0; } -void free_convert_filenames(void) +void +free_convert_filenames(void) { if (converted_filename) free((genericptr_t) converted_filename), converted_filename = 0; @@ -2515,7 +2518,7 @@ wizkit_addinv(struct obj *obj) return; /* subset of starting inventory pre-ID */ - obj->dknown = 1; + observe_object(obj); if (Role_if(PM_CLERIC)) obj->bknown = 1; /* ok to bypass set_bknown() */ /* same criteria as lift_object()'s check for available inventory slot */ @@ -2543,8 +2546,10 @@ proc_wizkit_line(char *buf) otmp = readobjnam(buf, (struct obj *) 0); if (otmp) { - if (otmp != &hands_obj) + if (otmp != &hands_obj) { + wish_history_add(buf); wizkit_addinv(otmp); + } } else { /* .60 limits output line width to 79 chars */ config_error_add("Bad wizkit item: \"%.60s\"", buf); @@ -3136,11 +3141,6 @@ debugcore(const char *filename, boolean wildcards) #endif /*DEBUG*/ #ifndef SFCTOOL -#ifdef UNIX -#ifndef PATH_MAX -#include -#endif -#endif #define SYSCONFFILE "system configuration file" diff --git a/src/fountain.c b/src/fountain.c index 979bb0518..78925b04c 100644 --- a/src/fountain.c +++ b/src/fountain.c @@ -641,7 +641,8 @@ drinksink(void) otmp->cursed = otmp->blessed = 0; pline("Some %s liquid flows from the faucet.", Blind ? "odd" : hcolor(OBJ_DESCR(objects[otmp->otyp]))); - otmp->dknown = !(Blind || Hallucination); + if(!(Blind || Hallucination)) + observe_object(otmp); otmp->quan++; /* Avoid panic upon useup() */ otmp->fromsink = 1; /* kludge for docall() */ (void) dopotion(otmp); diff --git a/src/getpos.c b/src/getpos.c index 36e9d95ea..ed53d334b 100644 --- a/src/getpos.c +++ b/src/getpos.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 getpos.c $NHDT-Date: 1723875487 2024/08/17 06:18:07 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.3 $ */ +/* NetHack 3.7 getpos.c $NHDT-Date: 1763708572 2025/11/20 23:02:52 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.6 $ */ /*-Copyright (c) Pasi Kallinen, 2023. */ /* NetHack may be freely redistributed. See license for details. */ @@ -18,6 +18,7 @@ staticfn void gloc_filter_floodfill(coordxy, coordxy); staticfn void gloc_filter_init(void); staticfn void gloc_filter_done(void); staticfn void gather_locs(coord **, int *, int); +staticfn boolean known_vibrating_square_at(coordxy, coordxy); staticfn void truncate_to_map(coordxy *, coordxy *, schar, schar); staticfn void getpos_refresh(void); /* Callback function for getpos() to highlight desired map locations. @@ -192,7 +193,7 @@ getpos_help(boolean force, const char *goal) putstr(tmpwin, 0, sbuf); putstr(tmpwin, 0, "Or enter a background symbol (ex. '<')."); Sprintf(sbuf, "Use '%s' to move the cursor on yourself.", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_SELF])); + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_SELF])); putstr(tmpwin, 0, sbuf); if (!iflags.terrainmode || (iflags.terrainmode & TER_MON) != 0) { getpos_help_keyxhelp(tmpwin, @@ -417,10 +418,26 @@ gloc_filter_done(void) } } +staticfn boolean +known_vibrating_square_at(coordxy x, coordxy y) +{ + /* note: this only acknowledges the genuine vibrating square, not + fake ones produced by wizard mode wishing for traps which could + possibly be transfered to normal play via bones file */ + if (invocation_pos(x, y)) { + struct trap *ttmp = t_at(x, y); + + return ttmp && ttmp->ttyp == VIBRATING_SQUARE && ttmp->tseen; + } + return FALSE; +} + DISABLE_WARNING_UNREACHABLE_CODE boolean -gather_locs_interesting(coordxy x, coordxy y, int gloc) +gather_locs_interesting( + coordxy x, coordxy y, + int gloc) { int glyph, sym; @@ -439,7 +456,7 @@ gather_locs_interesting(coordxy x, coordxy y, int gloc) /* unlike '/M', this skips monsters revealed by warning glyphs and remembered unseen ones */ return (glyph_is_monster(glyph) - && glyph != monnum_to_glyph(PM_LONG_WORM_TAIL,MALE) + && glyph != monnum_to_glyph(PM_LONG_WORM_TAIL, MALE) && glyph != monnum_to_glyph(PM_LONG_WORM_TAIL, FEMALE)); case GLOC_OBJS: return (glyph_is_object(glyph) @@ -482,7 +499,8 @@ gather_locs_interesting(coordxy x, coordxy y, int gloc) || is_cmap_room(sym) || is_cmap_corr(sym))) || glyph_is_nothing(glyph) - || glyph_is_unexplored(glyph))); + || glyph_is_unexplored(glyph)) + || known_vibrating_square_at(x, y)); } /*NOTREACHED*/ return FALSE; @@ -543,7 +561,7 @@ dxdy_to_dist_descr(coordxy dx, coordxy dy, boolean fulldir) if (!dx && !dy) { Sprintf(buf, "here"); - } else if ((dst = xytod(dx, dy)) != -1) { + } else if ((dst = xytodir(dx, dy)) != -1) { /* explicit direction; 'one step' is implicit */ Sprintf(buf, "%s", directionname(dst)); } else { @@ -1069,6 +1087,11 @@ getpos(coord *ccp, boolean force, const char *goal) && matching[glyph_to_cmap(k)]) goto foundc; } + /* FIXME: check player-specified vib.sq trap + symbol rather than or in addition to '~' */ + if (c == '~' + && known_vibrating_square_at(tx, ty)) + goto foundc; /* last, try actual terrain here (shouldn't we be using svl.lastseentyp[][] instead?) */ if (levl[tx][ty].seenv) { diff --git a/src/glyphs.c b/src/glyphs.c index 8b6a77854..778cb4aeb 100644 --- a/src/glyphs.c +++ b/src/glyphs.c @@ -251,7 +251,7 @@ glyph_find_core( break; case find_pm: if (glyph_is_monster(glyph) - && monsym(&mons[glyph_to_mon(glyph)]) + && mons[glyph_to_mon(glyph)].mlet == findwhat->val) do_callback = TRUE; break; @@ -299,7 +299,8 @@ glyph_find_core( */ -void fill_glyphid_cache(void) +void +fill_glyphid_cache(void) { int reslt = 0; @@ -350,7 +351,8 @@ init_glyph_cache(void) } } -void free_glyphid_cache(void) +void +free_glyphid_cache(void) { size_t idx; @@ -568,15 +570,22 @@ apply_customizations( } } } - if (at_least_one) { - shuffle_customizations(); - } + iflags.pending_customizations = at_least_one; } /* Shuffle the customizations to match shuffled object descriptions, * so a red potion isn't displayed with a blue customization, and so on. */ +void +maybe_shuffle_customizations(void) +{ + if (iflags.pending_customizations) { + shuffle_customizations(); + iflags.pending_customizations = 0; + } +} + #if 0 staticfn void shuffle_customizations(void) @@ -783,6 +792,7 @@ purge_custom_entries(enum graphics_sets which_set) gdc->count = 0; } } + void dump_all_glyphids(FILE *fp) { @@ -808,7 +818,7 @@ wizcustom_glyphids(winid win) wizcustom_callback(win, glyphnum, id); } } - } +} staticfn int parse_id( @@ -819,7 +829,7 @@ parse_id( int i = 0, j, mnum, glyph, pm_offset = 0, oc_offset = 0, cmap_offset = 0, pm_count = 0, oc_count = 0, cmap_count = 0; - boolean skip_base = FALSE, skip_this_one, dump_ids = FALSE, + boolean skip_base = FALSE, skip_this_one = FALSE, dump_ids = FALSE, filling_cache = FALSE, is_S = FALSE, is_G = FALSE; char buf[4][QBUFSZ]; @@ -1165,7 +1175,8 @@ clear_all_glyphmap_colors(void) } } -void reset_customcolors(void) +void +reset_customcolors(void) { clear_all_glyphmap_colors(); apply_customizations(gc.currentgraphics, do_custom_colors); diff --git a/src/hack.c b/src/hack.c index 7c30870c7..ebdc126c2 100644 --- a/src/hack.c +++ b/src/hack.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 hack.c $NHDT-Date: 1736530208 2025/01/10 09:30:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.477 $ */ +/* NetHack 3.7 hack.c $NHDT-Date: 1763708572 2025/11/20 23:02:52 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.494 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -104,7 +104,7 @@ obj_to_any(struct obj *obj) boolean revive_nasty(coordxy x, coordxy y, const char *msg) { - struct obj *otmp, *otmp2; + struct obj *otmp = 0, *otmp2 = 0; struct monst *mtmp; coord cc; boolean revived = FALSE; @@ -947,7 +947,7 @@ cant_squeeze_thru(struct monst *mon) /* lugging too much junk? */ amt = (mon == &gy.youmonst) ? inv_weight() + weight_cap() : curr_mon_load(mon); - if (amt > 600) + if (amt > WT_TOOMUCH_DIAGONAL) return 2; /* Sokoban restriction applies to hero only */ @@ -1046,7 +1046,7 @@ test_move( else Sprintf(buf, "impossible [background glyph=%d]", glyph); - pline_dir(xytod(dx, dy), "It's %s.", buf); + pline_dir(xytodir(dx, dy), "It's %s.", buf); } } return FALSE; @@ -1197,7 +1197,7 @@ test_move( if (mode != TEST_TRAV && svc.context.run >= 2 && !(Blind || Hallucination) && !could_move_onto_boulder(x, y)) { if (mode == DO_MOVE && flags.mention_walls) - pline_dir(xytod(dx,dy), "A boulder blocks your path."); + pline_dir(xytodir(dx,dy), "A boulder blocks your path."); return FALSE; } if (mode == DO_MOVE) { @@ -1834,8 +1834,8 @@ handle_tip(int tip) if (!flags.tips) return FALSE; - if (tip >= 0 && tip < NUM_TIPS && !svc.context.tips[tip]) { - svc.context.tips[tip] = TRUE; + if (tip >= 0 && tip < NUM_TIPS && !(svc.context.tips & (1 << tip))) { + svc.context.tips |= (1 << tip); /* the "Tip:" prefix is a hint to use of OPTIONS=!tips to suppress */ switch (tip) { case TIP_ENHANCE: @@ -1886,7 +1886,7 @@ swim_move_danger(coordxy x, coordxy y) || liquid_wall) { if (svc.context.nopick) { /* moving with m-prefix */ - svc.context.tips[TIP_SWIM] = TRUE; + svc.context.tips |= (1 << TIP_SWIM); return FALSE; } else if (ParanoidSwim || liquid_wall) { You("avoid %s into the %s.", @@ -2425,7 +2425,10 @@ avoid_moving_on_trap(coordxy x, coordxy y, boolean msg) { struct trap *trap; - if ((trap = t_at(x, y)) && trap->tseen) { + if ((trap = t_at(x, y)) && trap->tseen + /* the vibrating square is implemented as a trap but treated as if + it were a type of terrain */ + && trap->ttyp != VIBRATING_SQUARE) { if (msg && flags.mention_walls) { set_msg_xy(x, y); You("stop in front of %s.", @@ -2579,7 +2582,7 @@ move_out_of_bounds(coordxy x, coordxy y) dy = 0; } You("have already gone as far %s as possible.", - directionname(xytod(dx, dy))); + directionname(xytodir(dx, dy))); } nomul(0); svc.context.move = 0; @@ -3379,7 +3382,7 @@ char * in_rooms(coordxy x, coordxy y, int typewanted) { static char buf[5]; - char rno, *ptr = &buf[4]; + char rno = 0, *ptr = &buf[4]; int typefound, min_x, min_y, max_x, max_y_offset, step; struct rm *lev; @@ -4123,7 +4126,25 @@ saving_grace(int dmg) return 0; } - if (!u.usaving_grace && dmg >= u.uhp && (u.uhp * 100 / u.uhpmax) > 90) { + if (!svc.context.mon_moving) { + /* saving grace doesn't protect you from your own actions */ + return dmg; + } + + if (dmg < u.uhp || u.uhp <= 0) { + /* no need for saving grace */ + return dmg; + } + + if (gs.saving_grace_turn) { + /* saving grace already triggered and prevents HP reducing below 1 + this turn (specifically: until the next player action or turn + boundary), don't print further messages or livelog entries */ + return u.uhp - 1; + } + + if (!u.usaving_grace && + (gu.uhp_at_start_of_monster_turn * 100 / u.uhpmax) >= 90) { /* saving_grace doesn't have it's own livelog classification; we might invent one, or perhaps use LL_LIFESAVE, but surviving certain death (or preserving worn amulet of life saving) via @@ -4132,11 +4153,14 @@ saving_grace(int dmg) from #chronicle during play but show it to livelog observers */ livelog_printf(LL_CONDUCT | LL_SPOILER, "%s (%d damage, %d/%d HP)", "survived one-shot death via saving-grace", - dmg, u.uhp, u.uhpmax); + /* include damage that happened earlier this turn */ + gu.uhp_at_start_of_monster_turn - u.uhp + dmg, + gu.uhp_at_start_of_monster_turn, u.uhpmax); /* note: this could reduce dmg to 0 if u.uhpmax==1 */ dmg = u.uhp - 1; u.usaving_grace = 1; /* used up */ + gs.saving_grace_turn = TRUE; end_running(TRUE); if (u.usleep) unmul("Suddenly you wake up!"); @@ -4327,9 +4351,10 @@ dump_weights(void) { int i, cnt = 0, nmwidth = 49, mcount = NUMMONS, ocount = NUM_OBJECTS; char nmbuf[BUFSZ], nmbufbase[BUFSZ]; + size_t num_entries = (size_t) (mcount + ocount); weightlist = (struct weight_table_entry *) - alloc(sizeof (struct weight_table_entry) * (mcount + ocount)); + alloc(sizeof (struct weight_table_entry) * num_entries); decl_globals_init(); init_objects(); for (i = 0; i < mcount; ++i) { diff --git a/src/hacklib.c b/src/hacklib.c index 209982751..25891684e 100644 --- a/src/hacklib.c +++ b/src/hacklib.c @@ -711,7 +711,7 @@ online2(coordxy x0, coordxy y0, coordxy x1, coordxy y1) } #ifndef STRNCMPI -/* case insensitive counted string comparison */ +/* case-insensitive counted string comparison */ /*{ aka strncasecmp }*/ int strncmpi( @@ -735,7 +735,7 @@ strncmpi( #endif /* STRNCMPI */ #ifndef STRSTRI -/* case insensitive substring search */ +/* case-insensitive substring search */ char * strstri(const char *str, const char *sub) { @@ -936,11 +936,17 @@ case_insensitive_comp(const char *s1, const char *s2) return u1 - u2; } +#if defined(MACOS) +#define RETTYPE ssize_t +#else +#define RETTYPE int +#endif + boolean copy_bytes(int ifd, int ofd) { char buf[BUFSIZ]; - int nfrom, nto; + RETTYPE nfrom, nto; do { nto = 0; @@ -950,9 +956,11 @@ copy_bytes(int ifd, int ofd) nto = write(ofd, buf, nfrom); if (nto != nfrom || nfrom < 0) return FALSE; - } while (nfrom == BUFSIZ); + } while (nfrom == (RETTYPE) BUFSIZ); return TRUE; } +#undef RETTYPE + #define MAX_D 5 struct datamodel_information { int sz[MAX_D]; diff --git a/src/iactions.c b/src/iactions.c new file mode 100644 index 000000000..ca00a2be6 --- /dev/null +++ b/src/iactions.c @@ -0,0 +1,716 @@ +/* NetHack 3.7 iactions.c $NHDT-Date: 1762680996 2025/11/09 01:36:36 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.543 $ */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/*-Copyright (c) Pasi Kallinen, 2026. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +staticfn boolean item_naming_classification(struct obj *, char *, char *); +staticfn int item_reading_classification(struct obj *, char *); +staticfn void ia_addmenu(winid, int, char, const char *); +staticfn void itemactions_pushkeys(struct obj *, int); + +enum item_action_actions { + IA_NONE = 0, + IA_UNWIELD, /* hack for 'w-' */ + IA_APPLY_OBJ, /* 'a' */ + IA_DIP_OBJ, /* 'a' on a potion == dip */ + IA_NAME_OBJ, /* 'c' name individual item */ + IA_NAME_OTYP, /* 'C' name item's type */ + IA_DROP_OBJ, /* 'd' */ + IA_EAT_OBJ, /* 'e' */ + IA_ENGRAVE_OBJ, /* 'E' */ + IA_FIRE_OBJ, /* 'f' */ + IA_ADJUST_OBJ, /* 'i' #adjust inventory letter */ + IA_ADJUST_STACK, /* 'I' #adjust with count to split stack */ + IA_SACRIFICE, /* 'O' offer sacrifice */ + IA_BUY_OBJ, /* 'p' pay shopkeeper */ + IA_QUAFF_OBJ, + IA_QUIVER_OBJ, + IA_READ_OBJ, + IA_RUB_OBJ, + IA_THROW_OBJ, + IA_TAKEOFF_OBJ, + IA_TIP_CONTAINER, + IA_INVOKE_OBJ, + IA_WIELD_OBJ, + IA_WEAR_OBJ, + IA_SWAPWEAPON, + IA_TWOWEAPON, + IA_ZAP_OBJ, + IA_WHATIS_OBJ, /* '/' specify inventory object */ +}; + +/* construct text for the menu entries for IA_NAME_OBJ and IA_NAME_OTYP */ +staticfn boolean +item_naming_classification( + struct obj *obj, + char *onamebuf, + char *ocallbuf) +{ + static const char + Name[] = "Name", + Rename[] = "Rename or un-name", + Call[] = "Call", + /* "re-call" seems a bit weird, but "recall" and + "rename" don't fit for changing a type name */ + Recall[] = "Re-call or un-call"; + + onamebuf[0] = ocallbuf[0] = '\0'; + if (name_ok(obj) == GETOBJ_SUGGEST) { + Sprintf(onamebuf, "%s %s %s", + (!has_oname(obj) || !*ONAME(obj)) ? Name : Rename, + the_unique_obj(obj) ? "the" + : !is_plural(obj) ? "this specific" + : "this stack of", /*"these",*/ + simpleonames(obj)); + } + if (call_ok(obj) == GETOBJ_SUGGEST) { + char *callname = simpleonames(obj); + + /* prefix known unique item with "the", make all other types plural */ + if (the_unique_obj(obj)) /* treats unID'd fake amulets as if real */ + callname = the(callname); + else if (!is_plural(obj)) + callname = makeplural(callname); + Sprintf(ocallbuf, "%s the type for %s", + (!objects[obj->otyp].oc_uname + || !*objects[obj->otyp].oc_uname) ? Call : Recall, + callname); + } + return (*onamebuf || *ocallbuf) ? TRUE : FALSE; +} + +/* construct text for the menu entries for IA_READ_OBJ */ +staticfn int +item_reading_classification(struct obj *obj, char *outbuf) +{ + int otyp = obj->otyp, res = IA_READ_OBJ; + + *outbuf = '\0'; + if (otyp == FORTUNE_COOKIE) { + Strcpy(outbuf, "Read the message inside this cookie"); + } else if (otyp == T_SHIRT) { + Strcpy(outbuf, "Read the slogan on the shirt"); + } else if (otyp == ALCHEMY_SMOCK) { + Strcpy(outbuf, "Read the slogan on the apron"); + } else if (otyp == HAWAIIAN_SHIRT) { + Strcpy(outbuf, "Look at the pattern on the shirt"); + } else if (obj->oclass == SCROLL_CLASS) { + const char *magic = ((obj->dknown +#ifdef MAIL_STRUCTURES + && otyp != SCR_MAIL +#endif + && (otyp != SCR_BLANK_PAPER + || !objects[otyp].oc_name_known)) + ? " to activate its magic" : ""); + + Sprintf(outbuf, "Read this scroll%s", magic); + } else if (obj->oclass == SPBOOK_CLASS) { + boolean novel = (otyp == SPE_NOVEL), + blank = (otyp == SPE_BLANK_PAPER + && objects[otyp].oc_name_known), + tome = (otyp == SPE_BOOK_OF_THE_DEAD + && objects[otyp].oc_name_known); + + Sprintf(outbuf, "%s this %s", + (novel || blank) ? "Read" : tome ? "Examine" : "Study", + novel ? simpleonames(obj) /* "novel" or "paperback book" */ + : tome ? "tome" : "spellbook"); + } else { + res = IA_NONE; + } + return res; +} + +staticfn void +ia_addmenu(winid win, int act, char let, const char *txt) +{ + anything any; + int clr = NO_COLOR; + + any = cg.zeroany; + any.a_int = act; + add_menu(win, &nul_glyphinfo, &any, let, 0, + ATR_NONE, clr, txt, MENU_ITEMFLAGS_NONE); +} + +/* set up a command to execute on a specific item next */ +staticfn void +itemactions_pushkeys(struct obj *otmp, int act) +{ + switch (act) { + default: + impossible("Unknown item action %d", act); + break; + case IA_NONE: + break; + case IA_UNWIELD: + cmdq_add_ec(CQ_CANNED, (otmp == uwep) ? dowield + : (otmp == uswapwep) ? remarm_swapwep + : (otmp == uquiver) ? dowieldquiver + : donull); /* can't happen */ + cmdq_add_key(CQ_CANNED, HANDS_SYM); + break; + case IA_APPLY_OBJ: + cmdq_add_ec(CQ_CANNED, doapply); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_DIP_OBJ: + /* #altdip instead of normal #dip - takes potion to dip into + first (the inventory item instigating this) and item to + be dipped second, also ignores floor features such as + fountain/sink so we don't need to force m-prefix here */ + cmdq_add_ec(CQ_CANNED, dip_into); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_NAME_OBJ: + case IA_NAME_OTYP: + cmdq_add_ec(CQ_CANNED, docallcmd); + cmdq_add_key(CQ_CANNED, (act == IA_NAME_OBJ) ? 'i' : 'o'); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_DROP_OBJ: + cmdq_add_ec(CQ_CANNED, dodrop); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_EAT_OBJ: + /* start with m-prefix; for #eat, it means ignore floor food + if present and eat food from invent */ + cmdq_add_ec(CQ_CANNED, do_reqmenu); + cmdq_add_ec(CQ_CANNED, doeat); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_ENGRAVE_OBJ: + cmdq_add_ec(CQ_CANNED, doengrave); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_FIRE_OBJ: + cmdq_add_ec(CQ_CANNED, dofire); + break; + case IA_ADJUST_OBJ: + cmdq_add_ec(CQ_CANNED, doorganize); /* #adjust */ + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_ADJUST_STACK: + cmdq_add_ec(CQ_CANNED, adjust_split); /* #altadjust */ + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_SACRIFICE: + cmdq_add_ec(CQ_CANNED, dosacrifice); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_BUY_OBJ: + cmdq_add_ec(CQ_CANNED, dopay); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_QUAFF_OBJ: + /* start with m-prefix; for #quaff, it means ignore fountain + or sink if present and drink a potion from invent */ + cmdq_add_ec(CQ_CANNED, do_reqmenu); + cmdq_add_ec(CQ_CANNED, dodrink); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_QUIVER_OBJ: + cmdq_add_ec(CQ_CANNED, dowieldquiver); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_READ_OBJ: + cmdq_add_ec(CQ_CANNED, doread); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_RUB_OBJ: + cmdq_add_ec(CQ_CANNED, dorub); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_THROW_OBJ: + cmdq_add_ec(CQ_CANNED, dothrow); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_TAKEOFF_OBJ: + cmdq_add_ec(CQ_CANNED, ia_dotakeoff); /* #altdotakeoff */ + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_TIP_CONTAINER: + /* start with m-prefix to skip floor containers; + for menustyle:Traditional when more than one floor container + is present, player will get a #tip menu and have to pick + the "tip something being carried" choice, then this item + will be already chosen from inventory; suboptimal but + possibly an acceptable tradeoff since combining item actions + with use of traditional ggetobj() is an unlikely scenario */ + cmdq_add_ec(CQ_CANNED, do_reqmenu); + cmdq_add_ec(CQ_CANNED, dotip); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_INVOKE_OBJ: + cmdq_add_ec(CQ_CANNED, doinvoke); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_WIELD_OBJ: + cmdq_add_ec(CQ_CANNED, dowield); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_WEAR_OBJ: + cmdq_add_ec(CQ_CANNED, dowear); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_SWAPWEAPON: + cmdq_add_ec(CQ_CANNED, doswapweapon); + break; + case IA_TWOWEAPON: + cmdq_add_ec(CQ_CANNED, dotwoweapon); + break; + case IA_ZAP_OBJ: + cmdq_add_ec(CQ_CANNED, dozap); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_WHATIS_OBJ: + cmdq_add_ec(CQ_CANNED, dowhatis); /* "/" command */ + cmdq_add_key(CQ_CANNED, 'i'); /* "i" == item from inventory */ + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + } +} + +/* Show menu of possible actions hero could do with item otmp */ +int +itemactions(struct obj *otmp) +{ + int n, act = IA_NONE; + winid win; + char buf[BUFSZ], buf2[BUFSZ]; + menu_item *selected; + struct monst *mtmp; + const char *light = otmp->lamplit ? "Extinguish" : "Light"; + boolean already_worn = (otmp->owornmask & (W_ARMOR | W_ACCESSORY)) != 0; + + win = create_nhwindow(NHW_MENU); + start_menu(win, MENU_BEHAVE_STANDARD); + + /* -: unwield; picking current weapon offers an opportunity for 'w-' + to wield bare/gloved hands; likewise for 'Q-' with quivered item(s) */ + if (otmp == uwep || otmp == uswapwep || otmp == uquiver) { + const char *verb = (otmp == uquiver) ? "Quiver" : "Wield", + *action = (otmp == uquiver) ? "un-ready" : "un-wield", + *which = is_plural(otmp) ? "these" : "this", + *what = ((otmp->oclass == WEAPON_CLASS || is_weptool(otmp)) + ? "weapon" : "item"); + /* + * TODO: if uwep is ammo, tell player that to shoot instead of toss, + * the corresponding launcher must be wielded; + */ + Sprintf(buf, "%s '%c' to %s %s %s", + verb, HANDS_SYM, action, which, + is_plural(otmp) ? makeplural(what) : what); + ia_addmenu(win, IA_UNWIELD, '-', buf); + } + + /* a: apply */ + if (otmp->oclass == COIN_CLASS) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Flip a coin"); + else if (otmp->otyp == CREAM_PIE) + ia_addmenu(win, IA_APPLY_OBJ, 'a', + "Hit yourself with this cream pie"); + else if (otmp->otyp == BULLWHIP) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Lash out with this whip"); + else if (otmp->otyp == GRAPPLING_HOOK) + ia_addmenu(win, IA_APPLY_OBJ, 'a', + "Grapple something with this hook"); + else if (otmp->otyp == BAG_OF_TRICKS && objects[otmp->otyp].oc_name_known) + /* bag of tricks skips this unless discovered */ + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Reach into this bag"); + else if (Is_container(otmp)) + /* bag of tricks gets here only if not yet discovered */ + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Open this container"); + else if (otmp->otyp == CAN_OF_GREASE) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Use the can to grease an item"); + else if (otmp->otyp == LOCK_PICK + || otmp->otyp == CREDIT_CARD + || otmp->otyp == SKELETON_KEY) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Use this tool to pick a lock"); + else if (otmp->otyp == TINNING_KIT) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Use this kit to tin a corpse"); + else if (otmp->otyp == LEASH) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Tie a pet to this leash"); + else if (otmp->otyp == SADDLE) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Place this saddle on a pet"); + else if (otmp->otyp == MAGIC_WHISTLE + || otmp->otyp == TIN_WHISTLE) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Blow this whistle"); + else if (otmp->otyp == EUCALYPTUS_LEAF) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Use this leaf as a whistle"); + else if (otmp->otyp == STETHOSCOPE) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Listen through the stethoscope"); + else if (otmp->otyp == MIRROR) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Show something its reflection"); + else if (otmp->otyp == BELL || otmp->otyp == BELL_OF_OPENING) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Ring the bell"); + else if (otmp->otyp == CANDELABRUM_OF_INVOCATION) { + Sprintf(buf, "%s the candelabrum", light); + ia_addmenu(win, IA_APPLY_OBJ, 'a', buf); + } else if (otmp->otyp == WAX_CANDLE || otmp->otyp == TALLOW_CANDLE) { + boolean multiple = (otmp->quan == 1L) ? FALSE : TRUE; + const char *s = multiple ? "these" : "this"; + struct obj *o = carrying(CANDELABRUM_OF_INVOCATION); + + if (o && o->spe < 7) + Sprintf(buf, "Attach %s to your candelabrum, or %s %s", s, + !otmp->lamplit ? "light" : "extinguish", /* [lowercase] */ + multiple ? "them" : "it"); + else + Sprintf(buf, "%s %s %s", light, s, simpleonames(otmp)); + ia_addmenu(win, IA_APPLY_OBJ, 'a', buf); + } else if (otmp->otyp == OIL_LAMP || otmp->otyp == MAGIC_LAMP + || otmp->otyp == BRASS_LANTERN) { + Sprintf(buf, "%s this light source", light); + ia_addmenu(win, IA_APPLY_OBJ, 'a', buf); + } else if (otmp->otyp == POT_OIL && objects[otmp->otyp].oc_name_known) { + Sprintf(buf, "%s this oil", light); + ia_addmenu(win, IA_APPLY_OBJ, 'a', buf); + } else if (otmp->oclass == POTION_CLASS) { + /* FIXME? this should probably be moved to 'D' rather than be 'a' */ + Sprintf(buf, "Dip something into %s potion%s", + is_plural(otmp) ? "one of these" : "this", plur(otmp->quan)); + ia_addmenu(win, IA_DIP_OBJ, 'a', buf); + } else if (otmp->otyp == EXPENSIVE_CAMERA) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Take a photograph"); + else if (otmp->otyp == TOWEL) + ia_addmenu(win, IA_APPLY_OBJ, 'a', + "Clean yourself off with this towel"); + else if (otmp->otyp == CRYSTAL_BALL) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Peer into this crystal ball"); + else if (otmp->otyp == MAGIC_MARKER) + ia_addmenu(win, IA_APPLY_OBJ, 'a', + "Write on something with this marker"); + else if (otmp->otyp == FIGURINE) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Make this figurine transform"); + else if (otmp->otyp == UNICORN_HORN) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Use this unicorn horn"); + else if (otmp->otyp == HORN_OF_PLENTY + && objects[otmp->otyp].oc_name_known) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Blow into the horn of plenty"); + else if (otmp->otyp >= WOODEN_FLUTE && otmp->otyp <= DRUM_OF_EARTHQUAKE) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Play this musical instrument"); + else if (otmp->otyp == LAND_MINE || otmp->otyp == BEARTRAP) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Arm this trap"); + else if (otmp->otyp == PICK_AXE || otmp->otyp == DWARVISH_MATTOCK) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Dig with this digging tool"); + else if (otmp->oclass == WAND_CLASS) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Break this wand"); + + /* 'c', 'C' - call an item or its type something */ + if (item_naming_classification(otmp, buf, buf2)) { + if (*buf) + ia_addmenu(win, IA_NAME_OBJ, 'c', buf); + if (*buf2) + ia_addmenu(win, IA_NAME_OTYP, 'C', buf2); + } + + /* d: drop item, works on everything except worn items; those will + always have a takeoff/remove choice so we don't have to worry + about the menu maybe being empty when 'd' is suppressed */ + if (!already_worn) { + Sprintf(buf, "Drop this %s", (otmp->quan > 1L) ? "stack" : "item"); + ia_addmenu(win, IA_DROP_OBJ, 'd', buf); + } + + /* e: eat item */ + if (otmp->otyp == TIN) { + Sprintf(buf, "Open %s%s and eat the contents", + (otmp->quan > 1L) ? "one of these tins" : "this tin", + (otmp->otyp == TIN && uwep && uwep->otyp == TIN_OPENER) + ? " with your tin opener" : ""); + ia_addmenu(win, IA_EAT_OBJ, 'e', buf); + } else if (is_edible(otmp)) { + Sprintf(buf, "Eat %s", (otmp->quan > 1L) ? "one of these" : "this"); + ia_addmenu(win, IA_EAT_OBJ, 'e', buf); + } + + /* E: engrave with item */ + if (otmp->otyp == TOWEL) { + ia_addmenu(win, IA_ENGRAVE_OBJ, 'E', + "Wipe the floor with this towel"); + } else if (otmp->otyp == MAGIC_MARKER) { + ia_addmenu(win, IA_ENGRAVE_OBJ, 'E', + "Scribble graffiti on the floor"); + } else if (otmp->oclass == WEAPON_CLASS || otmp->oclass == WAND_CLASS + || otmp->oclass == GEM_CLASS || otmp->oclass == RING_CLASS) { + Sprintf(buf, "%s on the %s with %s", + (is_blade(otmp) || otmp->oclass == WAND_CLASS + || ((otmp->oclass == GEM_CLASS || otmp->oclass == RING_CLASS) + && objects[otmp->otyp].oc_tough)) ? "Engrave" : "Write", + surface(u.ux, u.uy), + (otmp->quan > 1L) ? "one of these items" : "this item"); + ia_addmenu(win, IA_ENGRAVE_OBJ, 'E', buf); + } + + /* f: fire quivered ammo */ + if (otmp == uquiver) { + boolean shoot = ammo_and_launcher(otmp, uwep); + + /* FIXME: see the multi-shot FIXME about "one of" for 't: throw' */ + Sprintf(buf, "%s %s", shoot ? "Shoot" : "Throw", + (otmp->quan > 1L) ? "one of these" : "this"); + if (shoot) { + assert(uwep != NULL); + Sprintf(eos(buf), " with your wielded %s", simpleonames(uwep)); + } + ia_addmenu(win, IA_FIRE_OBJ, 'f', buf); + } + + /* i: #adjust inventory letter; gold can't be adjusted unless there + is some in a slot other than '$' (which shouldn't be possible) */ + if (otmp->oclass != COIN_CLASS || check_invent_gold("item-action")) + ia_addmenu(win, IA_ADJUST_OBJ, 'i', + "Adjust inventory by assigning new letter"); + /* I: #adjust inventory item by splitting its stack */ + if (otmp->quan > 1L && otmp->oclass != COIN_CLASS) + ia_addmenu(win, IA_ADJUST_STACK, 'I', + "Adjust inventory by splitting this stack"); + + /* O: offer sacrifice */ + if (IS_ALTAR(levl[u.ux][u.uy].typ) && !u.uswallow) { + /* FIXME: this doesn't match #offer's likely candidates, which don't + include corpses on Astral and don't include amulets off Astral */ + if (otmp->otyp == CORPSE) + ia_addmenu(win, IA_SACRIFICE, 'O', + "Offer this corpse as a sacrifice at this altar"); + else if (otmp->otyp == AMULET_OF_YENDOR + || otmp->otyp == FAKE_AMULET_OF_YENDOR) + ia_addmenu(win, IA_SACRIFICE, 'O', + "Offer this amulet as a sacrifice at this altar"); + } + + /* p: pay for unpaid utems */ + if (otmp->unpaid + /* FIXME: should also handle player owned container (so not + flagged 'unpaid') holding shop owned items */ + && (mtmp = shop_keeper(*in_rooms(u.ux, u.uy, SHOPBASE))) != 0 + && inhishop(mtmp)) { + Sprintf(buf, "Buy this unpaid %s", + (otmp->quan > 1L) ? "stack" : "item"); + ia_addmenu(win, IA_BUY_OBJ, 'p', buf); + } + + /* P: put on accessory */ + if (!already_worn) { + /* if 'otmp' is worn, we'll skip 'P' and show 'R' below; + if not worn, we show 'P - Put on this ' if + the slot is available, or 'P - '; for the latter, + 'P' will fail but we don't want to omit the choice because + item actions can be used to learn commands */ + *buf = '\0'; + if (otmp->oclass == AMULET_CLASS) { + Strcpy(buf, !uamul ? "Put this amulet on" + : "[already wearing an amulet]"); + } else if (otmp->oclass == RING_CLASS || otmp->otyp == MEAT_RING) { + if (!uleft || !uright) + Strcpy(buf, "Put this ring on"); + else + Sprintf(buf, "[both ring %s in use]", + makeplural(body_part(FINGER))); + } else if (otmp->otyp == BLINDFOLD || otmp->otyp == TOWEL + || otmp->otyp == LENSES) { + if (ublindf) + Strcpy(buf, "[already wearing eyewear]"); + else if (otmp->otyp == LENSES) + Strcpy(buf, "Put these lenses on"); + else + Sprintf(buf, "Put this on%s", + (otmp->otyp == TOWEL) ? " to blindfold yourself" : ""); + } + if (*buf) + ia_addmenu(win, IA_WEAR_OBJ, 'P', buf); + } + + /* q: drink item */ + if (otmp->oclass == POTION_CLASS) { + Sprintf(buf, "Quaff (drink) %s", + (otmp->quan > 1L) ? "one of these potions" : "this potion"); + ia_addmenu(win, IA_QUAFF_OBJ, 'q', buf); + } + + /* Q: quiver throwable item */ + if ((otmp->oclass == GEM_CLASS || otmp->oclass == WEAPON_CLASS) + && otmp != uquiver) { + Sprintf(buf, "Quiver this %s for easy %s with \'f\'ire", + (otmp->quan > 1L) ? "stack" : "item", + ammo_and_launcher(otmp, uwep) ? "shooting" : "throwing"); + ia_addmenu(win, IA_QUIVER_OBJ, 'Q', buf); + } + + /* r: read item */ + if (item_reading_classification(otmp, buf) == IA_READ_OBJ) + ia_addmenu(win, IA_READ_OBJ, 'r', buf); + + /* R: remove accessory or rub item */ + if (otmp->owornmask & W_ACCESSORY) { + Sprintf(buf, "Remove this %s", + (otmp->owornmask & W_AMUL) ? "amulet" + : (otmp->owornmask & W_RING) ? "ring" + : (otmp->owornmask & W_TOOL) ? "eyewear" + : "accessory"); /* catchall -- can't happen */ + ia_addmenu(win, IA_TAKEOFF_OBJ, 'R', buf); + } + if (otmp->otyp == OIL_LAMP || otmp->otyp == MAGIC_LAMP + || otmp->otyp == BRASS_LANTERN) { + Sprintf(buf, "Rub this %s", simpleonames(otmp)); + ia_addmenu(win, IA_RUB_OBJ, 'R', buf); + } else if (otmp->oclass == GEM_CLASS && is_graystone(otmp)) + ia_addmenu(win, IA_RUB_OBJ, 'R', "Rub something on this stone"); + + /* t: throw item */ + if (!already_worn) { + boolean shoot = ammo_and_launcher(otmp, uwep); + + /* + * FIXME: + * 'one of these' should be changed to 'some of these' when there + * is the possibility of a multi-shot volley but we don't have + * any way to determine that except by actually calculating the + * volley count and that could randomly yield 1 here and 2..N + * while throwing or vice versa. + */ + Sprintf(buf, "%s %s%s", shoot ? "Shoot" : "Throw", + (otmp->quan == 1L) ? "this item" + : (otmp->otyp == GOLD_PIECE) ? "them" + : "one of these", + /* if otmp is quivered, we've already listed + 'f - shoot|throw this item' as a choice; + if 't' is duplicating that, say so ('t' and 'f' + behavior differs for throwing a stack of gold) */ + (otmp == uquiver && (otmp->otyp != GOLD_PIECE + || otmp->quan == 1L)) + ? " (same as 'f')" : ""); + ia_addmenu(win, IA_THROW_OBJ, 't', buf); + } + + /* T: take off armor, tip carried container */ + if (otmp->owornmask & W_ARMOR) + ia_addmenu(win, IA_TAKEOFF_OBJ, 'T', "Take off this armor"); + if ((Is_container(otmp) && (Has_contents(otmp) || !otmp->cknown)) + || (otmp->otyp == HORN_OF_PLENTY && (otmp->spe > 0 || !otmp->known))) + ia_addmenu(win, IA_TIP_CONTAINER, 'T', + "Tip all the contents out of this container"); + + /* V: invoke */ + if ((otmp->otyp == FAKE_AMULET_OF_YENDOR && !otmp->known) + || otmp->oartifact || objects[otmp->otyp].oc_unique + /* non-artifact crystal balls don't have any unique power but + the #invoke command lists them as likely candidates */ + || otmp->otyp == CRYSTAL_BALL) + ia_addmenu(win, IA_INVOKE_OBJ, 'V', + "Try to invoke a unique power of this object"); + + /* w: wield, hold in hands, works on everything but with different + advice text; not mentioned for things that are already wielded */ + if (otmp == uwep || cantwield(gy.youmonst.data)) { + ; /* either already wielded or can't wield anything; skip 'w' */ + } else if (otmp->oclass == WEAPON_CLASS || is_weptool(otmp) + || is_wet_towel(otmp) || otmp->otyp == HEAVY_IRON_BALL) { + Sprintf(buf, "Wield this %s as your weapon", + (otmp->quan > 1L) ? "stack" : "item"); + ia_addmenu(win, IA_WIELD_OBJ, 'w', buf); + } else if (otmp->otyp == TIN_OPENER) { + ia_addmenu(win, IA_WIELD_OBJ, 'w', + "Wield the tin opener to easily open tins"); + } else if (!already_worn) { + /* originally this was using "hold this item in your hands" but + there's no concept of "holding an item", plus it unwields + whatever item you already have wielded so use "wield this item" */ + Sprintf(buf, "Wield this %s in your %s", + (otmp->quan > 1L) ? "stack" : "item", + /* only two-handed weapons and unicorn horns care about + pluralizing "hand" and they won't reach here, but plural + sounds better when poly'd into something with "claw" */ + makeplural(body_part(HAND))); + ia_addmenu(win, IA_WIELD_OBJ, 'w', buf); + } + + /* W: wear armor */ + if (!already_worn) { + if (otmp->oclass == ARMOR_CLASS) { + /* if 'otmp' is worn we skip 'W' (and show 'T' above instead); + if it isn't, we either show "W - wear this" if otmp's slot + isn't populated, or "W - [already wearing ]"; + for the latter, picking 'W' will fail but we don't want to + omit 'W' in this situation */ + long Wmask = armcat_to_wornmask(objects[otmp->otyp].oc_armcat); + struct obj *o = wearmask_to_obj(Wmask); + + if (!o) + Strcpy(buf, "Wear this armor"); + else + Sprintf(buf, "[already wearing %s]", an(armor_simple_name(o))); + + ia_addmenu(win, IA_WEAR_OBJ, 'W', buf); + } + } + + /* x: Swap main and readied weapon */ + if (otmp == uwep && uswapwep) + ia_addmenu(win, IA_SWAPWEAPON, 'x', + "Swap this with your alternate weapon"); + else if (otmp == uwep) + ia_addmenu(win, IA_SWAPWEAPON, 'x', + "Ready this as an alternate weapon"); + else if (otmp == uswapwep) + ia_addmenu(win, IA_SWAPWEAPON, 'x', + "Swap this with your main weapon"); + + /* this is based on TWOWEAPOK() in wield.c; we don't call can_two_weapon() + because it is very verbose; attempting to two-weapon might be rejected + but we screen out most reasons for rejection before offering it as a + choice */ +#define MAYBETWOWEAPON(obj) \ + ((((obj)->oclass == WEAPON_CLASS) \ + ? !(is_launcher(obj) || is_ammo(obj) || is_missile(obj)) \ + : is_weptool(obj)) \ + && !bimanual(obj)) + + /* X: Toggle two-weapon mode on or off */ + if ((otmp == uwep || otmp == uswapwep) + /* if already two-weaponing, no special checks needed to toggle off */ + && (u.twoweap + /* but if not, try to filter most "you can't do that" here */ + || (could_twoweap(gy.youmonst.data) && !uarms + && uwep && MAYBETWOWEAPON(uwep) + && uswapwep && MAYBETWOWEAPON(uswapwep)))) { + Sprintf(buf, "Toggle two-weapon combat %s", u.twoweap ? "off" : "on"); + ia_addmenu(win, IA_TWOWEAPON, 'X', buf); + } + +#undef MAYBETWOWEAPON + + /* z: Zap wand */ + if (otmp->oclass == WAND_CLASS) + ia_addmenu(win, IA_ZAP_OBJ, 'z', + "Zap this wand to release its magic"); + + /* ?: Look up an item in the game's database */ + if (ia_checkfile(otmp)) { + Sprintf(buf, "Look up information about %s", + (otmp->quan > 1L) ? "these" : "this"); + ia_addmenu(win, IA_WHATIS_OBJ, '/', buf); + } + + Sprintf(buf, "Do what with %s?", the(cxname(otmp))); + end_menu(win, buf); + + n = select_menu(win, PICK_ONE, &selected); + + if (n > 0) { + act = selected[0].item.a_int; + free((genericptr_t) selected); + + itemactions_pushkeys(otmp, act); + } + destroy_nhwindow(win); + + /* finish the 'i' command: no time elapses and cancelling without + selecting an action doesn't matter */ + return ECMD_OK; +} + +/*iactions.c*/ diff --git a/src/insight.c b/src/insight.c index 9f0f71059..9f3763619 100644 --- a/src/insight.c +++ b/src/insight.c @@ -2109,6 +2109,12 @@ show_conduct(int final) if (u.uroleplay.pauper) enl_msg(You_, gi.invent ? "started" : "are", "started out", " without possessions", ""); + if (u.uroleplay.reroll) { + Sprintf(buf, "rerolled your character %ld time%s", + u.uroleplay.numrerolls, plur(u.uroleplay.numrerolls)); + you_have_X(buf); + } + /* nudist is far more than a subset of possessionless, and a much more impressive accomplishment, but showing "started out without possessions" before "faithfully nudist" looks more logical */ diff --git a/src/invent.c b/src/invent.c index ef5a78dea..a34517acf 100644 --- a/src/invent.c +++ b/src/invent.c @@ -1,15 +1,10 @@ -/* NetHack 3.7 invent.c $NHDT-Date: 1737384766 2025/01/20 06:52:46 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.531 $ */ +/* NetHack 3.7 invent.c $NHDT-Date: 1762680996 2025/11/09 01:36:36 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.543 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" -/* fake inventory letters, not 'a'..'z' or 'A'..'Z' */ -#define NOINVSYM '#' /* overflow because all 52 letters are in use */ -#define CONTAINED_SYM '>' /* designator for inside a container */ -#define HANDS_SYM '-' /* hands|fingers|self depending on context */ - staticfn void inuse_classify(Loot *, struct obj *); staticfn char *loot_xname(struct obj *); staticfn int invletter_value(char); @@ -43,11 +38,6 @@ staticfn int adjust_ok(struct obj *); staticfn int adjust_gold_ok(struct obj *); staticfn int doorganize_core(struct obj *); staticfn char obj_to_let(struct obj *); -staticfn boolean item_naming_classification(struct obj *, char *, char *); -staticfn int item_reading_classification(struct obj *, char *); -staticfn void ia_addmenu(winid, int, char, const char *); -staticfn void itemactions_pushkeys(struct obj *, int); -staticfn int itemactions(struct obj *); staticfn int dispinv_with_action(char *, boolean, const char *); /* enum and structs are defined in wintype.h */ @@ -169,7 +159,7 @@ loot_classify(Loot *sort_item, struct obj *obj) }; static char armcat[8]; const char *classorder; - char *p; + const char *p; int k, otyp = obj->otyp, oclass = obj->oclass; boolean seen, discovered = objects[otyp].oc_name_known ? TRUE : FALSE; @@ -178,7 +168,7 @@ loot_classify(Loot *sort_item, struct obj *obj) * will put lower valued ones before higher valued ones. */ if (!Blind) - obj->dknown = 1; /* xname(obj) does this; we want it sooner */ + observe_object(obj); /* xname(obj) does this; we want it sooner */ seen = obj->dknown ? TRUE : FALSE, /* class order */ classorder = flags.sortpack ? flags.inv_order : def_srt_order; @@ -965,8 +955,6 @@ merged(struct obj **potmp, struct obj **pobj) * This is called when adding objects to the hero's inventory normally (via * addinv) or when an object in the hero's inventory has been polymorphed * in-place. - * - * It may be valid to merge this code with addinv_core2(). */ void addinv_core1(struct obj *obj) @@ -1017,22 +1005,47 @@ addinv_core1(struct obj *obj) } /* - * Adjust hero intrinsics as if this object was being added to the hero's - * inventory. Called _after_ the object has been added to the hero's - * inventory. + * Adjust hero intrinsics (and perform other side effects) as if this + * object was being added to the hero's inventory. Called _after_ the + * object has been added to the hero's inventory. + * + * This can be used either for updating intrinsics, or to allow the hero to + * react to objects that are now in inventory. * * This is called when adding objects to the hero's inventory normally (via - * addinv) or when an object in the hero's inventory has been polymorphed - * in-place. + * addinv), when an object in the hero's inventory has been polymorphed + * in-place, or when the hero re-examines objects that they picked up while + * blind. + * + * This may occasionally be called on an item that was already in inventory, + * so it should be written to work even if called multiple times in a row + * (e.g. do not assume that the object was not in inventory already). */ void addinv_core2(struct obj *obj) { if (confers_luck(obj)) { /* new luckstone must be in inventory by this point - * for correct calculation */ + for correct calculation */ set_moreluck(); } + + /* Archeologists can decipher the writing on a scroll label to work out + what they are (exception: unlabeled scrolls don't have a label to + decipher) */ + if (Role_if(PM_ARCHEOLOGIST) && obj->oclass == SCROLL_CLASS && + obj->otyp != SCR_BLANK_PAPER && !Blind && + !objects[obj->otyp].oc_name_known) { + observe_object(obj); + pline("You decipher the label on %s.", yname(obj)); + makeknown(obj->otyp); + + /* conduct: this is avoidable via not picking up / wishing for + scrolls */ + if (!u.uconduct.literate++) + livelog_printf(LL_CONDUCT, + "became literate by deciphering a scroll label"); + } } /* @@ -1040,8 +1053,9 @@ addinv_core2(struct obj *obj) * Adjust hero attributes as necessary. */ staticfn struct obj * -addinv_core0(struct obj *obj, struct obj *other_obj, - boolean update_perm_invent) +addinv_core0( + struct obj *obj, struct obj *other_obj, + boolean update_perm_invent) { struct obj *otmp, *prev; int saved_otyp = (int) obj->otyp; /* for panic */ @@ -1049,6 +1063,9 @@ addinv_core0(struct obj *obj, struct obj *other_obj, if (obj->where != OBJ_FREE) panic("addinv: obj not free"); + if (obj->how_lost == LOST_EXPLODING) + return (struct obj *) NULL; + /* normally addtobill() clears no_charge when items in a shop are picked up, but won't do so if the shop has become untended */ obj->no_charge = 0; /* should not be set in hero's invent */ @@ -1140,7 +1157,8 @@ addinv(struct obj *obj) /* add obj to the hero's inventory by inserting in front of a specific item; used for throw-and-return in case '!fixinv' is in effect */ struct obj * -addinv_before(struct obj *obj, struct obj *other_obj) +addinv_before( + struct obj *obj, struct obj *other_obj) { /* if 'other_obj' is present this will implicitly be 'nomerge' */ return addinv_core0(obj, other_obj, TRUE); @@ -1196,7 +1214,7 @@ hold_another_object( char buf[BUFSZ]; if (!Blind) - obj->dknown = 1; /* maximize mergeability */ + observe_object(obj); /* maximize mergeability */ if (obj->oartifact) { /* place_object may change these */ boolean crysknife = (obj->otyp == CRYSKNIFE); @@ -1269,7 +1287,7 @@ hold_another_object( prinv(hold_msg, obj, oquan); /* obj made it into inventory and is staying there */ update_inventory(); - (void) encumber_msg(); + encumber_msg(); } } return obj; @@ -2445,8 +2463,11 @@ askchain( ininv ? safeq_xprname : doname, ininv ? safeq_shortxprname : ansimpleoname, "item"); - sym = (takeoff || ident || otmp->quan < 2L) ? nyaq(qbuf) - : nyNaq(qbuf); + /* nyaq(qbuf) or nyNaq(qbuf), bypassing canned input for ^A */ + sym = yn_function(qbuf, + (takeoff || ident || otmp->quan < 2L) + ? ynaqchars : ynNaqchars, + 'n', FALSE); } else sym = 'y'; @@ -2519,6 +2540,81 @@ askchain( return cnt; } + +/* The menu for rerolling attributes and inventory. + + This is similar to the other inventory menus, but simpler to help it fit on + the screen (there's more text around it and rerolling is difficult if you + can't see the whole list at once). + + Returns TRUE (and increases numrerolls) if a reroll was requested. */ +boolean +reroll_menu(void) +{ + winid win; + anything any; + menu_item *pick_list = NULL; + struct obj *otmp; + int tmpglyph; + glyph_info tmpglyphinfo; + char option; + char buf[BUFSZ]; + + win = create_nhwindow(NHW_MENU); + start_menu(win, MENU_BEHAVE_STANDARD); + any = cg.zeroany; + + any.a_char = 'n'; + add_menu(win, &nul_glyphinfo, &any, flags.lootabc ? 0 : 'p', 0, + ATR_NONE, NO_COLOR, "start the game with this character", + MENU_ITEMFLAGS_NONE); + any.a_char = 'y'; + add_menu(win, &nul_glyphinfo, &any, flags.lootabc ? 0 : 'r', 0, + ATR_NONE, NO_COLOR, "reroll another character", + MENU_ITEMFLAGS_NONE); + any.a_char = 0; + add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, NO_COLOR, "", + MENU_ITEMFLAGS_NONE); + + ++gd.distantname; /* avoid adding items to discoveries */ + ++iflags.override_ID; /* identify them */ + for (otmp = gi.invent; otmp; otmp = otmp->nobj) { + tmpglyph = obj_to_glyph(otmp, rn2_on_display_rng); + map_glyphinfo(0, 0, tmpglyph, 0U, &tmpglyphinfo); + add_menu(win, &tmpglyphinfo, &any, 0, 0, + ATR_NONE, NO_COLOR, doname(otmp), MENU_ITEMFLAGS_NONE); + } + --iflags.override_ID; + --gd.distantname; + + add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, NO_COLOR, "", + MENU_ITEMFLAGS_NONE); + Sprintf(buf, "St:%s Dx:%-1d Co:%-1d In:%-1d Wi:%-1d Ch:%-1d", + get_strength_str(), + ACURR(A_DEX), ACURR(A_CON), ACURR(A_INT), ACURR(A_WIS), + ACURR(A_CHA)); + add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, NO_COLOR, + buf, MENU_ITEMFLAGS_NONE); + + end_menu(win, "Reroll this character?"); + if (select_menu(win, PICK_ONE, &pick_list) > 0) { + option = pick_list[0].item.a_char; + free((genericptr_t) pick_list); + } else { + /* user closed the menu without selecting; unclear what their choice + is here so ask again; but (e.g. for hangup handling) stop asking if + the user cancels out again */ + option = y_n("Reroll this character?"); + } + destroy_nhwindow(win); + + if (option == 'y') { + ++u.uroleplay.numrerolls; + return TRUE; + } + return FALSE; +} + /* * Object identification routines: */ @@ -2543,7 +2639,8 @@ fully_identify_obj(struct obj *otmp) makeknown(otmp->otyp); if (otmp->oartifact) discover_artifact((xint16) otmp->oartifact); - otmp->known = otmp->dknown = otmp->bknown = otmp->rknown = 1; + observe_object(otmp); + otmp->known = otmp->bknown = otmp->rknown = 1; set_cknown_lknown(otmp); /* set otmp->{cknown,lknown} if applicable */ if (otmp->otyp == EGG && otmp->corpsenm != NON_PM) learn_egg_type(otmp->corpsenm); @@ -2659,12 +2756,15 @@ learn_unseen_invent(void) return; /* sanity check */ for (otmp = gi.invent; otmp; otmp = otmp->nobj) { - if (otmp->dknown && (otmp->bknown || !Role_if(PM_CLERIC))) + if (otmp->dknown && (otmp->bknown || !Role_if(PM_CLERIC)) && + (otmp->oclass != SCROLL_CLASS || !Role_if(PM_ARCHEOLOGIST))) continue; /* already seen */ invupdated = TRUE; /* xname() will set dknown, perhaps bknown (for priest[ess]); result from xname() is immediately released for re-use */ maybereleaseobuf(xname(otmp)); + addinv_core2(otmp); /* you react to seeing the object */ + /* * If object->eknown gets implemented (see learnwand(zap.c)), * handle deferred discovery here. @@ -2855,663 +2955,6 @@ xprname( RESTORE_WARNING_FORMAT_NONLITERAL -enum item_action_actions { - IA_NONE = 0, - IA_UNWIELD, /* hack for 'w-' */ - IA_APPLY_OBJ, /* 'a' */ - IA_DIP_OBJ, /* 'a' on a potion == dip */ - IA_NAME_OBJ, /* 'c' name individual item */ - IA_NAME_OTYP, /* 'C' name item's type */ - IA_DROP_OBJ, /* 'd' */ - IA_EAT_OBJ, /* 'e' */ - IA_ENGRAVE_OBJ, /* 'E' */ - IA_FIRE_OBJ, /* 'f' */ - IA_ADJUST_OBJ, /* 'i' #adjust inventory letter */ - IA_ADJUST_STACK, /* 'I' #adjust with count to split stack */ - IA_SACRIFICE, /* 'O' offer sacrifice */ - IA_BUY_OBJ, /* 'p' pay shopkeeper */ - IA_QUAFF_OBJ, - IA_QUIVER_OBJ, - IA_READ_OBJ, - IA_RUB_OBJ, - IA_THROW_OBJ, - IA_TAKEOFF_OBJ, - IA_TIP_CONTAINER, - IA_INVOKE_OBJ, - IA_WIELD_OBJ, - IA_WEAR_OBJ, - IA_SWAPWEAPON, - IA_TWOWEAPON, - IA_ZAP_OBJ, - IA_WHATIS_OBJ, /* '/' specify inventory object */ -}; - -/* construct text for the menu entries for IA_NAME_OBJ and IA_NAME_OTYP */ -staticfn boolean -item_naming_classification( - struct obj *obj, - char *onamebuf, - char *ocallbuf) -{ - static const char - Name[] = "Name", - Rename[] = "Rename or un-name", - Call[] = "Call", - /* "re-call" seems a bit weird, but "recall" and - "rename" don't fit for changing a type name */ - Recall[] = "Re-call or un-call"; - - onamebuf[0] = ocallbuf[0] = '\0'; - if (name_ok(obj) == GETOBJ_SUGGEST) { - Sprintf(onamebuf, "%s %s %s", - (!has_oname(obj) || !*ONAME(obj)) ? Name : Rename, - the_unique_obj(obj) ? "the" - : !is_plural(obj) ? "this specific" - : "this stack of", /*"these",*/ - simpleonames(obj)); - } - if (call_ok(obj) == GETOBJ_SUGGEST) { - char *callname = simpleonames(obj); - - /* prefix known unique item with "the", make all other types plural */ - if (the_unique_obj(obj)) /* treats unID'd fake amulets as if real */ - callname = the(callname); - else if (!is_plural(obj)) - callname = makeplural(callname); - Sprintf(ocallbuf, "%s the type for %s", - (!objects[obj->otyp].oc_uname - || !*objects[obj->otyp].oc_uname) ? Call : Recall, - callname); - } - return (*onamebuf || *ocallbuf) ? TRUE : FALSE; -} - -/* construct text for the menu entries for IA_READ_OBJ */ -staticfn int -item_reading_classification(struct obj *obj, char *outbuf) -{ - int otyp = obj->otyp, res = IA_READ_OBJ; - - *outbuf = '\0'; - if (otyp == FORTUNE_COOKIE) { - Strcpy(outbuf, "Read the message inside this cookie"); - } else if (otyp == T_SHIRT) { - Strcpy(outbuf, "Read the slogan on the shirt"); - } else if (otyp == ALCHEMY_SMOCK) { - Strcpy(outbuf, "Read the slogan on the apron"); - } else if (otyp == HAWAIIAN_SHIRT) { - Strcpy(outbuf, "Look at the pattern on the shirt"); - } else if (obj->oclass == SCROLL_CLASS) { - const char *magic = ((obj->dknown -#ifdef MAIL_STRUCTURES - && otyp != SCR_MAIL -#endif - && (otyp != SCR_BLANK_PAPER - || !objects[otyp].oc_name_known)) - ? " to activate its magic" : ""); - - Sprintf(outbuf, "Read this scroll%s", magic); - } else if (obj->oclass == SPBOOK_CLASS) { - boolean novel = (otyp == SPE_NOVEL), - blank = (otyp == SPE_BLANK_PAPER - && objects[otyp].oc_name_known), - tome = (otyp == SPE_BOOK_OF_THE_DEAD - && objects[otyp].oc_name_known); - - Sprintf(outbuf, "%s this %s", - (novel || blank) ? "Read" : tome ? "Examine" : "Study", - novel ? simpleonames(obj) /* "novel" or "paperback book" */ - : tome ? "tome" : "spellbook"); - } else { - res = IA_NONE; - } - return res; -} - -staticfn void -ia_addmenu(winid win, int act, char let, const char *txt) -{ - anything any; - int clr = NO_COLOR; - - any = cg.zeroany; - any.a_int = act; - add_menu(win, &nul_glyphinfo, &any, let, 0, - ATR_NONE, clr, txt, MENU_ITEMFLAGS_NONE); -} - -staticfn void -itemactions_pushkeys(struct obj *otmp, int act) -{ - switch (act) { - default: - impossible("Unknown item action"); - break; - case IA_NONE: - break; - case IA_UNWIELD: - cmdq_add_ec(CQ_CANNED, (otmp == uwep) ? dowield - : (otmp == uswapwep) ? remarm_swapwep - : (otmp == uquiver) ? dowieldquiver - : donull); /* can't happen */ - cmdq_add_key(CQ_CANNED, '-'); - break; - case IA_APPLY_OBJ: - cmdq_add_ec(CQ_CANNED, doapply); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_DIP_OBJ: - /* #altdip instead of normal #dip - takes potion to dip into - first (the inventory item instigating this) and item to - be dipped second, also ignores floor features such as - fountain/sink so we don't need to force m-prefix here */ - cmdq_add_ec(CQ_CANNED, dip_into); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_NAME_OBJ: - case IA_NAME_OTYP: - cmdq_add_ec(CQ_CANNED, docallcmd); - cmdq_add_key(CQ_CANNED, (act == IA_NAME_OBJ) ? 'i' : 'o'); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_DROP_OBJ: - cmdq_add_ec(CQ_CANNED, dodrop); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_EAT_OBJ: - /* start with m-prefix; for #eat, it means ignore floor food - if present and eat food from invent */ - cmdq_add_ec(CQ_CANNED, do_reqmenu); - cmdq_add_ec(CQ_CANNED, doeat); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_ENGRAVE_OBJ: - cmdq_add_ec(CQ_CANNED, doengrave); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_FIRE_OBJ: - cmdq_add_ec(CQ_CANNED, dofire); - break; - case IA_ADJUST_OBJ: - cmdq_add_ec(CQ_CANNED, doorganize); /* #adjust */ - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_ADJUST_STACK: - cmdq_add_ec(CQ_CANNED, adjust_split); /* #altadjust */ - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_SACRIFICE: - cmdq_add_ec(CQ_CANNED, dosacrifice); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_BUY_OBJ: - cmdq_add_ec(CQ_CANNED, dopay); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_QUAFF_OBJ: - /* start with m-prefix; for #quaff, it means ignore fountain - or sink if present and drink a potion from invent */ - cmdq_add_ec(CQ_CANNED, do_reqmenu); - cmdq_add_ec(CQ_CANNED, dodrink); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_QUIVER_OBJ: - cmdq_add_ec(CQ_CANNED, dowieldquiver); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_READ_OBJ: - cmdq_add_ec(CQ_CANNED, doread); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_RUB_OBJ: - cmdq_add_ec(CQ_CANNED, dorub); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_THROW_OBJ: - cmdq_add_ec(CQ_CANNED, dothrow); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_TAKEOFF_OBJ: - cmdq_add_ec(CQ_CANNED, dotakeoff); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_TIP_CONTAINER: - /* start with m-prefix to skip floor containers; - for menustyle:Traditional when more than one floor - container is present, player will get a #tip menu and - have to pick the "tip something being carried" choice, - then this item will be already chosen from inventory; - suboptimal but possibly an acceptable tradeoff since - combining item actions with use of traditional ggetobj() - is an unlikely scenario */ - cmdq_add_ec(CQ_CANNED, do_reqmenu); - cmdq_add_ec(CQ_CANNED, dotip); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_INVOKE_OBJ: - cmdq_add_ec(CQ_CANNED, doinvoke); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_WIELD_OBJ: - cmdq_add_ec(CQ_CANNED, dowield); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_WEAR_OBJ: - cmdq_add_ec(CQ_CANNED, dowear); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_SWAPWEAPON: - cmdq_add_ec(CQ_CANNED, doswapweapon); - break; - case IA_TWOWEAPON: - cmdq_add_ec(CQ_CANNED, dotwoweapon); - break; - case IA_ZAP_OBJ: - cmdq_add_ec(CQ_CANNED, dozap); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_WHATIS_OBJ: - cmdq_add_ec(CQ_CANNED, dowhatis); /* "/" command */ - cmdq_add_key(CQ_CANNED, 'i'); /* "i" == item from inventory */ - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - } -} - -/* Show menu of possible actions hero could do with item otmp */ -staticfn int -itemactions(struct obj *otmp) -{ - int n, act = IA_NONE; - winid win; - char buf[BUFSZ], buf2[BUFSZ]; - menu_item *selected; - struct monst *mtmp; - const char *light = otmp->lamplit ? "Extinguish" : "Light"; - boolean already_worn = (otmp->owornmask & (W_ARMOR | W_ACCESSORY)) != 0; - - win = create_nhwindow(NHW_MENU); - start_menu(win, MENU_BEHAVE_STANDARD); - - /* -: unwield; picking current weapon offers an opportunity for 'w-' - to wield bare/gloved hands; likewise for 'Q-' with quivered item(s) */ - if (otmp == uwep || otmp == uswapwep || otmp == uquiver) { - const char *verb = (otmp == uquiver) ? "Quiver" : "Wield", - *action = (otmp == uquiver) ? "un-ready" : "un-wield", - *which = is_plural(otmp) ? "these" : "this", - *what = ((otmp->oclass == WEAPON_CLASS || is_weptool(otmp)) - ? "weapon" : "item"); - /* - * TODO: if uwep is ammo, tell player that to shoot instead of toss, - * the corresponding launcher must be wielded; - */ - Sprintf(buf, "%s '%c' to %s %s %s", - verb, HANDS_SYM, action, which, - is_plural(otmp) ? makeplural(what) : what); - ia_addmenu(win, IA_UNWIELD, '-', buf); - } - - /* a: apply */ - if (otmp->oclass == COIN_CLASS) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Flip a coin"); - else if (otmp->otyp == CREAM_PIE) - ia_addmenu(win, IA_APPLY_OBJ, 'a', - "Hit yourself with this cream pie"); - else if (otmp->otyp == BULLWHIP) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Lash out with this whip"); - else if (otmp->otyp == GRAPPLING_HOOK) - ia_addmenu(win, IA_APPLY_OBJ, 'a', - "Grapple something with this hook"); - else if (otmp->otyp == BAG_OF_TRICKS && objects[otmp->otyp].oc_name_known) - /* bag of tricks skips this unless discovered */ - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Reach into this bag"); - else if (Is_container(otmp)) - /* bag of tricks gets here only if not yet discovered */ - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Open this container"); - else if (otmp->otyp == CAN_OF_GREASE) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Use the can to grease an item"); - else if (otmp->otyp == LOCK_PICK - || otmp->otyp == CREDIT_CARD - || otmp->otyp == SKELETON_KEY) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Use this tool to pick a lock"); - else if (otmp->otyp == TINNING_KIT) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Use this kit to tin a corpse"); - else if (otmp->otyp == LEASH) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Tie a pet to this leash"); - else if (otmp->otyp == SADDLE) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Place this saddle on a pet"); - else if (otmp->otyp == MAGIC_WHISTLE - || otmp->otyp == TIN_WHISTLE) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Blow this whistle"); - else if (otmp->otyp == EUCALYPTUS_LEAF) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Use this leaf as a whistle"); - else if (otmp->otyp == STETHOSCOPE) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Listen through the stethoscope"); - else if (otmp->otyp == MIRROR) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Show something its reflection"); - else if (otmp->otyp == BELL || otmp->otyp == BELL_OF_OPENING) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Ring the bell"); - else if (otmp->otyp == CANDELABRUM_OF_INVOCATION) { - Sprintf(buf, "%s the candelabrum", light); - ia_addmenu(win, IA_APPLY_OBJ, 'a', buf); - } else if (otmp->otyp == WAX_CANDLE || otmp->otyp == TALLOW_CANDLE) { - Sprintf(buf, "%s %s %s", light, - is_plural(otmp) ? "these" : "this", - simpleonames(otmp)); - ia_addmenu(win, IA_APPLY_OBJ, 'a', buf); - } else if (otmp->otyp == OIL_LAMP || otmp->otyp == MAGIC_LAMP - || otmp->otyp == BRASS_LANTERN) { - Sprintf(buf, "%s this light source", light); - ia_addmenu(win, IA_APPLY_OBJ, 'a', buf); - } else if (otmp->otyp == POT_OIL && objects[otmp->otyp].oc_name_known) { - Sprintf(buf, "%s this oil", light); - ia_addmenu(win, IA_APPLY_OBJ, 'a', buf); - } else if (otmp->oclass == POTION_CLASS) { - /* FIXME? this should probably be moved to 'D' rather than be 'a' */ - Sprintf(buf, "Dip something into %s potion%s", - is_plural(otmp) ? "one of these" : "this", plur(otmp->quan)); - ia_addmenu(win, IA_DIP_OBJ, 'a', buf); - } else if (otmp->otyp == EXPENSIVE_CAMERA) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Take a photograph"); - else if (otmp->otyp == TOWEL) - ia_addmenu(win, IA_APPLY_OBJ, 'a', - "Clean yourself off with this towel"); - else if (otmp->otyp == CRYSTAL_BALL) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Peer into this crystal ball"); - else if (otmp->otyp == MAGIC_MARKER) - ia_addmenu(win, IA_APPLY_OBJ, 'a', - "Write on something with this marker"); - else if (otmp->otyp == FIGURINE) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Make this figurine transform"); - else if (otmp->otyp == UNICORN_HORN) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Use this unicorn horn"); - else if (otmp->otyp == HORN_OF_PLENTY - && objects[otmp->otyp].oc_name_known) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Blow into the horn of plenty"); - else if (otmp->otyp >= WOODEN_FLUTE && otmp->otyp <= DRUM_OF_EARTHQUAKE) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Play this musical instrument"); - else if (otmp->otyp == LAND_MINE || otmp->otyp == BEARTRAP) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Arm this trap"); - else if (otmp->otyp == PICK_AXE || otmp->otyp == DWARVISH_MATTOCK) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Dig with this digging tool"); - else if (otmp->oclass == WAND_CLASS) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Break this wand"); - - /* 'c', 'C' - call an item or its type something */ - if (item_naming_classification(otmp, buf, buf2)) { - if (*buf) - ia_addmenu(win, IA_NAME_OBJ, 'c', buf); - if (*buf2) - ia_addmenu(win, IA_NAME_OTYP, 'C', buf2); - } - - /* d: drop item, works on everything except worn items; those will - always have a takeoff/remove choice so we don't have to worry - about the menu maybe being empty when 'd' is suppressed */ - if (!already_worn) { - Sprintf(buf, "Drop this %s", (otmp->quan > 1L) ? "stack" : "item"); - ia_addmenu(win, IA_DROP_OBJ, 'd', buf); - } - - /* e: eat item */ - if (otmp->otyp == TIN) { - Sprintf(buf, "Open %s%s and eat the contents", - (otmp->quan > 1L) ? "one of these tins" : "this tin", - (otmp->otyp == TIN && uwep && uwep->otyp == TIN_OPENER) - ? " with your tin opener" : ""); - ia_addmenu(win, IA_EAT_OBJ, 'e', buf); - } else if (is_edible(otmp)) { - Sprintf(buf, "Eat %s", (otmp->quan > 1L) ? "one of these" : "this"); - ia_addmenu(win, IA_EAT_OBJ, 'e', buf); - } - - /* E: engrave with item */ - if (otmp->otyp == TOWEL) { - ia_addmenu(win, IA_ENGRAVE_OBJ, 'E', - "Wipe the floor with this towel"); - } else if (otmp->otyp == MAGIC_MARKER) { - ia_addmenu(win, IA_ENGRAVE_OBJ, 'E', - "Scribble graffiti on the floor"); - } else if (otmp->oclass == WEAPON_CLASS || otmp->oclass == WAND_CLASS - || otmp->oclass == GEM_CLASS || otmp->oclass == RING_CLASS) { - Sprintf(buf, "%s on the %s with %s", - (is_blade(otmp) || otmp->oclass == WAND_CLASS - || ((otmp->oclass == GEM_CLASS || otmp->oclass == RING_CLASS) - && objects[otmp->otyp].oc_tough)) ? "Engrave" : "Write", - surface(u.ux, u.uy), - (otmp->quan > 1L) ? "one of these items" : "this item"); - ia_addmenu(win, IA_ENGRAVE_OBJ, 'E', buf); - } - - /* f: fire quivered ammo */ - if (otmp == uquiver) { - boolean shoot = ammo_and_launcher(otmp, uwep); - - /* FIXME: see the multi-shot FIXME about "one of" for 't: throw' */ - Sprintf(buf, "%s %s", shoot ? "Shoot" : "Throw", - (otmp->quan > 1L) ? "one of these" : "this"); - if (shoot) { - assert(uwep != NULL); - Sprintf(eos(buf), " with your wielded %s", simpleonames(uwep)); - } - ia_addmenu(win, IA_FIRE_OBJ, 'f', buf); - } - - /* i: #adjust inventory letter; gold can't be adjusted unless there - is some in a slot other than '$' (which shouldn't be possible) */ - if (otmp->oclass != COIN_CLASS || check_invent_gold("item-action")) - ia_addmenu(win, IA_ADJUST_OBJ, 'i', - "Adjust inventory by assigning new letter"); - /* I: #adjust inventory item by splitting its stack */ - if (otmp->quan > 1L && otmp->oclass != COIN_CLASS) - ia_addmenu(win, IA_ADJUST_STACK, 'I', - "Adjust inventory by splitting this stack"); - - /* O: offer sacrifice */ - if (IS_ALTAR(levl[u.ux][u.uy].typ) && !u.uswallow) { - /* FIXME: this doesn't match #offer's likely candidates, which don't - include corpses on Astral and don't include amulets off Astral */ - if (otmp->otyp == CORPSE) - ia_addmenu(win, IA_SACRIFICE, 'O', - "Offer this corpse as a sacrifice at this altar"); - else if (otmp->otyp == AMULET_OF_YENDOR - || otmp->otyp == FAKE_AMULET_OF_YENDOR) - ia_addmenu(win, IA_SACRIFICE, 'O', - "Offer this amulet as a sacrifice at this altar"); - } - - /* p: pay for unpaid utems */ - if (otmp->unpaid - /* FIXME: should also handle player owned container (so not - flagged 'unpaid') holding shop owned items */ - && (mtmp = shop_keeper(*in_rooms(u.ux, u.uy, SHOPBASE))) != 0 - && inhishop(mtmp)) { - Sprintf(buf, "Buy this unpaid %s", - (otmp->quan > 1L) ? "stack" : "item"); - ia_addmenu(win, IA_BUY_OBJ, 'p', buf); - } - - /* P: put on accessory */ - if (!already_worn) { - if (otmp->oclass == RING_CLASS || otmp->otyp == MEAT_RING) - ia_addmenu(win, IA_WEAR_OBJ, 'P', "Put this ring on"); - else if (otmp->oclass == AMULET_CLASS) - ia_addmenu(win, IA_WEAR_OBJ, 'P', "Put this amulet on"); - else if (otmp->otyp == TOWEL || otmp->otyp == BLINDFOLD) - ia_addmenu(win, IA_WEAR_OBJ, 'P', - "Use this to blindfold yourself"); - else if (otmp->otyp == LENSES) - ia_addmenu(win, IA_WEAR_OBJ, 'P', "Put these lenses on"); - } - - /* q: drink item */ - if (otmp->oclass == POTION_CLASS) { - Sprintf(buf, "Quaff (drink) %s", - (otmp->quan > 1L) ? "one of these potions" : "this potion"); - ia_addmenu(win, IA_QUAFF_OBJ, 'q', buf); - } - - /* Q: quiver throwable item */ - if ((otmp->oclass == GEM_CLASS || otmp->oclass == WEAPON_CLASS) - && otmp != uquiver) { - Sprintf(buf, "Quiver this %s for easy %s with \'f\'ire", - (otmp->quan > 1L) ? "stack" : "item", - ammo_and_launcher(otmp, uwep) ? "shooting" : "throwing"); - ia_addmenu(win, IA_QUIVER_OBJ, 'Q', buf); - } - - /* r: read item */ - if (item_reading_classification(otmp, buf) == IA_READ_OBJ) - ia_addmenu(win, IA_READ_OBJ, 'r', buf); - - /* R: remove accessory or rub item */ - if (otmp->owornmask & W_ACCESSORY) - ia_addmenu(win, IA_TAKEOFF_OBJ, 'R', "Remove this accessory"); - if (otmp->otyp == OIL_LAMP || otmp->otyp == MAGIC_LAMP - || otmp->otyp == BRASS_LANTERN) { - Sprintf(buf, "Rub this %s", simpleonames(otmp)); - ia_addmenu(win, IA_RUB_OBJ, 'R', buf); - } else if (otmp->oclass == GEM_CLASS && is_graystone(otmp)) - ia_addmenu(win, IA_RUB_OBJ, 'R', "Rub something on this stone"); - - /* t: throw item */ - if (!already_worn) { - boolean shoot = ammo_and_launcher(otmp, uwep); - - /* - * FIXME: - * 'one of these' should be changed to 'some of these' when there - * is the possibility of a multi-shot volley but we don't have - * any way to determine that except by actually calculating the - * volley count and that could randomly yield 1 here and 2..N - * while throwing or vice versa. - */ - Sprintf(buf, "%s %s%s", shoot ? "Shoot" : "Throw", - (otmp->quan == 1L) ? "this item" - : (otmp->otyp == GOLD_PIECE) ? "them" - : "one of these", - /* if otmp is quivered, we've already listed - 'f - shoot|throw this item' as a choice; - if 't' is duplicating that, say so ('t' and 'f' - behavior differs for throwing a stack of gold) */ - (otmp == uquiver && (otmp->otyp != GOLD_PIECE - || otmp->quan == 1L)) - ? " (same as 'f')" : ""); - ia_addmenu(win, IA_THROW_OBJ, 't', buf); - } - - /* T: take off armor, tip carried container */ - if (otmp->owornmask & W_ARMOR) - ia_addmenu(win, IA_TAKEOFF_OBJ, 'T', "Take off this armor"); - if ((Is_container(otmp) && (Has_contents(otmp) || !otmp->cknown)) - || (otmp->otyp == HORN_OF_PLENTY && (otmp->spe > 0 || !otmp->known))) - ia_addmenu(win, IA_TIP_CONTAINER, 'T', - "Tip all the contents out of this container"); - - /* V: invoke */ - if ((otmp->otyp == FAKE_AMULET_OF_YENDOR && !otmp->known) - || otmp->oartifact || objects[otmp->otyp].oc_unique - /* non-artifact crystal balls don't have any unique power but - the #invoke command lists them as likely candidates */ - || otmp->otyp == CRYSTAL_BALL) - ia_addmenu(win, IA_INVOKE_OBJ, 'V', - "Try to invoke a unique power of this object"); - - /* w: wield, hold in hands, works on everything but with different - advice text; not mentioned for things that are already wielded */ - if (otmp == uwep || cantwield(gy.youmonst.data)) { - ; /* either already wielded or can't wield anything; skip 'w' */ - } else if (otmp->oclass == WEAPON_CLASS || is_weptool(otmp) - || is_wet_towel(otmp) || otmp->otyp == HEAVY_IRON_BALL) { - Sprintf(buf, "Wield this %s as your weapon", - (otmp->quan > 1L) ? "stack" : "item"); - ia_addmenu(win, IA_WIELD_OBJ, 'w', buf); - } else if (otmp->otyp == TIN_OPENER) { - ia_addmenu(win, IA_WIELD_OBJ, 'w', - "Wield the tin opener to easily open tins"); - } else if (!already_worn) { - /* originally this was using "hold this item in your hands" but - there's no concept of "holding an item", plus it unwields - whatever item you already have wielded so use "wield this item" */ - Sprintf(buf, "Wield this %s in your %s", - (otmp->quan > 1L) ? "stack" : "item", - /* only two-handed weapons and unicorn horns care about - pluralizing "hand" and they won't reach here, but plural - sounds better when poly'd into something with "claw" */ - makeplural(body_part(HAND))); - ia_addmenu(win, IA_WIELD_OBJ, 'w', buf); - } - - /* W: wear armor */ - if (!already_worn) { - if (otmp->oclass == ARMOR_CLASS) - ia_addmenu(win, IA_WEAR_OBJ, 'W', "Wear this armor"); - } - - /* x: Swap main and readied weapon */ - if (otmp == uwep && uswapwep) - ia_addmenu(win, IA_SWAPWEAPON, 'x', - "Swap this with your alternate weapon"); - else if (otmp == uwep) - ia_addmenu(win, IA_SWAPWEAPON, 'x', - "Ready this as an alternate weapon"); - else if (otmp == uswapwep) - ia_addmenu(win, IA_SWAPWEAPON, 'x', - "Swap this with your main weapon"); - - /* this is based on TWOWEAPOK() in wield.c; we don't call can_two_weapon() - because it is very verbose; attempting to two-weapon might be rejected - but we screen out most reasons for rejection before offering it as a - choice */ -#define MAYBETWOWEAPON(obj) \ - ((((obj)->oclass == WEAPON_CLASS) \ - ? !(is_launcher(obj) || is_ammo(obj) || is_missile(obj)) \ - : is_weptool(obj)) \ - && !bimanual(obj)) - - /* X: Toggle two-weapon mode on or off */ - if ((otmp == uwep || otmp == uswapwep) - /* if already two-weaponing, no special checks needed to toggle off */ - && (u.twoweap - /* but if not, try to filter most "you can't do that" here */ - || (could_twoweap(gy.youmonst.data) && !uarms - && uwep && MAYBETWOWEAPON(uwep) - && uswapwep && MAYBETWOWEAPON(uswapwep)))) { - Sprintf(buf, "Toggle two-weapon combat %s", u.twoweap ? "off" : "on"); - ia_addmenu(win, IA_TWOWEAPON, 'X', buf); - } - -#undef MAYBETWOWEAPON - - /* z: Zap wand */ - if (otmp->oclass == WAND_CLASS) - ia_addmenu(win, IA_ZAP_OBJ, 'z', - "Zap this wand to release its magic"); - - /* ?: Look up an item in the game's database */ - if (ia_checkfile(otmp)) { - Sprintf(buf, "Look up information about %s", - (otmp->quan > 1L) ? "these" : "this"); - ia_addmenu(win, IA_WHATIS_OBJ, '/', buf); - } - - Sprintf(buf, "Do what with %s?", the(cxname(otmp))); - end_menu(win, buf); - - n = select_menu(win, PICK_ONE, &selected); - - if (n > 0) { - act = selected[0].item.a_int; - free((genericptr_t) selected); - - itemactions_pushkeys(otmp, act); - } - destroy_nhwindow(win); - - /* finish the 'i' command: no time elapses and cancelling without - selecting an action doesn't matter */ - return ECMD_OK; -} /* show some or all of inventory while allowing the picking of an item in order to preform context-sensitive item action on it; always returns 'ok'; @@ -4009,6 +3452,13 @@ display_inventory(const char *lets, boolean want_reply) FALSE, want_reply, (long *) 0); } +void +repopulate_perminvent(void) +{ + (void) display_pickinv(NULL, (char *) 0, (char *) 0, + FALSE, FALSE, (long *) 0); +} + /* * Show what is current using inventory letters. * @@ -4944,7 +4394,11 @@ mergable( if (obj->cursed != otmp->cursed || obj->blessed != otmp->blessed) return FALSE; - if ((obj->how_lost & ~LOSTOVERRIDEMASK) != 0) + + if (obj->how_lost == LOST_EXPLODING + || otmp->how_lost == LOST_EXPLODING) + return FALSE; + if (otmp->how_lost != LOST_NONE && (obj->how_lost != otmp->how_lost)) return FALSE; #if 0 /* don't require 'bypass' to match; that results in items dropped * via 'D' not stacking with compatible items already on the floor; @@ -4961,8 +4415,7 @@ mergable( if (obj->unpaid != otmp->unpaid || obj->spe != otmp->spe || obj->no_charge != otmp->no_charge || obj->obroken != otmp->obroken - || obj->otrapped != otmp->otrapped || obj->lamplit != otmp->lamplit - || obj->how_lost != otmp->how_lost) + || obj->otrapped != otmp->otrapped || obj->lamplit != otmp->lamplit) return FALSE; if (obj->oclass == FOOD_CLASS @@ -6075,7 +5528,7 @@ display_binventory(coordxy x, coordxy y, boolean as_if_seen) for (n = 0, obj = svl.level.buriedobjlist; obj; obj = obj->nobj) if (obj->ox == x && obj->oy == y) { if (as_if_seen) - obj->dknown = 1; + observe_object(obj); n++; } diff --git a/src/light.c b/src/light.c index 574665fa9..93f7ea50a 100644 --- a/src/light.c +++ b/src/light.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 light.c $NHDT-Date: 1726609514 2024/09/17 21:45:14 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.75 $ */ +/* NetHack 3.7 light.c $NHDT-Date: 1773375430 2026/03/12 20:17:10 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.82 $ */ /* Copyright (c) Dean Luick, 1994 */ /* NetHack may be freely redistributed. See license for details. */ @@ -974,9 +974,12 @@ wiz_light_sources(void) return ECMD_OK; } #endif /* !SFCTOOL */ + /* for 'onefile' processing where end of this file isn't necessarily the end of the source code seen by the compiler */ #undef LSF_SHOW #undef LSF_NEEDS_FIXUP +#undef LSF_IS_PROBLEMATIC #undef mon_is_local + /*light.c*/ diff --git a/src/mail.c b/src/mail.c index e6a5c2935..96735abe5 100644 --- a/src/mail.c +++ b/src/mail.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mail.c $NHDT-Date: 1596498174 2020/08/03 23:42:54 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.47 $ */ +/* NetHack 3.7 mail.c $NHDT-Date: 1762750699 2025/11/09 20:58:19 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.77 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Pasi Kallinen, 2018. */ /* NetHack may be freely redistributed. See license for details. */ @@ -333,18 +333,21 @@ md_rush(struct monst *md, if (fx == tx && fy == ty) break; - SetVoice(md, 0, 80, 0); - if ((mon = m_at(fx, fy)) != 0) /* save monster at this position */ - verbalize1(md_exclamations()); - else if (u_at(fx, fy)) - verbalize("Excuse me."); + mon = m_at(fx, fy); /* save monster at this position */ + if (!Deaf) { + SetVoice(md, 0, 80, 0); + if (mon) + verbalize1(md_exclamations()); + else if (u_at(fx, fy)) + verbalize("Excuse me."); + } if (mon) remove_monster(fx, fy); place_monster(md, fx, fy); /* put md down */ newsym(fx, fy); /* see it */ flush_screen(0); /* make sure md shows up */ - nh_delay_output(); /* wait a little bit */ + nh_delay_output(); /* wait a little bit */ /* Remove md from the dungeon. Restore original mon, if necessary. */ remove_monster(fx, fy); @@ -365,8 +368,12 @@ md_rush(struct monst *md, remove_monster(fx, fy); place_monster(md, fx, fy); /* display md with text below */ newsym(fx, fy); - SetVoice(md, 0, 80, 0); - verbalize("This place's too crowded. I'm outta here."); + if (!Deaf) { + SetVoice(md, 0, 80, 0); + verbalize("This place's too crowded. I'm outta here."); + } else { + pline("%s.", Never_mind); + } remove_monster(fx, fy); if ((mon->mx != fx) || (mon->my != fy)) /* put mon back */ @@ -406,8 +413,12 @@ newmail(struct mail_info *info) goto go_back; message_seen = TRUE; - SetVoice(md, 0, 80, 0); - verbalize("%s, %s! %s.", Hello(md), svp.plname, info->display_txt); + if (!Deaf) { + SetVoice(md, 0, 80, 0); + verbalize("%s, %s! %s.", Hello(md), svp.plname, info->display_txt); + } else { + pline("Message: %s.", info->display_txt); + } if (info->message_typ) { struct obj *obj = mksobj(SCR_MAIL, FALSE, FALSE); @@ -418,8 +429,13 @@ newmail(struct mail_info *info) new_omailcmd(obj, info->response_cmd); if (!m_next2u(md)) { - SetVoice(md, 0, 80, 0); - verbalize("Catch!"); + if (!Deaf) { + SetVoice(md, 0, 80, 0); + verbalize("Catch!"); + } else { + /* don't bother with nonverbal alternative ... */ + ; + } } display_nhwindow(WIN_MESSAGE, FALSE); obj = hold_another_object(obj, "Oops!", (const char *) 0, diff --git a/src/makemon.c b/src/makemon.c index c95fe8439..6f158ee01 100644 --- a/src/makemon.c +++ b/src/makemon.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 makemon.c $NHDT-Date: 1720128166 2024/07/04 21:22:46 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.249 $ */ +/* NetHack 3.7 makemon.c $NHDT-Date: 1770949988 2026/02/12 18:33:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.271 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1288,13 +1288,18 @@ makemon( /* quest leader and nemesis both know about all trap types */ if (ptr->msound == MS_LEADER || ptr->msound == MS_NEMESIS) mon_learns_traps(mtmp, ALL_TRAPS); + /* locations where monsters are already experienced with wands */ + if (Is_stronghold(&u.uz) || Is_knox(&u.uz) || In_endgame(&u.uz) || + In_hell(&u.uz) || In_V_tower(&u.uz) || In_quest(&u.uz)) + mtmp->mwandexp = TRUE; place_monster(mtmp, x, y); mtmp->mcansee = mtmp->mcanmove = TRUE; + mtmp->mgenmklev = gi.in_mklev; mtmp->seen_resistance = M_SEEN_NOTHING; mtmp->mpeaceful = (mmflags & MM_ANGRY) ? FALSE : peace_minded(ptr); if ((mmflags & MM_MINVIS) != 0) /* for ^G */ - mon_set_minvis(mtmp); /* call after place_monster() */ + mon_set_minvis(mtmp, FALSE); /* call after place_monster() */ switch (ptr->mlet) { case S_MIMIC: diff --git a/src/mcastu.c b/src/mcastu.c index 95e9a1c65..34d839906 100644 --- a/src/mcastu.c +++ b/src/mcastu.c @@ -1,48 +1,62 @@ -/* NetHack 3.7 mcastu.c $NHDT-Date: 1726168598 2024/09/12 19:16:38 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.105 $ */ +/* NetHack 3.7 mcastu.c $NHDT-Date: 1770949988 2026/02/12 18:33:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.111 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" -/* monster mage spells */ -enum mcast_mage_spells { - MGC_PSI_BOLT = 0, - MGC_CURE_SELF, - MGC_HASTE_SELF, - MGC_STUN_YOU, - MGC_DISAPPEAR, - MGC_WEAKEN_YOU, - MGC_DESTRY_ARMR, - MGC_CURSE_ITEMS, - MGC_AGGRAVATION, - MGC_SUMMON_MONS, - MGC_CLONE_WIZ, - MGC_DEATH_TOUCH +#define MCASTU_ENUM +enum mcast_spells { + #include "mcastu.h" +}; +#undef MCASTU_ENUM + +struct _mcast_data { + int level; + int flags; }; -/* monster cleric spells */ -enum mcast_cleric_spells { - CLC_OPEN_WOUNDS = 0, - CLC_CURE_SELF, - CLC_CONFUSE_YOU, - CLC_PARALYZE, - CLC_BLIND_YOU, - CLC_INSECTS, - CLC_CURSE_ITEMS, - CLC_LIGHTNING, - CLC_FIRE_PILLAR, - CLC_GEYSER +#define MCASTU_INIT +static struct _mcast_data mcast_data[] = { + #include "mcastu.h" +}; +#undef MCASTU_INIT + +/* spell lists for specific monster casters */ +/* the spells in the list should be in ascending level order */ +static int mon_cleric_spells[] = { + MCAST_OPEN_WOUNDS, MCAST_CURE_SELF, MCAST_CONFUSE_YOU, MCAST_PARALYZE, + MCAST_BLIND_YOU, MCAST_INSECTS, MCAST_CURSE_ITEMS, MCAST_LIGHTNING, + MCAST_FIRE_PILLAR, MCAST_GEYSER +}; +static int mon_wizard_spells[] = { + MCAST_PSI_BOLT, MCAST_CURE_SELF, MCAST_HASTE_SELF, MCAST_STUN_YOU, + MCAST_DISAPPEAR, MCAST_WEAKEN_YOU, MCAST_DESTRY_ARMR, MCAST_CURSE_ITEMS, + MCAST_AGGRAVATION, MCAST_SUMMON_MONS, MCAST_CLONE_WIZ, MCAST_DEATH_TOUCH }; staticfn void cursetxt(struct monst *, boolean); -staticfn int choose_magic_spell(int); -staticfn int choose_clerical_spell(int); +staticfn int choose_monster_spell(struct monst *, int); staticfn int m_cure_self(struct monst *, int); -staticfn void cast_wizard_spell(struct monst *, int, int); -staticfn void cast_cleric_spell(struct monst *, int, int); -staticfn boolean is_undirected_spell(unsigned int, int); -staticfn boolean spell_would_be_useless(struct monst *, unsigned int, int); +staticfn void mcast_death_touch(struct monst *); +staticfn void mcast_clone_wiz(struct monst *); +staticfn void mcast_summon_mons(struct monst *); +staticfn void mcast_destroy_armor(void); +staticfn void mcast_weaken_you(struct monst *, int); +staticfn void mcast_disappear(struct monst *); +staticfn void mcast_stun_you(int); +staticfn int mcast_geyser(int); +staticfn int mcast_fire_pillar(struct monst *, int); +staticfn int mcast_lightning(struct monst *, int); +staticfn int mcast_psi_bolt(int); +staticfn int mcast_open_wounds(int); +staticfn void mcast_insects(struct monst *); +staticfn void mcast_blind_you(void); +staticfn int mcast_paralyze(struct monst *); +staticfn void mcast_confuse_you(struct monst *); +staticfn void mcast_spell(struct monst *, int, int); +staticfn boolean is_undirected_spell(int); +staticfn boolean spell_would_be_useless(struct monst *, int); /* feedback when frustrated monster couldn't cast a spell */ staticfn void @@ -70,103 +84,42 @@ cursetxt(struct monst *mtmp, boolean undirected) } } -/* convert a level-based random selection into a specific mage spell; - inappropriate choices will be screened out by spell_would_be_useless() */ +/* choose a spell for monster to cast */ staticfn int -choose_magic_spell(int spellval) +choose_monster_spell(struct monst *mtmp, int adtyp) { - /* for 3.4.3 and earlier, val greater than 22 selected default spell */ - while (spellval > 24 && rn2(25)) - spellval = rn2(spellval); + int *list = NULL; + int i, spellval, len = 0; + int maxlev; - switch (spellval) { - case 24: - case 23: - if (Antimagic || Hallucination) - return MGC_PSI_BOLT; - FALLTHROUGH; - /*FALLTHRU*/ - case 22: - case 21: - case 20: - return MGC_DEATH_TOUCH; - case 19: - case 18: - return MGC_CLONE_WIZ; - case 17: - case 16: - case 15: - return MGC_SUMMON_MONS; - case 14: - case 13: - return MGC_AGGRAVATION; - case 12: - case 11: - case 10: - return MGC_CURSE_ITEMS; - case 9: - case 8: - return MGC_DESTRY_ARMR; - case 7: - case 6: - return MGC_WEAKEN_YOU; - case 5: - case 4: - return MGC_DISAPPEAR; - case 3: - return MGC_STUN_YOU; - case 2: - return MGC_HASTE_SELF; - case 1: - return MGC_CURE_SELF; - case 0: - default: - return MGC_PSI_BOLT; + /* which spell list to use? */ + if (adtyp == AD_SPEL) { + list = mon_wizard_spells; + len = SIZE(mon_wizard_spells); + } else if (adtyp == AD_CLRC) { + list = mon_cleric_spells; + len = SIZE(mon_cleric_spells); } -} -/* convert a level-based random selection into a specific cleric spell */ -staticfn int -choose_clerical_spell(int spellnum) -{ - /* for 3.4.3 and earlier, num greater than 13 selected the default spell - */ - while (spellnum > 15 && rn2(16)) - spellnum = rn2(spellnum); + if (!list || len < 1) + return MCAST_PSI_BOLT; - switch (spellnum) { - case 15: - case 14: - if (rn2(3)) - return CLC_OPEN_WOUNDS; - FALLTHROUGH; - /*FALLTHRU*/ - case 13: - return CLC_GEYSER; - case 12: - return CLC_FIRE_PILLAR; - case 11: - return CLC_LIGHTNING; - case 10: - case 9: - return CLC_CURSE_ITEMS; - case 8: - return CLC_INSECTS; - case 7: - case 6: - return CLC_BLIND_YOU; - case 5: - case 4: - return CLC_PARALYZE; - case 3: - case 2: - return CLC_CONFUSE_YOU; - case 1: - return CLC_CURE_SELF; - case 0: - default: - return CLC_OPEN_WOUNDS; - } + /* max spell level in this monster spell list */ + maxlev = mcast_data[list[len - 1]].level; + + /* which level spell to cast? */ + spellval = rn2(mtmp->m_lev); + if (spellval > maxlev && rn2(maxlev)) + spellval = rn2(maxlev); + + /* find the highest spell in the list we could cast */ + for (i = len-1; i >= 0; i--) + if (mcast_data[list[i]].level <= spellval + && !spell_would_be_useless(mtmp, list[i])) + return list[i]; + + /* or return the first spell in the list */ + return list[0]; } /* return values: @@ -200,15 +153,11 @@ castmu( int cnt = 40; do { - spellnum = rn2(ml); - if (mattk->adtyp == AD_SPEL) - spellnum = choose_magic_spell(spellnum); - else - spellnum = choose_clerical_spell(spellnum); + spellnum = choose_monster_spell(mtmp, mattk->adtyp); /* not trying to attack? don't allow directed spells */ if (!thinks_it_foundyou) { - if (!is_undirected_spell(mattk->adtyp, spellnum) - || spell_would_be_useless(mtmp, mattk->adtyp, spellnum)) { + if (!is_undirected_spell(spellnum) + || spell_would_be_useless(mtmp, spellnum)) { if (foundyou) impossible( "spellcasting monster found you and doesn't know it?"); @@ -217,7 +166,7 @@ castmu( break; } } while (--cnt > 0 - && spell_would_be_useless(mtmp, mattk->adtyp, spellnum)); + && spell_would_be_useless(mtmp, spellnum)); if (cnt == 0) return M_ATTK_MISS; } @@ -225,10 +174,12 @@ castmu( /* monster unable to cast spells? */ if (mtmp->mcan || mtmp->mspec_used || !ml || m_seenres(mtmp, cvt_adtyp_to_mseenres(mattk->adtyp))) { - cursetxt(mtmp, is_undirected_spell(mattk->adtyp, spellnum)); + cursetxt(mtmp, is_undirected_spell(spellnum)); return M_ATTK_MISS; } + debugpline3("castmu:%s,lvl:%i,spell:%i", noit_Monnam(mtmp), ml, spellnum); + if (mattk->adtyp == AD_SPEL || mattk->adtyp == AD_CLRC) { /* monst->m_lev is unsigned (uchar), monst->mspec_used is int */ mtmp->mspec_used = (int) ((mtmp->m_lev < 8) ? (10 - mtmp->m_lev) : 2); @@ -245,7 +196,7 @@ castmu( * for fire mis-aimed at ice. */ if (!foundyou && thinks_it_foundyou - && !is_undirected_spell(mattk->adtyp, spellnum)) { + && !is_undirected_spell(spellnum)) { pline_mon(mtmp, "%s casts a spell at %s!", canseemon(mtmp) ? Monnam(mtmp) : "Something", is_waterwall(mtmp->mux, mtmp->muy) ? "empty water" @@ -262,10 +213,10 @@ castmu( } return M_ATTK_MISS; } - if (canspotmon(mtmp) || !is_undirected_spell(mattk->adtyp, spellnum)) { + if (canspotmon(mtmp) || !is_undirected_spell(spellnum)) { pline_mon(mtmp, "%s casts a spell%s!", canspotmon(mtmp) ? Monnam(mtmp) : "Something", - is_undirected_spell(mattk->adtyp, spellnum) ? "" + is_undirected_spell(spellnum) ? "" : (Invis && !perceives(mtmp->data) && !u_at(mtmp->mux, mtmp->muy)) ? " at a spot near you" @@ -344,10 +295,7 @@ castmu( break; case AD_SPEL: /* wizard spell */ case AD_CLRC: /* clerical spell */ - if (mattk->adtyp == AD_SPEL) - cast_wizard_spell(mtmp, dmg, spellnum); - else - cast_cleric_spell(mtmp, dmg, spellnum); + mcast_spell(mtmp, dmg, spellnum); dmg = 0; /* done by the spell casting functions */ break; } /* switch */ @@ -437,6 +385,410 @@ death_inflicted_by( * Monster wizard and cleric spellcasting functions. */ +staticfn void +mcast_death_touch(struct monst *mtmp) +{ + pline("Oh no, %s's using the touch of death!", mhe(mtmp)); + if (nonliving(gy.youmonst.data) || is_demon(gy.youmonst.data)) { + You("seem no deader than before."); + } else if (!Antimagic && rn2(mtmp->m_lev) > 12) { + if (Hallucination) { + You("have an out of body experience."); + } else { + touch_of_death(mtmp); + } + monstunseesu(M_SEEN_MAGR); + } else { + if (Antimagic) { + shieldeff(u.ux, u.uy); + monstseesu(M_SEEN_MAGR); + } + pline("Lucky for you, it didn't work!"); + } +} + +staticfn void +mcast_clone_wiz(struct monst *mtmp) +{ + if (mtmp->iswiz && svc.context.no_of_wizards == 1) { + pline("Double Trouble..."); + clonewiz(); + } else + impossible("bad wizard cloning?"); +} + +staticfn void +mcast_summon_mons(struct monst *mtmp) +{ + int count = nasty(mtmp); + + if (!count) { + ; /* nothing was created? */ + } else if (mtmp->iswiz) { + SetVoice(mtmp, 0, 80, 0); + verbalize("Destroy the thief, my pet%s!", plur(count)); + } else { + boolean one = (count == 1); + const char *mappear = one ? "A monster appears" + : "Monsters appear"; + + /* messages not quite right if plural monsters created but + only a single monster is seen */ + if (Invis && !perceives(mtmp->data) + && (mtmp->mux != u.ux || mtmp->muy != u.uy)) + pline("%s %s a spot near you!", mappear, + one ? "at" : "around"); + else if (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy)) + pline("%s %s your displaced image!", mappear, + one ? "by" : "around"); + else + pline("%s from nowhere!", mappear); + } +} + +staticfn void +mcast_destroy_armor(void) +{ + if (Antimagic) { + shieldeff(u.ux, u.uy); + monstseesu(M_SEEN_MAGR); + pline("A field of force surrounds you!"); + } else if (!destroy_arm()) { + Your("skin itches."); + } else { + /* monsters only realize you aren't magic-protected if armor is + actually destroyed */ + monstunseesu(M_SEEN_MAGR); + } +} + +staticfn void +mcast_weaken_you(struct monst *mtmp, int dmg) +{ + if (Antimagic) { + shieldeff(u.ux, u.uy); + monstseesu(M_SEEN_MAGR); + You_feel("momentarily weakened."); + } else { + char kbuf[BUFSZ]; + + You("suddenly feel weaker!"); + dmg = mtmp->m_lev - 6; + if (dmg < 1) /* paranoia since only chosen when m_lev is high */ + dmg = 1; + if (Half_spell_damage) + dmg = (dmg + 1) / 2; + losestr(rnd(dmg), + death_inflicted_by(kbuf, "strength loss", mtmp), + KILLED_BY); + svk.killer.name[0] = '\0'; /* not killed if we get here... */ + monstunseesu(M_SEEN_MAGR); + } +} + +staticfn void +mcast_disappear(struct monst *mtmp) +{ + if (!mtmp->minvis && !mtmp->invis_blkd) { + if (canseemon(mtmp)) + pline_mon(mtmp, "%s suddenly %s!", Monnam(mtmp), + !See_invisible ? "disappears" : "becomes transparent"); + mon_set_minvis(mtmp, FALSE); + if (cansee(mtmp->mx, mtmp->my) && !canspotmon(mtmp)) + map_invisible(mtmp->mx, mtmp->my); + } else + impossible("no reason for monster to cast disappear spell?"); +} + +staticfn void +mcast_stun_you(int dmg) +{ + if (Antimagic || Free_action) { + shieldeff(u.ux, u.uy); + monstseesu(M_SEEN_MAGR); + if (!Stunned) + You_feel("momentarily disoriented."); + make_stunned(1L, FALSE); + } else { + You(Stunned ? "struggle to keep your balance." : "reel..."); + dmg = d(ACURR(A_DEX) < 12 ? 6 : 4, 4); + if (Half_spell_damage) + dmg = (dmg + 1) / 2; + make_stunned((HStun & TIMEOUT) + (long) dmg, FALSE); + monstunseesu(M_SEEN_MAGR); + } +} + +staticfn int +mcast_geyser(int dmg) +{ + /* this is physical damage (force not heat), + * not magical damage or fire damage + */ + pline("A sudden geyser slams into you from nowhere!"); + dmg = d(8, 6); + if (Half_physical_damage) + dmg = (dmg + 1) / 2; +#if 0 /* since inventory items aren't affected, don't include this */ + /* make floor items wet */ + water_damage_chain(level.objects[u.ux][u.uy], TRUE); +#endif + return dmg; +} + +staticfn int +mcast_fire_pillar(struct monst *mtmp, int dmg) +{ + int orig_dmg; + + pline("A pillar of fire strikes all around you!"); + orig_dmg = dmg = d(8, 6); + if (Fire_resistance) { + shieldeff(u.ux, u.uy); + monstseesu(M_SEEN_FIRE); + dmg = 0; + } else { + monstunseesu(M_SEEN_FIRE); + } + if (Half_spell_damage) + dmg = (dmg + 1) / 2; + burn_away_slime(); + (void) burnarmor(&gy.youmonst); + /* item destruction dmg */ + (void) destroy_items(&gy.youmonst, AD_FIRE, orig_dmg); + ignite_items(gi.invent); + /* burn up flammable items on the floor, melt ice terrain */ + mon_spell_hits_spot(mtmp, AD_FIRE, u.ux, u.uy); + return dmg; +} + +staticfn int +mcast_lightning(struct monst *mtmp, int dmg) +{ + int orig_dmg; + boolean reflects; + + Soundeffect(se_bolt_of_lightning, 80); + pline("A bolt of lightning strikes down at you from above!"); + reflects = ureflects("It bounces off your %s%s.", ""); + orig_dmg = dmg = d(8, 6); + if (reflects || Shock_resistance) { + shieldeff(u.ux, u.uy); + dmg = 0; + if (reflects) { + monstseesu(M_SEEN_REFL); + return dmg; + } + monstunseesu(M_SEEN_REFL); + monstseesu(M_SEEN_ELEC); + } else { + monstunseesu(M_SEEN_ELEC | M_SEEN_REFL); + } + if (Half_spell_damage) + dmg = (dmg + 1) / 2; + (void) destroy_items(&gy.youmonst, AD_ELEC, orig_dmg); + /* lightning might destroy iron bars if hero is on such a spot; + reflection protects terrain here [execution won't get here due + to 'if (reflects) break' above] but hero resistance doesn't; + do this before maybe blinding the hero via flashburn() */ + mon_spell_hits_spot(mtmp, AD_ELEC, u.ux, u.uy); + /* blind hero; no effect if already blind */ + (void) flashburn((long) rnd(100), TRUE); + return dmg; +} + +staticfn int +mcast_psi_bolt(int dmg) +{ + /* prior to 3.4.0 Antimagic was setting the damage to 1--this + made the spell virtually harmless to players with magic res. */ + if (Antimagic) { + shieldeff(u.ux, u.uy); + monstseesu(M_SEEN_MAGR); + dmg = (dmg + 1) / 2; + } else { + monstunseesu(M_SEEN_MAGR); + } + if (dmg <= 5) + You("get a slight %sache.", body_part(HEAD)); + else if (dmg <= 10) + Your("brain is on fire!"); + else if (dmg <= 20) + Your("%s suddenly aches painfully!", body_part(HEAD)); + else + Your("%s suddenly aches very painfully!", body_part(HEAD)); + return dmg; +} + +staticfn int +mcast_open_wounds(int dmg) +{ + if (Antimagic) { + shieldeff(u.ux, u.uy); + monstseesu(M_SEEN_MAGR); + dmg = (dmg + 1) / 2; + } else { + monstunseesu(M_SEEN_MAGR); + } + if (dmg <= 5) + Your("skin itches badly for a moment."); + else if (dmg <= 10) + pline("Wounds appear on your body!"); + else if (dmg <= 20) + pline("Severe wounds appear on your body!"); + else + Your("body is covered with painful wounds!"); + return dmg; +} + +staticfn void +mcast_insects(struct monst *mtmp) +{ + /* Try for insects, and if there are none + left, go for (sticks to) snakes. -3. */ + struct permonst *pm = mkclass(S_ANT, 0); + struct monst *mtmp2 = (struct monst *) 0; + char whatbuf[QBUFSZ], let = (pm ? S_ANT : S_SNAKE); + boolean success = FALSE, seecaster; + int i, quan, oldseen, newseen; + coord bypos; + const char *fmt, *what; + + oldseen = monster_census(TRUE); + quan = (mtmp->m_lev < 2) ? 1 : rnd((int) mtmp->m_lev / 2); + if (quan < 3) + quan = 3; + for (i = 0; i <= quan; i++) { + if (!enexto(&bypos, mtmp->mux, mtmp->muy, mtmp->data)) + return; + if ((pm = mkclass(let, 0)) != 0 + && (mtmp2 = makemon(pm, bypos.x, bypos.y, MM_ANGRY | MM_NOMSG)) + != 0) { + success = TRUE; + mtmp2->msleeping = mtmp2->mpeaceful = mtmp2->mtame = 0; + set_malign(mtmp2); + } + } + newseen = monster_census(TRUE); + + /* not canspotmon() which includes unseen things sensed via warning */ + seecaster = canseemon(mtmp) || tp_sensemon(mtmp) || Detect_monsters; + what = (let == S_SNAKE) ? "snakes" : "insects"; + if (Hallucination) + what = makeplural(bogusmon(whatbuf, (char *) 0)); + + fmt = 0; + if (!seecaster) { + if (newseen <= oldseen || Unaware) { + /* unseen caster fails or summons unseen critters, + or unconscious hero ("You dream that you hear...") */ + You_hear("someone summoning %s.", what); + } else { + char *arg; + + if (what != whatbuf) + what = strcpy(whatbuf, what); + /* unseen caster summoned seen critter(s) */ + arg = (newseen == oldseen + 1) ? an(makesingular(what)) + : whatbuf; + if (!Deaf) { + Soundeffect(se_someone_summoning, 100); + You_hear("someone summoning something, and %s %s.", arg, + vtense(arg, "appear")); + } else { + pline("%s %s.", upstart(arg), vtense(arg, "appear")); + } + } + + /* seen caster, possibly producing unseen--or just one--critters; + hero is told what the caster is doing and doesn't necessarily + observe complete accuracy of that caster's results (in other + words, no need to fuss with visibility or singularization; + player is told what's happening even if hero is unconscious) */ + } else if (!success) { + fmt = "%s casts at a clump of sticks, but nothing happens.%s"; + what = ""; + } else if (let == S_SNAKE) { + fmt = "%s transforms a clump of sticks into %s!"; + } else if (Invis && !perceives(mtmp->data) + && (mtmp->mux != u.ux || mtmp->muy != u.uy)) { + fmt = "%s summons %s around a spot near you!"; + } else if (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy)) { + fmt = "%s summons %s around your displaced image!"; + } else { + fmt = "%s summons %s!"; + } + if (fmt) { + DISABLE_WARNING_FORMAT_NONLITERAL; + pline_mon(mtmp, fmt, Monnam(mtmp), what); + RESTORE_WARNING_FORMAT_NONLITERAL; + } +} + +staticfn void +mcast_blind_you(void) +{ + /* note: resists_blnd() doesn't apply here */ + if (!Blinded) { + int num_eyes = eyecount(gy.youmonst.data); + + pline("Scales cover your %s!", (num_eyes == 1) + ? body_part(EYE) + : makeplural(body_part(EYE))); + make_blinded(Half_spell_damage ? 100L : 200L, FALSE); + if (!Blind) + Your1(vision_clears); + } else + impossible("no reason for monster to cast blindness spell?"); +} + +staticfn int +mcast_paralyze(struct monst *mtmp) +{ + int dmg = 0; + + if (Antimagic || Free_action) { + shieldeff(u.ux, u.uy); + monstseesu(M_SEEN_MAGR); + if (gm.multi >= 0) + You("stiffen briefly."); + dmg = 1; /* to produce nomul(-1), not actual damage */ + } else { + if (gm.multi >= 0) + You("are frozen in place!"); + dmg = 4 + (int) mtmp->m_lev; + if (Half_spell_damage) + dmg = (dmg + 1) / 2; + monstunseesu(M_SEEN_MAGR); + } + nomul(-dmg); + gm.multi_reason = "paralyzed by a monster"; + gn.nomovemsg = 0; + return dmg; +} + +staticfn void +mcast_confuse_you(struct monst *mtmp) +{ + if (Antimagic) { + shieldeff(u.ux, u.uy); + monstseesu(M_SEEN_MAGR); + You_feel("momentarily dizzy."); + } else { + boolean oldprop = !!Confusion; + int dmg = (int) mtmp->m_lev; + + if (Half_spell_damage) + dmg = (dmg + 1) / 2; + make_confused(HConfusion + dmg, TRUE); + if (Hallucination) + You_feel("%s!", oldprop ? "trippier" : "trippy"); + else + You_feel("%sconfused!", oldprop ? "more " : ""); + monstunseesu(M_SEEN_MAGR); + } +} + /* If dmg is zero, then the monster is not casting at you. If the monster is intentionally not casting at you, we have previously @@ -446,175 +798,93 @@ death_inflicted_by( and spell_would_be_useless(). */ staticfn void -cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum) +mcast_spell(struct monst *mtmp, int dmg, int spellnum) { if (dmg < 0) { - impossible("monster cast wizard spell (%d) with negative dmg (%d)?", + impossible("monster cast spell (%d) with negative dmg (%d)?", spellnum, dmg); return; } - if (dmg == 0 && !is_undirected_spell(AD_SPEL, spellnum)) { + if (dmg == 0 && !is_undirected_spell(spellnum)) { impossible("cast directed wizard spell (%d) with dmg=0?", spellnum); return; } switch (spellnum) { - case MGC_DEATH_TOUCH: - pline("Oh no, %s's using the touch of death!", mhe(mtmp)); - if (nonliving(gy.youmonst.data) || is_demon(gy.youmonst.data)) { - You("seem no deader than before."); - } else if (!Antimagic && rn2(mtmp->m_lev) > 12) { - if (Hallucination) { - You("have an out of body experience."); - } else { - touch_of_death(mtmp); - } - monstunseesu(M_SEEN_MAGR); - } else { - if (Antimagic) { - shieldeff(u.ux, u.uy); - monstseesu(M_SEEN_MAGR); - } - pline("Lucky for you, it didn't work!"); - } + case MCAST_DEATH_TOUCH: + mcast_death_touch(mtmp); dmg = 0; break; - case MGC_CLONE_WIZ: - if (mtmp->iswiz && svc.context.no_of_wizards == 1) { - pline("Double Trouble..."); - clonewiz(); - dmg = 0; - } else - impossible("bad wizard cloning?"); - break; - case MGC_SUMMON_MONS: { - int count = nasty(mtmp); - - if (!count) { - ; /* nothing was created? */ - } else if (mtmp->iswiz) { - SetVoice(mtmp, 0, 80, 0); - verbalize("Destroy the thief, my pet%s!", plur(count)); - } else { - boolean one = (count == 1); - const char *mappear = one ? "A monster appears" - : "Monsters appear"; - - /* messages not quite right if plural monsters created but - only a single monster is seen */ - if (Invis && !perceives(mtmp->data) - && (mtmp->mux != u.ux || mtmp->muy != u.uy)) - pline("%s %s a spot near you!", mappear, - one ? "at" : "around"); - else if (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy)) - pline("%s %s your displaced image!", mappear, - one ? "by" : "around"); - else - pline("%s from nowhere!", mappear); - } + case MCAST_CLONE_WIZ: + mcast_clone_wiz(mtmp); dmg = 0; break; - } - case MGC_AGGRAVATION: + case MCAST_SUMMON_MONS: + mcast_summon_mons(mtmp); + dmg = 0; + break; + case MCAST_AGGRAVATION: You_feel("that monsters are aware of your presence."); aggravate(); dmg = 0; break; - case MGC_CURSE_ITEMS: + case MCAST_CURSE_ITEMS: You_feel("as if you need some help."); rndcurse(); dmg = 0; break; - case MGC_DESTRY_ARMR: - if (Antimagic) { - shieldeff(u.ux, u.uy); - monstseesu(M_SEEN_MAGR); - pline("A field of force surrounds you!"); - } else if (!destroy_arm(some_armor(&gy.youmonst))) { - Your("skin itches."); - } else { - /* monsters only realize you aren't magic-protected if armor is - actually destroyed */ - monstunseesu(M_SEEN_MAGR); - } + case MCAST_DESTRY_ARMR: + mcast_destroy_armor(); dmg = 0; break; - case MGC_WEAKEN_YOU: /* drain strength */ - if (Antimagic) { - shieldeff(u.ux, u.uy); - monstseesu(M_SEEN_MAGR); - You_feel("momentarily weakened."); - } else { - char kbuf[BUFSZ]; - - You("suddenly feel weaker!"); - dmg = mtmp->m_lev - 6; - if (dmg < 1) /* paranoia since only chosen when m_lev is high */ - dmg = 1; - if (Half_spell_damage) - dmg = (dmg + 1) / 2; - losestr(rnd(dmg), - death_inflicted_by(kbuf, "strength loss", mtmp), - KILLED_BY); - svk.killer.name[0] = '\0'; /* not killed if we get here... */ - monstunseesu(M_SEEN_MAGR); - } + case MCAST_WEAKEN_YOU: /* drain strength */ + mcast_weaken_you(mtmp, dmg); dmg = 0; break; - case MGC_DISAPPEAR: /* makes self invisible */ - if (!mtmp->minvis && !mtmp->invis_blkd) { - if (canseemon(mtmp)) - pline_mon(mtmp, "%s suddenly %s!", Monnam(mtmp), - !See_invisible ? "disappears" : "becomes transparent"); - mon_set_minvis(mtmp); - if (cansee(mtmp->mx, mtmp->my) && !canspotmon(mtmp)) - map_invisible(mtmp->mx, mtmp->my); - dmg = 0; - } else - impossible("no reason for monster to cast disappear spell?"); - break; - case MGC_STUN_YOU: - if (Antimagic || Free_action) { - shieldeff(u.ux, u.uy); - monstseesu(M_SEEN_MAGR); - if (!Stunned) - You_feel("momentarily disoriented."); - make_stunned(1L, FALSE); - } else { - You(Stunned ? "struggle to keep your balance." : "reel..."); - dmg = d(ACURR(A_DEX) < 12 ? 6 : 4, 4); - if (Half_spell_damage) - dmg = (dmg + 1) / 2; - make_stunned((HStun & TIMEOUT) + (long) dmg, FALSE); - monstunseesu(M_SEEN_MAGR); - } + case MCAST_DISAPPEAR: /* makes self invisible */ + mcast_disappear(mtmp); dmg = 0; break; - case MGC_HASTE_SELF: + case MCAST_STUN_YOU: + mcast_stun_you(dmg); + dmg = 0; + break; + case MCAST_HASTE_SELF: mon_adjust_speed(mtmp, 1, (struct obj *) 0); dmg = 0; break; - case MGC_CURE_SELF: + case MCAST_CURE_SELF: dmg = m_cure_self(mtmp, dmg); break; - case MGC_PSI_BOLT: - /* prior to 3.4.0 Antimagic was setting the damage to 1--this - made the spell virtually harmless to players with magic res. */ - if (Antimagic) { - shieldeff(u.ux, u.uy); - monstseesu(M_SEEN_MAGR); - dmg = (dmg + 1) / 2; - } else { - monstunseesu(M_SEEN_MAGR); - } - if (dmg <= 5) - You("get a slight %sache.", body_part(HEAD)); - else if (dmg <= 10) - Your("brain is on fire!"); - else if (dmg <= 20) - Your("%s suddenly aches painfully!", body_part(HEAD)); - else - Your("%s suddenly aches very painfully!", body_part(HEAD)); + case MCAST_PSI_BOLT: + dmg = mcast_psi_bolt(dmg); + break; + case MCAST_GEYSER: + dmg = mcast_geyser(dmg); + break; + case MCAST_FIRE_PILLAR: + dmg = mcast_fire_pillar(mtmp, dmg); + break; + case MCAST_LIGHTNING: + dmg = mcast_lightning(mtmp, dmg); + break; + case MCAST_INSECTS: + mcast_insects(mtmp); + dmg = 0; + break; + case MCAST_BLIND_YOU: + mcast_blind_you(); + dmg = 0; + break; + case MCAST_PARALYZE: + dmg = mcast_paralyze(mtmp); + break; + case MCAST_CONFUSE_YOU: + mcast_confuse_you(mtmp); + dmg = 0; + break; + case MCAST_OPEN_WOUNDS: + dmg = mcast_open_wounds(dmg); break; default: impossible("mcastu: invalid magic spell (%d)", spellnum); @@ -626,291 +896,17 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum) mdamageu(mtmp, dmg); } -DISABLE_WARNING_FORMAT_NONLITERAL - -staticfn void -cast_cleric_spell(struct monst *mtmp, int dmg, int spellnum) -{ - int orig_dmg = 0; - - if (dmg < 0) { - impossible("monster cast cleric spell (%d) with negative dmg (%d)?", - spellnum, dmg); - return; - } - if (dmg == 0 && !is_undirected_spell(AD_CLRC, spellnum)) { - impossible("cast directed cleric spell (%d) with dmg=0?", spellnum); - return; - } - - switch (spellnum) { - case CLC_GEYSER: - /* this is physical damage (force not heat), - * not magical damage or fire damage - */ - pline("A sudden geyser slams into you from nowhere!"); - dmg = d(8, 6); - if (Half_physical_damage) - dmg = (dmg + 1) / 2; -#if 0 /* since inventory items aren't affected, don't include this */ - /* make floor items wet */ - water_damage_chain(level.objects[u.ux][u.uy], TRUE); -#endif - break; - case CLC_FIRE_PILLAR: - pline("A pillar of fire strikes all around you!"); - orig_dmg = dmg = d(8, 6); - if (Fire_resistance) { - shieldeff(u.ux, u.uy); - monstseesu(M_SEEN_FIRE); - dmg = 0; - } else { - monstunseesu(M_SEEN_FIRE); - } - if (Half_spell_damage) - dmg = (dmg + 1) / 2; - burn_away_slime(); - (void) burnarmor(&gy.youmonst); - /* item destruction dmg */ - (void) destroy_items(&gy.youmonst, AD_FIRE, orig_dmg); - ignite_items(gi.invent); - /* burn up flammable items on the floor, melt ice terrain */ - mon_spell_hits_spot(mtmp, AD_FIRE, u.ux, u.uy); - break; - case CLC_LIGHTNING: { - boolean reflects; - - Soundeffect(se_bolt_of_lightning, 80); - pline("A bolt of lightning strikes down at you from above!"); - reflects = ureflects("It bounces off your %s%s.", ""); - orig_dmg = dmg = d(8, 6); - if (reflects || Shock_resistance) { - shieldeff(u.ux, u.uy); - dmg = 0; - if (reflects) { - monstseesu(M_SEEN_REFL); - break; - } - monstunseesu(M_SEEN_REFL); - monstseesu(M_SEEN_ELEC); - } else { - monstunseesu(M_SEEN_ELEC | M_SEEN_REFL); - } - if (Half_spell_damage) - dmg = (dmg + 1) / 2; - (void) destroy_items(&gy.youmonst, AD_ELEC, orig_dmg); - /* lightning might destroy iron bars if hero is on such a spot; - reflection protects terrain here [execution won't get here due - to 'if (reflects) break' above] but hero resistance doesn't; - do this before maybe blinding the hero via flashburn() */ - mon_spell_hits_spot(mtmp, AD_ELEC, u.ux, u.uy); - /* blind hero; no effect if already blind */ - (void) flashburn((long) rnd(100), TRUE); - break; - } - case CLC_CURSE_ITEMS: - You_feel("as if you need some help."); - rndcurse(); - dmg = 0; - break; - case CLC_INSECTS: { - /* Try for insects, and if there are none - left, go for (sticks to) snakes. -3. */ - struct permonst *pm = mkclass(S_ANT, 0); - struct monst *mtmp2 = (struct monst *) 0; - char whatbuf[QBUFSZ], let = (pm ? S_ANT : S_SNAKE); - boolean success = FALSE, seecaster; - int i, quan, oldseen, newseen; - coord bypos; - const char *fmt, *what; - - oldseen = monster_census(TRUE); - quan = (mtmp->m_lev < 2) ? 1 : rnd((int) mtmp->m_lev / 2); - if (quan < 3) - quan = 3; - for (i = 0; i <= quan; i++) { - if (!enexto(&bypos, mtmp->mux, mtmp->muy, mtmp->data)) - break; - if ((pm = mkclass(let, 0)) != 0 - && (mtmp2 = makemon(pm, bypos.x, bypos.y, MM_ANGRY | MM_NOMSG)) - != 0) { - success = TRUE; - mtmp2->msleeping = mtmp2->mpeaceful = mtmp2->mtame = 0; - set_malign(mtmp2); - } - } - newseen = monster_census(TRUE); - - /* not canspotmon() which includes unseen things sensed via warning */ - seecaster = canseemon(mtmp) || tp_sensemon(mtmp) || Detect_monsters; - what = (let == S_SNAKE) ? "snakes" : "insects"; - if (Hallucination) - what = makeplural(bogusmon(whatbuf, (char *) 0)); - - fmt = 0; - if (!seecaster) { - if (newseen <= oldseen || Unaware) { - /* unseen caster fails or summons unseen critters, - or unconscious hero ("You dream that you hear...") */ - You_hear("someone summoning %s.", what); - } else { - char *arg; - - if (what != whatbuf) - what = strcpy(whatbuf, what); - /* unseen caster summoned seen critter(s) */ - arg = (newseen == oldseen + 1) ? an(makesingular(what)) - : whatbuf; - if (!Deaf) { - Soundeffect(se_someone_summoning, 100); - You_hear("someone summoning something, and %s %s.", arg, - vtense(arg, "appear")); - } else { - pline("%s %s.", upstart(arg), vtense(arg, "appear")); - } - } - - /* seen caster, possibly producing unseen--or just one--critters; - hero is told what the caster is doing and doesn't necessarily - observe complete accuracy of that caster's results (in other - words, no need to fuss with visibility or singularization; - player is told what's happening even if hero is unconscious) */ - } else if (!success) { - fmt = "%s casts at a clump of sticks, but nothing happens.%s"; - what = ""; - } else if (let == S_SNAKE) { - fmt = "%s transforms a clump of sticks into %s!"; - } else if (Invis && !perceives(mtmp->data) - && (mtmp->mux != u.ux || mtmp->muy != u.uy)) { - fmt = "%s summons %s around a spot near you!"; - } else if (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy)) { - fmt = "%s summons %s around your displaced image!"; - } else { - fmt = "%s summons %s!"; - } - if (fmt) - pline_mon(mtmp, fmt, Monnam(mtmp), what); - - dmg = 0; - break; - } - case CLC_BLIND_YOU: - /* note: resists_blnd() doesn't apply here */ - if (!Blinded) { - int num_eyes = eyecount(gy.youmonst.data); - - pline("Scales cover your %s!", (num_eyes == 1) - ? body_part(EYE) - : makeplural(body_part(EYE))); - make_blinded(Half_spell_damage ? 100L : 200L, FALSE); - if (!Blind) - Your1(vision_clears); - dmg = 0; - } else - impossible("no reason for monster to cast blindness spell?"); - break; - case CLC_PARALYZE: - if (Antimagic || Free_action) { - shieldeff(u.ux, u.uy); - monstseesu(M_SEEN_MAGR); - if (gm.multi >= 0) - You("stiffen briefly."); - dmg = 1; /* to produce nomul(-1), not actual damage */ - } else { - if (gm.multi >= 0) - You("are frozen in place!"); - dmg = 4 + (int) mtmp->m_lev; - if (Half_spell_damage) - dmg = (dmg + 1) / 2; - monstunseesu(M_SEEN_MAGR); - } - nomul(-dmg); - gm.multi_reason = "paralyzed by a monster"; - gn.nomovemsg = 0; - dmg = 0; - break; - case CLC_CONFUSE_YOU: - if (Antimagic) { - shieldeff(u.ux, u.uy); - monstseesu(M_SEEN_MAGR); - You_feel("momentarily dizzy."); - } else { - boolean oldprop = !!Confusion; - - dmg = (int) mtmp->m_lev; - if (Half_spell_damage) - dmg = (dmg + 1) / 2; - make_confused(HConfusion + dmg, TRUE); - if (Hallucination) - You_feel("%s!", oldprop ? "trippier" : "trippy"); - else - You_feel("%sconfused!", oldprop ? "more " : ""); - monstunseesu(M_SEEN_MAGR); - } - dmg = 0; - break; - case CLC_CURE_SELF: - dmg = m_cure_self(mtmp, dmg); - break; - case CLC_OPEN_WOUNDS: - if (Antimagic) { - shieldeff(u.ux, u.uy); - monstseesu(M_SEEN_MAGR); - dmg = (dmg + 1) / 2; - } else { - monstunseesu(M_SEEN_MAGR); - } - if (dmg <= 5) - Your("skin itches badly for a moment."); - else if (dmg <= 10) - pline("Wounds appear on your body!"); - else if (dmg <= 20) - pline("Severe wounds appear on your body!"); - else - Your("body is covered with painful wounds!"); - break; - default: - impossible("mcastu: invalid clerical spell (%d)", spellnum); - dmg = 0; - break; - } - - if (dmg) - mdamageu(mtmp, dmg); -} - -RESTORE_WARNING_FORMAT_NONLITERAL - staticfn boolean -is_undirected_spell(unsigned int adtyp, int spellnum) +is_undirected_spell(int spellnum) { - if (adtyp == AD_SPEL) { - switch (spellnum) { - case MGC_CLONE_WIZ: - case MGC_SUMMON_MONS: - case MGC_AGGRAVATION: - case MGC_DISAPPEAR: - case MGC_HASTE_SELF: - case MGC_CURE_SELF: - return TRUE; - default: - break; - } - } else if (adtyp == AD_CLRC) { - switch (spellnum) { - case CLC_INSECTS: - case CLC_CURE_SELF: - return TRUE; - default: - break; - } - } + if ((mcast_data[spellnum].flags & MCF_INDIRECT) != 0) + return TRUE; return FALSE; } /* Some spells are useless under some circumstances. */ staticfn boolean -spell_would_be_useless(struct monst *mtmp, unsigned int adtyp, int spellnum) +spell_would_be_useless(struct monst *mtmp, int spellnum) { /* Some spells don't require the player to really be there and can be cast * by the monster when you're invisible, yet still shouldn't be cast when @@ -918,60 +914,72 @@ spell_would_be_useless(struct monst *mtmp, unsigned int adtyp, int spellnum) * This check isn't quite right because it always uses your real position. * We really want something like "if the monster could see mux, muy". */ - boolean mcouldseeu = couldsee(mtmp->mx, mtmp->my); - if (adtyp == AD_SPEL) { - /* aggravate monsters, etc. won't be cast by peaceful monsters */ - if (mtmp->mpeaceful - && (spellnum == MGC_AGGRAVATION || spellnum == MGC_SUMMON_MONS - || spellnum == MGC_CLONE_WIZ)) + /* spell is only cast by hostile monsters */ + if ((mcast_data[spellnum].flags & MCF_HOSTILE) != 0) { + if (mtmp->mpeaceful) return TRUE; + } + + /* spell needs the monster to see hero */ + if ((mcast_data[spellnum].flags & MCF_SIGHT) != 0) { + boolean mcouldseeu = couldsee(mtmp->mx, mtmp->my); + + if (!mcouldseeu) + return TRUE; + } + + switch (spellnum) { + case MCAST_DEATH_TOUCH: + if ((Antimagic || Hallucination) && !rn2(2)) + return TRUE; + break; + case MCAST_GEYSER: + if (!rn2(5)) + return TRUE; + break; + case MCAST_CLONE_WIZ: + /* only the Wizard is allowed to clone himself */ + if (!mtmp->iswiz || svc.context.no_of_wizards > 1) + return TRUE; + break; + case MCAST_AGGRAVATION: + /* aggravation (global wakeup) when everyone is already active */ + /* if nothing needs to be awakened then this spell is useless + but caster might not realize that [chance to pick it then + must be very small otherwise caller's many retry attempts + will eventually end up picking it too often] */ + if (!has_aggravatables(mtmp)) + return rn2(100) ? TRUE : FALSE; + break; + case MCAST_HASTE_SELF: /* haste self when already fast */ - if (mtmp->permspeed == MFAST && spellnum == MGC_HASTE_SELF) + if (mtmp->permspeed == MFAST) return TRUE; + break; + case MCAST_DISAPPEAR: /* invisibility when already invisible */ - if ((mtmp->minvis || mtmp->invis_blkd) && spellnum == MGC_DISAPPEAR) + if (mtmp->minvis || mtmp->invis_blkd) return TRUE; /* peaceful monster won't cast invisibility if you can't see invisible, same as when monsters drink potions of invisibility. This doesn't really make a lot of sense, but lets the player avoid hitting peaceful monsters by mistake */ - if (mtmp->mpeaceful && !See_invisible && spellnum == MGC_DISAPPEAR) + if (mtmp->mpeaceful && !See_invisible) return TRUE; + break; + case MCAST_CURE_SELF: /* healing when already healed */ - if (mtmp->mhp == mtmp->mhpmax && spellnum == MGC_CURE_SELF) + if (mtmp->mhp == mtmp->mhpmax) return TRUE; - /* don't summon monsters if it doesn't think you're around */ - if (!mcouldseeu && (spellnum == MGC_SUMMON_MONS - || (!mtmp->iswiz && spellnum == MGC_CLONE_WIZ))) - return TRUE; - if ((!mtmp->iswiz || svc.context.no_of_wizards > 1) - && spellnum == MGC_CLONE_WIZ) - return TRUE; - /* aggravation (global wakeup) when everyone is already active */ - if (spellnum == MGC_AGGRAVATION) { - /* if nothing needs to be awakened then this spell is useless - but caster might not realize that [chance to pick it then - must be very small otherwise caller's many retry attempts - will eventually end up picking it too often] */ - if (!has_aggravatables(mtmp)) - return rn2(100) ? TRUE : FALSE; - } - } else if (adtyp == AD_CLRC) { - /* summon insects/sticks to snakes won't be cast by peaceful monsters - */ - if (mtmp->mpeaceful && spellnum == CLC_INSECTS) - return TRUE; - /* healing when already healed */ - if (mtmp->mhp == mtmp->mhpmax && spellnum == CLC_CURE_SELF) - return TRUE; - /* don't summon insects if it doesn't think you're around */ - if (!mcouldseeu && spellnum == CLC_INSECTS) - return TRUE; - /* blindness spell on blinded player */ - if (Blinded && spellnum == CLC_BLIND_YOU) + break; + case MCAST_BLIND_YOU: + if (Blinded) return TRUE; + break; + default: + break; } return FALSE; } diff --git a/src/mhitm.c b/src/mhitm.c index 322b65b22..5e8bb928e 100644 --- a/src/mhitm.c +++ b/src/mhitm.c @@ -1259,7 +1259,7 @@ slept_monst(struct monst *mon) void rustm(struct monst *mdef, struct obj *obj) { - int dmgtyp = -1, chance = 1; + int dmgtyp = ERODE_NONE, chance = 1; if (!mdef || !obj) return; /* just in case */ @@ -1275,7 +1275,7 @@ rustm(struct monst *mdef, struct obj *obj) chance = 6; } - if (dmgtyp >= 0 && !rn2(chance)) + if (dmgtyp != ERODE_NONE && !rn2(chance)) (void) erode_obj(obj, (char *) 0, dmgtyp, EF_GREASE | EF_VERBOSE); } diff --git a/src/mhitu.c b/src/mhitu.c index d6332c18d..71d90a8ec 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mhitu.c $NHDT-Date: 1740534854 2025/02/25 17:54:14 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.327 $ */ +/* NetHack 3.7 mhitu.c $NHDT-Date: 1775259433 2026/04/03 15:37:13 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.341 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -12,13 +12,13 @@ staticfn void missmu(struct monst *, boolean, struct attack *); staticfn void mswings(struct monst *, struct obj *, boolean); staticfn void wildmiss(struct monst *, struct attack *); staticfn void calc_mattacku_vars(struct monst *, boolean *, boolean *, - boolean *, boolean *); + boolean *, boolean *); staticfn void summonmu(struct monst *, boolean); staticfn int hitmu(struct monst *, struct attack *); staticfn int gulpmu(struct monst *, struct attack *); staticfn int explmu(struct monst *, struct attack *, boolean); staticfn void mayberem(struct monst *, const char *, struct obj *, - const char *); + const char *); staticfn int assess_dmg(struct monst *, int); staticfn int passiveum(struct permonst *, struct monst *, struct attack *); @@ -94,7 +94,7 @@ missmu(struct monst *mtmp, boolean nearmiss, struct attack *mattk) pline_mon(mtmp, "%s pretends to be friendly.", Monnam(mtmp)); else pline_mon(mtmp, "%s %smisses!", Monnam(mtmp), - (nearmiss && flags.verbose) ? "just " : ""); + (nearmiss && flags.verbose) ? "just " : ""); stop_occupation(); } @@ -142,7 +142,9 @@ mswings( /* return how a poison attack was delivered */ const char * -mpoisons_subj(struct monst *mtmp, struct attack *mattk) +mpoisons_subj( + struct monst *mtmp, + struct attack *mattk) { if (mattk->aatyp == AT_WEAP) { struct obj *mwep = (mtmp == &gy.youmonst) ? uwep : MON_WEP(mtmp); @@ -210,9 +212,9 @@ wildmiss(struct monst *mtmp, struct attack *mattk) || nolimbs(mtmp->data)) ? "lunges" : "swings"; - if (compat) + if (compat) { pline("%s tries to touch you and misses!", Monst_name); - else + } else { switch (rn2(3)) { case 0: pline("%s %s wildly and misses!", Monst_name, swings); @@ -230,7 +232,7 @@ wildmiss(struct monst *mtmp, struct attack *mattk) pline("%s %s wildly!", Monst_name, swings); break; } - + } } else if (unotthere) { /* Displaced */ /* give 'displaced' message even if hero is Blind */ if (compat) @@ -431,6 +433,13 @@ getmattk( } + /* elementals on their home plane do double damage */ + if (attk != alt_attk_buf && is_home_elemental(mptr)) { + *alt_attk_buf = *attk; + attk = alt_attk_buf; + attk->damn *= 2; + } + return attk; } @@ -892,7 +901,8 @@ mattacku(struct monst *mtmp) mon_currwep = MON_WEP(mtmp); if (mon_currwep) { boolean bash = (is_pole(mon_currwep) - && !is_art(mon_currwep, ART_SNICKERSNEE) + && !is_art(mon_currwep, + ART_SNICKERSNEE) && m_next2u(mtmp)); hittmp = hitval(mon_currwep, &gy.youmonst); @@ -1034,7 +1044,9 @@ diseasemu(struct permonst *mdat) /* check whether slippery clothing protects from hug or wrap attack */ boolean -u_slip_free(struct monst *mtmp, struct attack *mattk) +u_slip_free( + struct monst *mtmp, + struct attack *mattk) { struct obj *obj; @@ -1199,7 +1211,8 @@ hitmu(struct monst *mtmp, struct attack *mattk) mhm.damage = 1; } - if (mhm.damage) { + if (mhm.damage > 0) { + /* [Half_physical_damage isn't applied to mhm.permdmg] */ if (Half_physical_damage /* Mitre of Holiness, even if not currently blessed */ || (Role_if(PM_CLERIC) && uarmh && is_quest_artifact(uarmh) @@ -1366,15 +1379,14 @@ gulpmu(struct monst *mtmp, struct attack *mattk) for other swallowings, longer time means more chances for the swallower to attack */ if (mattk->adtyp == AD_DGST) { - tim_tmp = 25 - (int) mtmp->m_lev; - if (tim_tmp > 0) - tim_tmp = rnd(tim_tmp) / 2; - else if (tim_tmp < 0) - tim_tmp = -(rnd(-tim_tmp) / 2); /* having good armor & high constitution makes it take longer for you to be digested, but you'll end up trapped inside for longer too */ - tim_tmp += -u.uac + 10 + (ACURR(A_CON) / 3 - 1); + tim_tmp = (int)ACURR(A_CON) + 10 - (int)u.uac + rn2(20); + if (tim_tmp < 0) + tim_tmp = 0; + tim_tmp /= (int) mtmp->m_lev; + tim_tmp += 3; } else { /* higher level attacker takes longer to eject hero */ tim_tmp = rnd((int) mtmp->m_lev + 10 / 2); @@ -1534,8 +1546,15 @@ gulpmu(struct monst *mtmp, struct attack *mattk) break; } - if (physical_damage) + if (physical_damage) { + /* same damage reduction for AC as in hitmu */ + if (u.uac < 0) + tmp -= rnd(-u.uac); + if (tmp < 0) + tmp = 1; + tmp = Maybe_Half_Phys(tmp); + } gm.mswallower = mtmp; /* match gulpmm() */ mdamageu(mtmp, tmp); @@ -1569,7 +1588,10 @@ gulpmu(struct monst *mtmp, struct attack *mattk) /* monster explodes in your face */ staticfn int -explmu(struct monst *mtmp, struct attack *mattk, boolean ufound) +explmu( + struct monst *mtmp, + struct attack *mattk, + boolean ufound) { boolean kill_agr = TRUE; boolean not_affected; @@ -2127,7 +2149,7 @@ doseduce(struct monst *mon) /* have her call your gloves by their correct name, possibly revealing them to you */ if (yourgloves) - yourgloves->dknown = 1; + observe_object(yourgloves); verbalize("Well, then you owe me %s%s!", yourgloves ? yname(yourgloves) : "twelve pairs of gloves", @@ -2263,8 +2285,12 @@ doseduce(struct monst *mon) if (cost > umoney) cost = umoney; if (!cost) { - SetVoice(mon, 0, 80, 0); - verbalize("It's on the house!"); + if (!Deaf) { + SetVoice(mon, 0, 80, 0); + verbalize("It's on the house!"); + } else { + pline("No charge."); + } } else { pline_mon(mon, "%s takes %ld %s for services rendered!", noit_Monnam(mon), cost, currency(cost)); @@ -2345,9 +2371,10 @@ assess_dmg(struct monst *mtmp, int tmp) callback (optional). Callback returns 0 if the attack is active */ -boolean ranged_attk_assessed( -struct monst *mtmp, -boolean (*assessfunc)(struct monst *, int)) +boolean +ranged_attk_assessed( + struct monst *mtmp, + boolean (*assessfunc)(struct monst *, int)) { int i; struct permonst *ptr = mtmp->data; @@ -2364,7 +2391,9 @@ boolean (*assessfunc)(struct monst *, int)) /* can be used as ranged_attk_assessed() callback. Returns TRUE if monster is avoiding use of this attack */ boolean -mon_avoiding_this_attack(struct monst *mtmp, int attkidx) +mon_avoiding_this_attack( + struct monst *mtmp, + int attkidx) { struct permonst *ptr = mtmp->data; int typ = -1; @@ -2381,7 +2410,8 @@ mon_avoiding_this_attack(struct monst *mtmp, int attkidx) * ranged_attk_assessed(mtmp, mon_avoiding_this_attack) * but without the added assessment function call overhead. */ -boolean ranged_attk_available(struct monst *mtmp) +boolean +ranged_attk_available(struct monst *mtmp) { int i, typ = -1; struct permonst *ptr = mtmp->data; @@ -2608,4 +2638,6 @@ cloneu(void) return mon; } +#undef ld + /*mhitu.c*/ diff --git a/src/minion.c b/src/minion.c index 6acd508a7..96f9ffe1c 100644 --- a/src/minion.c +++ b/src/minion.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 minion.c $NHDT-Date: 1624322864 2021/06/22 00:47:44 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.60 $ */ +/* NetHack 3.7 minion.c $NHDT-Date: 1762727599 2025/11/09 14:33:19 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.81 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2008. */ /* NetHack may be freely redistributed. See license for details. */ @@ -332,7 +332,8 @@ demon_talk(struct monst *mtmp) else if (canseemon(mtmp)) pline("%s seems to be demanding something.", Amonnam(mtmp)); offer = 0L; - if (!Deaf && ((offer = bribe(mtmp)) >= demand)) { + if (!Deaf && + ((offer = bribe(mtmp, "How much will you offer?")) >= demand)) { pline("%s vanishes, laughing about cowardly mortals.", Amonnam(mtmp)); } else if (offer > 0L @@ -346,20 +347,24 @@ demon_talk(struct monst *mtmp) return 0; } } + /* if 'mtmp' is unrecognizable due to hero's hallucination, + #chronicle will reveal its true identity -- just live with that; + also, avoid random hallucinatory currency() units */ livelog_printf(LL_UMONST, "bribed %s with %ld %s for safe passage", - Amonnam(mtmp), offer, currency(offer)); + x_monnam(mtmp, ARTICLE_A, (char *) 0, EXACT_NAME, FALSE), + offer, (offer == 1L) ? "zorkmid" : "zorkmids"); mongone(mtmp); return 1; } long -bribe(struct monst *mtmp) +bribe(struct monst *mtmp, const char *prompt) { char buf[BUFSZ] = DUMMY; long offer; long umoney = money_cnt(gi.invent); - getlin("How much will you offer?", buf); + getlin(prompt, buf); if (sscanf(buf, "%ld", &offer) != 1) offer = 0L; diff --git a/src/mklev.c b/src/mklev.c index 8c75cc1df..2ddeefae9 100644 --- a/src/mklev.c +++ b/src/mklev.c @@ -306,6 +306,10 @@ add_room(coordxy lowx, coordxy lowy, coordxy hix, coordxy hiy, { struct mkroom *croom; +#ifdef DEBUG + if (svn.nroom >= MAXNROFROOMS) + panic("level has too many rooms"); +#endif /*DEBUG*/ croom = &svr.rooms[svn.nroom]; do_room_or_subroom(croom, lowx, lowy, hix, hiy, lit, rtype, special, (boolean) TRUE); @@ -322,6 +326,12 @@ add_subroom(struct mkroom *proom, { struct mkroom *croom; +#ifdef DEBUG + if (gn.nsubroom >= MAXNROFROOMS) + panic("level has too many subrooms"); + if (proom->nsubrooms >= MAX_SUBROOMS) + panic("room has too many subrooms"); +#endif /*DEBUG*/ croom = &gs.subrooms[gn.nsubroom]; do_room_or_subroom(croom, lowx, lowy, hix, hiy, lit, rtype, special, (boolean) FALSE); @@ -449,8 +459,6 @@ join(int a, int b, boolean nxcor) if (troom->lx > croom->hx) { dx = 1; dy = 0; - xx = croom->hx + 1; - tx = troom->lx - 1; if (!finddpos(&cc, DIR_E, croom)) return; if (!finddpos(&tt, DIR_W, troom)) @@ -458,8 +466,6 @@ join(int a, int b, boolean nxcor) } else if (troom->hy < croom->ly) { dy = -1; dx = 0; - yy = croom->ly - 1; - ty = troom->hy + 1; if (!finddpos(&cc, DIR_N, croom)) return; if (!finddpos(&tt, DIR_S, troom)) @@ -467,8 +473,6 @@ join(int a, int b, boolean nxcor) } else if (troom->hx < croom->lx) { dx = -1; dy = 0; - xx = croom->lx - 1; - tx = troom->hx + 1; if (!finddpos(&cc, DIR_W, croom)) return; if (!finddpos(&tt, DIR_E, troom)) @@ -476,8 +480,6 @@ join(int a, int b, boolean nxcor) } else { dy = 1; dx = 0; - yy = croom->hy + 1; - ty = troom->ly - 1; if (!finddpos(&cc, DIR_S, croom)) return; if (!finddpos(&tt, DIR_N, troom)) @@ -899,6 +901,7 @@ clear_level_structures(void) svl.level.flags.noautosearch = 0; svl.level.flags.fumaroles = 0; svl.level.flags.stormy = 0; + svl.level.flags.stasis_until = 0L; svn.nroom = 0; svr.rooms[0].hx = -1; @@ -1812,8 +1815,7 @@ staticfn void mktrap_victim(struct trap *ttmp) { /* Object generated by the trap; initially NULL, stays NULL if - we fail to generate an object or if the trap doesn't - generate objects. */ + the trap doesn't generate objects. */ struct obj *otmp = NULL; int victim_mnum; /* race of the victim */ unsigned lvl = level_difficulty(); @@ -1867,14 +1869,18 @@ mktrap_victim(struct trap *ttmp) break; } - otmp = mkobj(poss_class, FALSE); /* these items are always cursed, both for flavour (owned by a dead adventurer, bones-pile-style) and for balance (less useful to use, and encourage pets to avoid the trap) */ - if (otmp) { - otmp->blessed = 0; - otmp->cursed = 1; - otmp->owt = weight(otmp); + otmp = mkobj(poss_class, FALSE); + curse(otmp); + /* for mktrap_victim(), PIT is actually an exploded LANDMINE */ + if (ttmp->ttyp == PIT && breaktest(otmp)) { + /* landmine: if fragile object has been created, destroy it; + don't worry about non-empty containers--they aren't + breakable--nor about breakable contents of such */ + dealloc_obj(otmp); + } else { place_object(otmp, x, y); } @@ -2054,7 +2060,7 @@ mktrap( m.x = m.y = 0; /* no traps in pools */ - if (tm && is_pool(tm->x, tm->y)) + if (tm && is_pool_or_lava(tm->x, tm->y)) return; if (num > NO_TRAP && num < TRAPNUM) { @@ -2126,7 +2132,8 @@ mktrap( immediately lethal). Finally, pits are excluded because it's weird to see an item in a pit and yet not be able to identify that the pit is there. */ - if (kind != NO_TRAP && !(mktrapflags & MKTRAP_NOVICTIM) + if (gi.in_mklev + && kind != NO_TRAP && !(mktrapflags & MKTRAP_NOVICTIM) && lvl <= (unsigned) rnd(4) && kind != SQKY_BOARD && kind != RUST_TRAP /* rolling boulder trap might not have a boulder if there was no @@ -2135,8 +2142,15 @@ mktrap( && !(kind == ROLLING_BOULDER_TRAP && t->launch.x == t->tx && t->launch.y == t->ty) && !is_pit(kind) && (kind < HOLE || kind == MAGIC_TRAP)) { + if (kind == LANDMINE) { + /* if victim was killed by a land mine, we won't scatter objects; + treat it as exploded, converting it into an unconcealed pit */ + t->ttyp = PIT; + t->tseen = 1; + } mktrap_victim(t); } + return; } /* Create stairs up or down at x,y. diff --git a/src/mkmaze.c b/src/mkmaze.c index 9c665a37e..573eada04 100644 --- a/src/mkmaze.c +++ b/src/mkmaze.c @@ -1426,8 +1426,9 @@ get_level_extends( *bottom = ymax; } -/* put a non-diggable boundary around the initial portion of a level map. - * assumes that no level will initially put things beyond the isok() range. +/* put a non-diggable/non-phaseable boundary around the initial portion + * of a level map. assumes that no level will initially put things + * beyond the isok() range. * * we can't bound unconditionally on the last line with something in it, * because that something might be a niche which was already reachable, @@ -1449,9 +1450,14 @@ bound_digging(void) for (x = 0; x < COLNO; x++) for (y = 0; y < ROWNO; y++) - if (IS_STWALL(levl[x][y].typ) - && (y <= ymin || y >= ymax || x <= xmin || x >= xmax)) - levl[x][y].wall_info |= W_NONDIGGABLE; + if (IS_STWALL(levl[x][y].typ)) { + /* undiggable walls at edges, ... */ + if (y <= ymin || y >= ymax || x <= xmin || x >= xmax) + levl[x][y].wall_info |= W_NONDIGGABLE; + /* one tile past that, everything is also unphaseable */ + if (y < ymin || y > ymax || x < xmin || x > xmax) + levl[x][y].wall_info |= W_NONPASSWALL; + } } void diff --git a/src/mkobj.c b/src/mkobj.c index a7687ca8a..eb55a0802 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mkobj.c $NHDT-Date: 1737528890 2025/01/21 22:54:50 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.315 $ */ +/* NetHack 3.7 mkobj.c $NHDT-Date: 1764044196 2025/11/24 20:16:36 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.326 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -34,10 +34,10 @@ struct icp { }; static const struct icp mkobjprobs[] = { { 10, WEAPON_CLASS }, - { 10, ARMOR_CLASS }, + { 11, ARMOR_CLASS }, { 20, FOOD_CLASS }, { 8, TOOL_CLASS }, - { 8, GEM_CLASS }, + { 7, GEM_CLASS }, { 16, POTION_CLASS }, { 16, SCROLL_CLASS }, { 4, SPBOOK_CLASS }, @@ -834,6 +834,8 @@ static const char dknowns[] = { WAND_CLASS, RING_CLASS, POTION_CLASS, void clear_dknown(struct obj *obj) { + /* note: this is an unobserving not an observing, so don't call + observe_object even if dknown is being set to 1 */ obj->dknown = strchr(dknowns, obj->oclass) ? 0 : 1; if ((obj->otyp >= ELVEN_SHIELD && obj->otyp <= ORCISH_SHIELD) || obj->otyp == SHIELD_OF_REFLECTION @@ -960,6 +962,7 @@ mksobj_init(struct obj **obj, boolean artif) we initialize glob->owt explicitly so weight() doesn't need to perform any fix up and returns glob->owt as-is */ otmp->owt = objects[otmp->otyp].oc_weight; + /* dknown, but not observed */ otmp->known = otmp->dknown = 1; otmp->corpsenm = PM_GRAY_OOZE + (otmp->otyp - GLOB_OF_GRAY_OOZE); start_glob_timeout(otmp, 0L); @@ -1112,6 +1115,10 @@ mksobj_init(struct obj **obj, boolean artif) case WAND_CLASS: if (otmp->otyp == WAN_WISHING) otmp->spe = 1; + else if (otmp->otyp == WAN_STASIS) + /* just as easy to recharge as other NODIR wands, but starts with + fewer charges */ + otmp->spe = rn1(4, 3); else otmp->spe = rn1(5, (objects[otmp->otyp].oc_dir == NODIR) ? 11 : 4); @@ -1657,7 +1664,7 @@ shrink_glob( } if (updinv) { update_inventory(); - (void) encumber_msg(); + encumber_msg(); } } @@ -1979,6 +1986,18 @@ rnd_treefruit_at(coordxy x, coordxy y) return mksobj_at(ROLL_FROM(treefruits), x, y, TRUE, FALSE); } +/* for describing objects embedded in trees */ +boolean +is_treefruit(struct obj *otmp) +{ + int fruitidx; + + for (fruitidx = 0; fruitidx < SIZE(treefruits); ++fruitidx) + if (treefruits[fruitidx] == otmp->otyp) + return TRUE; + return FALSE; +} + /* create a stack of N gold pieces; never returns Null */ struct obj * mkgold(long amount, coordxy x, coordxy y) @@ -2271,8 +2290,9 @@ is_rottable(struct obj *otmp) { int otyp = otmp->otyp; - return (boolean) (objects[otyp].oc_material <= WOOD - && objects[otyp].oc_material != LIQUID); + return (boolean) ((objects[otyp].oc_material <= WOOD + && objects[otyp].oc_material != LIQUID) + || objects[otyp].oc_material == DRAGON_HIDE); } /* @@ -2892,7 +2912,7 @@ hornoplenty( /* item still in magic horn was weightless; when it's now in a carried container, hero's encumbrance could change */ if (carried(targetbox)) { - (void) encumber_msg(); + encumber_msg(); update_inventory(); /* for contents count or wizweight */ } } else { diff --git a/src/mon.c b/src/mon.c index dce75521c..789ca8276 100644 --- a/src/mon.c +++ b/src/mon.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mon.c $NHDT-Date: 1753856387 2025/07/29 22:19:47 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.611 $ */ +/* NetHack 3.7 mon.c $NHDT-Date: 1770949988 2026/02/12 18:33:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.621 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -6,6 +6,7 @@ #include "hack.h" #include "mfndpos.h" +staticfn void pet_sanity_check(struct monst *, const char *); staticfn void sanity_check_single_mon(struct monst *, boolean, const char *); staticfn struct obj *make_corpse(struct monst *, unsigned); staticfn int minliquid_core(struct monst *); @@ -45,14 +46,28 @@ extern const struct shclass shtypes[]; /* defined in shknam.c */ || !svl.level.flags.deathdrops \ || (svl.level.flags.graveyard && is_undead(mdat) && rn2(3))) - -#if 0 +#if 0 /* potentially of historical interest */ /* part of the original warning code which was replaced in 3.3.1 */ const char *warnings[] = { "white", "pink", "red", "ruby", "purple", "black" }; #endif /* 0 */ +staticfn void +pet_sanity_check( + struct monst *mtmp, + const char *msgarg) +{ + if (has_edog(mtmp)) { + struct edog *edog = EDOG(mtmp); + + if (edog->droptime > svm.moves) + impossible("insane pet #%u has droptime (%ld)" + " in the future (%ld) (%s)", + mtmp->m_id, edog->droptime, svm.moves, msgarg); + /* TODO: verify some of the other edog fields */ + } +} staticfn void sanity_check_single_mon( @@ -116,8 +131,12 @@ sanity_check_single_mon( if (mtmp->isminion && !has_emin(mtmp)) impossible("minion without emin (%s)", msg); /* guardian angel on astral level is tame but has emin rather than edog */ - if (mtmp->mtame && !has_edog(mtmp) && !mtmp->isminion) - impossible("pet without edog (%s)", msg); + if (mtmp->mtame) { + if (!has_edog(mtmp) && !mtmp->isminion) + impossible("pet without edog (%s)", msg); + else + pet_sanity_check(mtmp, msg); + } /* steed should be tame and saddled */ if (mtmp == u.usteed) { const char *ns, *nt = !mtmp->mtame ? "not tame" : 0; @@ -133,7 +152,7 @@ sanity_check_single_mon( if (mtmp->mtrapped) { if (mtmp->wormno) { - /* TODO: how to check worm in trap? */ + ; /* TODO: how to check worm in trap? */ } else if (!t_at(mx, my)) impossible("trapped without a trap (%s)", msg); } @@ -667,14 +686,18 @@ make_corpse(struct monst *mtmp, unsigned int corpseflags) case PM_ROPE_GOLEM: num = rn2(3); while (num-- > 0) { - obj = mksobj_at(rn2(2) ? LEASH : BULLWHIP, x, y, TRUE, FALSE); + obj = mksobj_at(rn2(2) ? LEASH + : rn2(3) ? BULLWHIP : GRAPPLING_HOOK, + x, y, TRUE, FALSE); } free_mgivenname(mtmp); break; case PM_LEATHER_GOLEM: num = d(2, 4); while (num--) - obj = mksobj_at(LEATHER_ARMOR, x, y, TRUE, FALSE); + obj = mksobj_at(rn2(4) ? LEATHER_ARMOR + : rn2(3) ? LEATHER_CLOAK : SADDLE, + x, y, TRUE, FALSE); free_mgivenname(mtmp); break; case PM_GOLD_GOLEM: @@ -1112,9 +1135,14 @@ mcalcmove( * MFAST's `+ 2' prevents hasted speed 1 from becoming a no-op; * both adjustments have negligible effect on higher speeds. */ - if (mon->mspeed == MSLOW) - mmove = (2 * mmove + 1) / 3; - else if (mon->mspeed == MFAST) + if (mon->mspeed == MSLOW) { + /* slow-monster effects work better against faster monsters: they + lose 1/3 of their speed below 12 but 2/3 of their speed above */ + if (mmove < NORMAL_SPEED) + mmove = (2 * mmove + 1) / 3; + else + mmove = 4 + (mmove / 3); + } else if (mon->mspeed == MFAST) mmove = (4 * mmove + 2) / 3; if (mon == u.usteed && u.ugallop && svc.context.mv) { @@ -1775,7 +1803,7 @@ mon_givit(struct monst *mtmp, struct permonst *ptr) char mtmpbuf[BUFSZ]; Strcpy(mtmpbuf, Monnam(mtmp)); - mon_set_minvis(mtmp); + mon_set_minvis(mtmp, FALSE); if (vis) pline_mon(mtmp, "%s %s.", mtmpbuf, !canspotmon(mtmp) ? "vanishes" @@ -2111,8 +2139,7 @@ m_in_air(struct monst *mtmp) int mfndpos( struct monst *mon, - coord *poss, /* coord poss[9] */ - long *info, /* long info[9] */ + struct mfndposdata *data, long flag) { struct permonst *mdat = mon->data; @@ -2132,12 +2159,12 @@ mfndpos( y = mon->my; nowtyp = levl[x][y].typ; + (void)memset((genericptr_t) data, 0, sizeof(struct mfndposdata)); + nodiag = NODIAG(mdat - mons); wantpool = (mdat->mlet == S_EEL); poolok = ((!Is_waterlevel(&u.uz) && m_in_air(mon)) || (is_swimmer(mdat) && !wantpool)); - /* note: floating eye is the only is_floater() so this could be - simplified, but then adding another floater would be error prone */ lavaok = (m_in_air(mon) || likes_lava(mdat)); if (mdat == &mons[PM_FLOATING_EYE]) /* prefers to avoid heat */ lavaok = FALSE; @@ -2247,11 +2274,11 @@ mfndpos( dispy = ny; } - info[cnt] = 0; + data->info[cnt] = 0; if (onscary(dispx, dispy, mon)) { if (!(flag & ALLOW_SSM)) continue; - info[cnt] |= ALLOW_SSM; + data->info[cnt] |= ALLOW_SSM; } if (u_at(nx, ny) || (nx == mon->mux && ny == mon->muy)) { @@ -2267,25 +2294,25 @@ mfndpos( } if (!(flag & ALLOW_U)) continue; - info[cnt] |= ALLOW_U; + data->info[cnt] |= ALLOW_U; } else { if (MON_AT(nx, ny)) { struct monst *mtmp2 = m_at(nx, ny); long mmflag = flag | mm_aggression(mon, mtmp2); if (mmflag & ALLOW_M) { - info[cnt] |= ALLOW_M; + data->info[cnt] |= ALLOW_M; if (mtmp2->mtame) { if (!(mmflag & ALLOW_TM)) continue; - info[cnt] |= ALLOW_TM; + data->info[cnt] |= ALLOW_TM; } } else { flag &= ~ALLOW_MDISP; /* depends upon defender */ mmflag = flag | mm_displacement(mon, mtmp2); if (!(mmflag & ALLOW_MDISP)) continue; - info[cnt] |= ALLOW_MDISP; + data->info[cnt] |= ALLOW_MDISP; } } /* Note: ALLOW_SANCT only prevents movement, not @@ -2296,23 +2323,23 @@ mfndpos( && in_your_sanctuary((struct monst *) 0, nx, ny)) { if (!(flag & ALLOW_SANCT)) continue; - info[cnt] |= ALLOW_SANCT; + data->info[cnt] |= ALLOW_SANCT; } } if (checkobj && sobj_at(CLOVE_OF_GARLIC, nx, ny)) { if (flag & NOGARLIC) continue; - info[cnt] |= NOGARLIC; + data->info[cnt] |= NOGARLIC; } if (checkobj && sobj_at(BOULDER, nx, ny)) { if (!(flag & ALLOW_ROCK)) continue; - info[cnt] |= ALLOW_ROCK; + data->info[cnt] |= ALLOW_ROCK; } if (monseeu && monlineu(mon, nx, ny)) { if (flag & NOTONL) continue; - info[cnt] |= NOTONL; + data->info[cnt] |= NOTONL; } /* check for diagonal tight squeeze */ if (nx != x && ny != y && bad_rock(mdat, x, ny) @@ -2332,17 +2359,17 @@ mfndpos( } /* fixed-destination teleport trap, was used by hero */ if (fixed_tele_trap(ttmp) && hastrack(nx, ny)) - info[cnt] |= ALLOW_TRAPS; + data->info[cnt] |= ALLOW_TRAPS; else if (!m_harmless_trap(mon, ttmp)) { if (!(flag & ALLOW_TRAPS)) { if (mon_knows_traps(mon, ttmp->ttyp)) continue; } - info[cnt] |= ALLOW_TRAPS; + data->info[cnt] |= ALLOW_TRAPS; } } - poss[cnt].x = nx; - poss[cnt].y = ny; + data->poss[cnt].x = nx; + data->poss[cnt].y = ny; cnt++; } } @@ -2350,6 +2377,7 @@ mfndpos( wantpool = FALSE; goto nexttry; } + data->cnt = cnt; return cnt; } @@ -2359,23 +2387,36 @@ mfndpos( staticfn long mm_2way_aggression(struct monst *magr, struct monst *mdef) { + if (On_W_tower_level(&u.uz)) { + /* treat inside the Wizard's tower as if it were a separate level + from outside so when hero is inside Wizard's tower, both monsters + need to be too; when outside, the monsters need to be too */ + if (In_W_tower(u.ux, u.uy, &u.uz) + ? (!In_W_tower(magr->mx, magr->my, &u.uz) + || !In_W_tower(mdef->mx, mdef->my, &u.uz)) + : (In_W_tower(magr->mx, magr->my, &u.uz) + || In_W_tower(mdef->mx, mdef->my, &u.uz))) + return 0L; + } /* liches/zombies vs things that can be zombified Note: avoid this on the Castle level, partly for balance reasons (the monster-versus-monster fights clear out significant portions of the Castle and make it easier than it should be), partly for flavor reasons (monsters who attacked other monsters to zombify them would - have been counterattacked to death long before the hero arried). + have been counterattacked to death long before the hero arrived). Also don't include unique monsters in this, otherwise it leads to them waking up early (e.g. because a zombie decided to attack the Wizard of Yendor). */ - if (zombie_maker(magr) && zombie_form(mdef->data) != NON_PM) - if (!Is_stronghold(&u.uz) && - !unique_corpstat(magr->data) && !unique_corpstat(mdef->data)) + if (zombie_maker(magr) && zombie_form(mdef->data) != NON_PM) { + if (magr->mgenmklev && mdef->mgenmklev) + return 0L; + if (!Is_stronghold(&u.uz) + && !unique_corpstat(magr->data) && !unique_corpstat(mdef->data)) return (ALLOW_M | ALLOW_TM); - - return 0; + } + return 0L; } /* Monster against monster special attacks; for the specified monster @@ -2392,7 +2433,7 @@ mm_aggression( /* don't allow pets to fight each other */ if (magr->mtame && mdef->mtame) - return 0; + return 0L; /* supposedly purple worms are attracted to shrieking because they like to eat shriekers, so attack the latter when feasible */ @@ -2674,20 +2715,6 @@ mon_leaving_level(struct monst *mon) #endif } if (onmap) { - /* gulpmm() tries to deal with this, but without this extra - place_monster() the messages for exploding engulfed gas spore - are delivered without the engulfer being shown on the map */ - if (gm.mswallower && gm.mswallower != mon) { - if (gm.mswallower != &gy.youmonst) { - place_monster(gm.mswallower, - gm.mswallower->mx, gm.mswallower->my); - } else { - u_on_newpos(u.ux, u.uy); - if (canspotself()) - display_self(); - } - } - mon->mundetected = 0; /* for migration; doesn't matter for death */ /* unhide mimic in case its shape has been blocking line of sight or it is accompanying the hero to another level */ @@ -2938,7 +2965,7 @@ vamprises(struct monst *mtmp) set_msg_xy(0, 0); /* in case none of the messages was delivered */ door->doormask = D_NODOOR; - unblock_point(x, y); + recalc_block_point(x, y); if (trapped) { boolean trap_killed, save_verbose = flags.verbose; @@ -3419,6 +3446,7 @@ unstuck(struct monst *mtmp) set_ustuck((struct monst *) 0); if (swallowed) { + gm.mswallower = (struct monst *) 0; u.ux = mtmp->mx; u.uy = mtmp->my; if (Punished && uchain->where != OBJ_FLOOR) @@ -3468,10 +3496,10 @@ xkilled( iflags.sad_feeling = FALSE; mtmp->mhp = 0; /* caller will usually have already done this */ - if (!noconduct) /* KMH, conduct */ + if (!noconduct) { /* KMH, conduct */ if (!u.uconduct.killer++) livelog_printf(LL_CONDUCT, "killed for the first time"); - + } if (!nomsg) { boolean namedpet = has_mgivenname(mtmp) && !Hallucination; @@ -4647,6 +4675,13 @@ restrap(struct monst *mtmp) return FALSE; if (mtmp->data->mlet == S_MIMIC) { + if (mtmp->msleeping || mtmp->mfrozen) { + /* + * The mimic needs to be awake to disguise itself + * as something else. + */ + return FALSE; + } set_mimic_sym(mtmp); return TRUE; } else if (levl[mtmp->mx][mtmp->my].typ == ROOM) { @@ -4843,8 +4878,10 @@ decide_to_shapeshift(struct monst *mon) if (!is_vampshifter(mon)) { /* regular shapeshifter; 'ptr' is Null */ - if (!rn2(6)) + if (!mon->mspec_used && !rn2(6)) { dochng = TRUE; + mon->mspec_used = 3 + rn2(10); + } } else if (!(mon->mstrategy & STRAT_WAITFORU)) { /* The vampire has to be in good health (mhp) to maintain * its shifted form. @@ -6040,4 +6077,12 @@ flash_mon(struct monst *mtmp) gv.viz_array[my][mx] = saveviz; newsym(mx, my); } + +/* cleanup for 'onefile' processing */ +#undef LEVEL_SPECIFIC_NOCORPSE +#undef KEEPTRAITS +#undef mstoning +#undef livelog_mon_nam +#undef BREEDER_EGG + /*mon.c*/ diff --git a/src/monmove.c b/src/monmove.c index 8bf665099..6489d19d4 100644 --- a/src/monmove.c +++ b/src/monmove.c @@ -241,22 +241,33 @@ boolean onscary(coordxy x, coordxy y, struct monst *mtmp) { struct engr *ep; + /* <0,0> is used by musical scaring; + * it doesn't care about scrolls or engravings or dungeon branch */ + boolean auditory_scare = (x == 0 && y == 0), + magical_scare = !auditory_scare; - /* creatures who are directly resistant to magical scaring: - * humans aren't monsters - * uniques have ascended their base monster instincts - * Rodney, lawful minions, Angels, the Riders, shopkeepers - * inside their own shop, priests inside their own temple */ + /* creatures who are directly resistant to any type of scaring: + * Rodney, lawful minions, Angels, the Riders */ if (mtmp->iswiz || is_lminion(mtmp) || mtmp->data == &mons[PM_ANGEL] - || is_rider(mtmp->data) - || mtmp->data->mlet == S_HUMAN || unique_corpstat(mtmp->data) - || (mtmp->isshk && inhishop(mtmp)) + || is_rider(mtmp->data)) + return FALSE; + + /* creatures who are directly resistant to magical scaring + * based on the mere presence of something at a location: + * humans etc. + * uniques have ascended their base monster instincts */ + if (magical_scare + && (mtmp->data->mlet == S_HUMAN || unique_corpstat(mtmp->data))) + return FALSE; + + /* creatues who resist scaring under particular circumstances: + * shopkeepers inside their own shop + * priests inside their own temple */ + if ((mtmp->isshk && inhishop(mtmp)) || (mtmp->ispriest && inhistemple(mtmp))) return FALSE; - /* <0,0> is used by musical scaring to check for the above; - * it doesn't care about scrolls or engravings or dungeon branch */ - if (x == 0 && y == 0) + if (auditory_scare) return TRUE; /* should this still be true for defiled/molochian altars? */ @@ -286,7 +297,7 @@ onscary(coordxy x, coordxy y, struct monst *mtmp) || (Displaced && mtmp->mux == x && mtmp->muy == y) || (ep->guardobjects && vobj_at(x, y))) && !(mtmp->isshk || mtmp->isgd || !mtmp->mcansee - || mtmp->mpeaceful || mtmp->data->mlet == S_HUMAN + || mtmp->mpeaceful || mtmp->data == &mons[PM_MINOTAUR] || Inhell || In_endgame(&u.uz))); } @@ -1058,9 +1069,7 @@ itsstuck(struct monst *mtmp) boolean should_displace( struct monst *mtmp, - coord *poss, /* coord poss[9] */ - long *info, /* long info[9] */ - int cnt, + const struct mfndposdata *data, coordxy ggx, coordxy ggy) { @@ -1070,11 +1079,11 @@ should_displace( int i, nx, ny; int ndist; - for (i = 0; i < cnt; i++) { - nx = poss[i].x; - ny = poss[i].y; + for (i = 0; i < data->cnt; i++) { + nx = data->poss[i].x; + ny = data->poss[i].y; ndist = dist2(nx, ny, ggx, ggy); - if (MON_AT(nx, ny) && (info[i] & ALLOW_MDISP) && !(info[i] & ALLOW_M) + if (MON_AT(nx, ny) && (data->info[i] & ALLOW_MDISP) && !(data->info[i] & ALLOW_M) && !undesirable_disp(mtmp, nx, ny)) { if (shortest_with_displacing == -1 || (ndist < shortest_with_displacing)) @@ -1715,7 +1724,7 @@ m_move(struct monst *mtmp, int after) struct permonst *ptr; int chi, mmoved = MMOVE_NOTHING, /* not strictly nec.: chi >= 0 will do */ preferredrange_min = 0, preferredrange_max = 0; - long info[9]; + struct mfndposdata mfp; long flag; coordxy omx = mtmp->mx, omy = mtmp->my; @@ -1785,11 +1794,10 @@ m_move(struct monst *mtmp, int after) if (covetousattack & M_ATTK_AGR_DIED) return MMOVE_DIED; mmoved = MMOVE_MOVED; - } else { - mmoved = MMOVE_NOTHING; + return postmov(mtmp, ptr, omx, omy, mmoved, + seenflgs, can_tunnel, can_unlock, can_open); } - return postmov(mtmp, ptr, omx, omy, mmoved, - seenflgs, can_tunnel, can_unlock, can_open); + /* otherwise continue with normal AI routine */ } /* likewise for shopkeeper, guard, or priest */ @@ -1911,9 +1919,8 @@ m_move(struct monst *mtmp, int after) int jcnt, cnt; int ndist, nidist; coord *mtrk; - coord poss[9]; - cnt = mfndpos(mtmp, poss, info, flag); + cnt = mfndpos(mtmp, &mfp, flag); if (cnt == 0 && !is_unicorn(mtmp->data)) { if (find_defensive(mtmp, TRUE) && use_defensive(mtmp)) return MMOVE_DONE; @@ -1930,22 +1937,22 @@ m_move(struct monst *mtmp, int after) if (is_unicorn(ptr) && noteleport_level(mtmp)) { /* on noteleport levels, perhaps we cannot avoid hero */ for (i = 0; i < cnt; i++) - if (!(info[i] & NOTONL)) + if (!(mfp.info[i] & NOTONL)) avoid = TRUE; } better_with_displacing = - should_displace(mtmp, poss, info, cnt, ggx, ggy); + should_displace(mtmp, &mfp, ggx, ggy); for (i = 0; i < cnt; i++) { - if (avoid && (info[i] & NOTONL)) + if (avoid && (mfp.info[i] & NOTONL)) continue; - nx = poss[i].x; - ny = poss[i].y; + nx = mfp.poss[i].x; + ny = mfp.poss[i].y; if (m_avoid_kicked_loc(mtmp, nx, ny)) continue; - if (MON_AT(nx, ny) && (info[i] & ALLOW_MDISP) - && !(info[i] & ALLOW_M) && !better_with_displacing) + if (MON_AT(nx, ny) && (mfp.info[i] & ALLOW_MDISP) + && !(mfp.info[i] & ALLOW_M) && !better_with_displacing) continue; if (appr != 0) { mtrk = &mtmp->mtrack[0]; @@ -1993,8 +2000,8 @@ m_move(struct monst *mtmp, int after) * mfndpos) has no effect for normal attacks, though it lets a * confused monster attack you by accident. */ - assert(IndexOk(chi, info)); - if (info[chi] & ALLOW_U) { + assert(IndexOk(chi, mfp.info)); + if (mfp.info[chi] & ALLOW_U) { nix = mtmp->mux; niy = mtmp->muy; } @@ -2009,11 +2016,11 @@ m_move(struct monst *mtmp, int after) * Pets get taken care of above and shouldn't reach this code. * Conflict gets handled even farther away (movemon()). */ - if ((info[chi] & ALLOW_M) != 0 + if ((mfp.info[chi] & ALLOW_M) != 0 || (nix == mtmp->mux && niy == mtmp->muy)) return m_move_aggress(mtmp, nix, niy); - if ((info[chi] & ALLOW_MDISP) != 0) { + if ((mfp.info[chi] & ALLOW_MDISP) != 0) { struct monst *mtmp2; int mstatus; @@ -2030,7 +2037,7 @@ m_move(struct monst *mtmp, int after) if (!m_in_out_region(mtmp, nix, niy)) return MMOVE_DONE; - if ((info[chi] & ALLOW_ROCK) && m_can_break_boulder(mtmp)) { + if ((mfp.info[chi] & ALLOW_ROCK) && m_can_break_boulder(mtmp)) { (void) m_break_boulder(mtmp, nix, niy); return MMOVE_DONE; } diff --git a/src/mthrowu.c b/src/mthrowu.c index 259484698..24001738a 100644 --- a/src/mthrowu.c +++ b/src/mthrowu.c @@ -8,6 +8,7 @@ staticfn int monmulti(struct monst *, struct obj *, struct obj *); staticfn void monshoot(struct monst *, struct obj *, struct obj *); staticfn boolean ucatchgem(struct obj *, struct monst *); +staticfn boolean u_catch_thrown_obj(struct obj *); staticfn const char *breathwep_name(int); staticfn boolean drop_throw(struct obj *, boolean, coordxy, coordxy); staticfn boolean blocking_terrain(coordxy, coordxy); @@ -334,7 +335,7 @@ ohitmon( ismimic = M_AP_TYPE(mtmp) && M_AP_TYPE(mtmp) != M_AP_MONSTER; vis = cansee(gb.bhitpos.x, gb.bhitpos.y); if (vis) - otmp->dknown = 1; + observe_object(otmp); tmp = 5 + find_mac(mtmp) + omon_adj(mtmp, otmp, FALSE); /* High level monsters will be more likely to hit */ @@ -527,6 +528,27 @@ ucatchgem( return FALSE; } +/* hero may catch thrown obj. it is added to inventory, if possible */ +staticfn boolean +u_catch_thrown_obj(struct obj *otmp) +{ + int catch_chance = 100 - ACURR(A_DEX) + - ((Role_if(PM_MONK) || Role_if(PM_ROGUE)) ? 20 : 0); + + if (!Blind && !Confusion && !Stunned && !Fumbling + && otmp->oclass != VENOM_CLASS + && !nohands(gy.youmonst.data) && freehand() + && calc_capacity(otmp->owt) <= SLT_ENCUMBER && !rn2(catch_chance)) { + char buf[BUFSZ]; + + Snprintf(buf, BUFSZ, "You catch the %s!", simpleonames(otmp)); + (void) hold_another_object(otmp, "You catch, but drop, the %s.", + simpleonames(otmp), buf); + return TRUE; + } + return FALSE; +} + #define MT_FLIGHTCHECK(pre,forcehit) \ (/* missile hits edge of screen */ \ !isok(gb.bhitpos.x + dx, gb.bhitpos.y + dy) \ @@ -652,7 +674,7 @@ m_throw( singleobj->ox = gb.bhitpos.x += dx; singleobj->oy = gb.bhitpos.y += dy; if (cansee(gb.bhitpos.x, gb.bhitpos.y)) - singleobj->dknown = 1; + observe_object(singleobj); mtmp = m_at(gb.bhitpos.x, gb.bhitpos.y); if (mtmp && shade_miss(mon, mtmp, singleobj, TRUE, TRUE)) { @@ -666,13 +688,16 @@ m_throw( if (gm.multi) nomul(0); + /* hero might be poly'd into a unicorn */ + if (singleobj->oclass == GEM_CLASS && ucatchgem(singleobj, mon)) + break; + + if (!tethered_weapon && u_catch_thrown_obj(singleobj)) + break; + if (singleobj->oclass == POTION_CLASS) { potionhit(&gy.youmonst, singleobj, POTHIT_MONST_THROW); break; - } else if (singleobj->oclass == GEM_CLASS) { - /* hero might be poly'd into a unicorn */ - if (ucatchgem(singleobj, mon)) - break; } oldumort = u.umortality; @@ -698,8 +723,9 @@ m_throw( hitv = 3 - distmin(u.ux, u.uy, mon->mx, mon->my); if (hitv < -4) hitv = -4; + /* [elves get a shooting bonus, orcs don't...] */ if (is_elf(mon->data) - && objects[singleobj->otyp].oc_skill == P_BOW) { + && objects[singleobj->otyp].oc_skill == -P_BOW) { hitv++; if (MON_WEP(mon) && MON_WEP(mon)->otyp == ELVEN_BOW) hitv++; @@ -1093,7 +1119,8 @@ breamm(struct monst *mtmp, struct attack *mattk, struct monst *mtarg) Monnam(mtmp), breathwep_name(typ)); gb.buzzer = mtmp; dobuzz(BZ_M_BREATH(BZ_OFS_AD(typ)), (int) mattk->damn, - mtmp->mx, mtmp->my, sgn(gt.tbx), sgn(gt.tby), utarget); + mtmp->mx, mtmp->my, sgn(gt.tbx), sgn(gt.tby), + utarget, utarget, FALSE); gb.buzzer = 0; nomul(0); /* breath runs out sometimes. Also, give monster some @@ -1416,7 +1443,7 @@ hit_bars( } } else { if (!Deaf) { - static enum sound_effect_entries se[] SOUNDLIBONLY = { + static enum sound_effect_entries se[] = { se_zero_invalid, se_bars_whang, se_bars_whap, se_bars_flapp, se_bars_clink, se_bars_clonk @@ -1436,6 +1463,7 @@ hit_bars( Soundeffect(se[bsindx], 100); pline("%s!", barsounds[bsindx]); + nhUse(se[bsindx]); } if (!(harmless_missile(otmp) || is_flimsy(otmp))) noise = 4 * 4; diff --git a/src/muse.c b/src/muse.c index 42243c832..2d5e82bd4 100644 --- a/src/muse.c +++ b/src/muse.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 muse.c $NHDT-Date: 1737392015 2025/01/20 08:53:35 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.234 $ */ +/* NetHack 3.7 muse.c $NHDT-Date: 1770949988 2026/02/12 18:33:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.241 $ */ /* Copyright (C) 1990 by Ken Arromdee */ /* NetHack may be freely redistributed. See license for details. */ @@ -29,7 +29,9 @@ staticfn boolean linedup_chk_corpse(coordxy, coordxy); staticfn void m_use_undead_turning(struct monst *, struct obj *); staticfn boolean hero_behind_chokepoint(struct monst *); staticfn boolean mon_has_friends(struct monst *); +staticfn boolean mon_likes_objpile_at(struct monst *mtmp, coordxy x, coordxy y) NONNULLARG1; staticfn int mbhitm(struct monst *, struct obj *); +staticfn void buzz_force_miss(int, int, coordxy, coordxy, int, int); staticfn boolean fhito_loc(struct obj *obj, coordxy x, coordxy y, int (*fhito)(OBJ_P, OBJ_P)); staticfn void mbhit(struct monst *, int, int (*)(MONST_P, OBJ_P), @@ -207,7 +209,7 @@ mplayhorn( ? "nearby" : "in the distance"); unknow_object(otmp); /* hero loses info when unseen obj is used */ } else if (self) { - otmp->dknown = 1; + observe_object(otmp); objnamp = xname(otmp); if (strlen(objnamp) >= QBUFSZ) objnamp = simpleonames(otmp); @@ -216,7 +218,7 @@ mplayhorn( pline("%s!", monverbself(mtmp, Monnam(mtmp), "play", objbuf)); makeknown(otmp->otyp); /* (wands handle this slightly differently) */ } else { - otmp->dknown = 1; + observe_object(otmp); objnamp = xname(otmp); if (strlen(objnamp) >= QBUFSZ) objnamp = simpleonames(otmp); @@ -242,7 +244,7 @@ mreadmsg(struct monst *mtmp, struct obj *otmp) if (!vismon && Deaf) return; /* no feedback */ - otmp->dknown = 1; /* seeing or hearing scroll read reveals its label */ + observe_object(otmp); /* seeing/hearing scroll read reveals its label */ Strcpy(onambuf, singular(otmp, vismon ? doname : ansimpleoname)); if (vismon) { @@ -291,7 +293,7 @@ staticfn void mquaffmsg(struct monst *mtmp, struct obj *otmp) { if (canseemon(mtmp)) { - otmp->dknown = 1; + observe_object(otmp); pline_mon(mtmp, "%s drinks %s!", Monnam(mtmp), singular(otmp, doname)); } else if (!Deaf) { Soundeffect(se_mon_chugging_potion, 25); @@ -1347,14 +1349,14 @@ hero_behind_chokepoint(struct monst *mtmp) coordxy x = mtmp->mux + dx; coordxy y = mtmp->muy + dy; - int dir = xytod(dx, dy); + int dir = xytodir(dx, dy); int dir_l = DIR_CLAMP(DIR_LEFT2(dir)); int dir_r = DIR_CLAMP(DIR_RIGHT2(dir)); coord c1, c2; - dtoxy(&c1, dir_l); - dtoxy(&c2, dir_r); + dirtocoord(&c1, dir_l); + dirtocoord(&c2, dir_r); c1.x += x, c2.x += x; c1.y += y, c2.y += y; @@ -1388,6 +1390,30 @@ mon_has_friends(struct monst *mtmp) return FALSE; } +/* does monster like object pile at x,y? */ +staticfn boolean +mon_likes_objpile_at(struct monst *mtmp, coordxy x, coordxy y) +{ + int i; + struct obj *otmp; + + if (!isok(x,y) || !OBJ_AT(x,y)) + return FALSE; + + /* monster likes any of the top 3 items in the pile? */ + for (i = 0, otmp = svl.level.objects[x][y]; otmp && i < 3; i++) { + if (mon_would_take_item(mtmp, otmp)) + return TRUE; + otmp = otmp->nexthere; + } + + /* pile is larger than 3 stacks? */ + if (i >= 3) + return TRUE; + + return FALSE; +} + /* Select an offensive item/action for a monster. Returns TRUE iff one is * found. */ @@ -1488,6 +1514,7 @@ find_offensive(struct monst *mtmp) /* do try to move hero to a more vulnerable spot */ && (onscary(u.ux, u.uy, mtmp) || (hero_behind_chokepoint(mtmp) && mon_has_friends(mtmp)) + || mon_likes_objpile_at(mtmp, u.ux, u.uy) || stairway_at(u.ux, u.uy))) { gm.m.offensive = obj; gm.m.has_offense = MUSE_WAN_TELEPORTATION; @@ -1588,7 +1615,8 @@ mbhitm(struct monst *mtmp, struct obj *otmp) Soundeffect(se_boing, 40); pline("Boing!"); learnit = TRUE; - } else if (rnd(20) < 10 + u.uac) { + } else if (rnd(20) < 10 + u.uac && + !(gb.buzzer && !gb.buzzer->mwandexp)) { monstunseesu(M_SEEN_MAGR); /* mons see hero not resisting */ pline_The("wand hits you!"); tmp = d(2, 12); @@ -1783,6 +1811,12 @@ mbhit( } } +staticfn void +buzz_force_miss(int type, int nd, coordxy sx, coordxy sy, int dx, int dy) +{ + dobuzz(type, nd, sx, sy, dx, dy, TRUE, FALSE, TRUE); +} + /* Perform an offensive action for a monster. Must be called immediately * after find_offensive(). Return values are same as use_defensive(). */ @@ -1793,6 +1827,12 @@ use_offensive(struct monst *mtmp) struct obj *otmp = gm.m.offensive; boolean oseen; + /* if a monster has never used an attack wand before, it takes them some + time to get used to holding that much power, so the first shot always + misses */ + void (*buzzfn)(int, int, coordxy, coordxy, int, int) = + mtmp->mwandexp ? buzz : buzz_force_miss; + /* offensive potions are not drunk, they're thrown */ if (otmp->oclass != POTION_CLASS && (i = precheck(mtmp, otmp)) != 0) return i; @@ -1811,12 +1851,13 @@ use_offensive(struct monst *mtmp) gm.m_using = TRUE; gc.current_wand = otmp; gb.buzzer = mtmp; - buzz(BZ_M_WAND(BZ_OFS_WAN(otmp->otyp)), - (otmp->otyp == WAN_MAGIC_MISSILE) ? 2 : 6, mtmp->mx, mtmp->my, - sgn(mtmp->mux - mtmp->mx), sgn(mtmp->muy - mtmp->my)); + buzzfn(BZ_M_WAND(BZ_OFS_WAN(otmp->otyp)), + (otmp->otyp == WAN_MAGIC_MISSILE) ? 2 : 6, mtmp->mx, mtmp->my, + sgn(mtmp->mux - mtmp->mx), sgn(mtmp->muy - mtmp->my)); gb.buzzer = 0; gc.current_wand = 0; gm.m_using = FALSE; + mtmp->mwandexp = TRUE; return (DEADMONSTER(mtmp)) ? 1 : 2; case MUSE_FIRE_HORN: case MUSE_FROST_HORN: @@ -1824,13 +1865,14 @@ use_offensive(struct monst *mtmp) gm.m_using = TRUE; gb.buzzer = mtmp; gc.current_wand = otmp; /* needed by zhitu() */ - buzz(BZ_M_WAND(BZ_OFS_AD((otmp->otyp == FROST_HORN) ? AD_COLD - : AD_FIRE)), + buzzfn(BZ_M_WAND(BZ_OFS_AD( + (otmp->otyp == FROST_HORN) ? AD_COLD : AD_FIRE)), rn1(6, 6), mtmp->mx, mtmp->my, sgn(mtmp->mux - mtmp->mx), sgn(mtmp->muy - mtmp->my)); gb.buzzer = 0; gc.current_wand = 0; gm.m_using = FALSE; + mtmp->mwandexp = TRUE; return (DEADMONSTER(mtmp)) ? 1 : 2; case MUSE_WAN_TELEPORTATION: case MUSE_WAN_UNDEAD_TURNING: @@ -1838,9 +1880,13 @@ use_offensive(struct monst *mtmp) gz.zap_oseen = oseen; mzapwand(mtmp, otmp, FALSE); gm.m_using = TRUE; + gb.buzzer = mtmp; mbhit(mtmp, rn1(8, 6), mbhitm, bhito, otmp); + gb.buzzer = 0; /* note: 'otmp' might have been destroyed (drawbridge destruction) */ gm.m_using = FALSE; + if (gm.m.has_offense == MUSE_WAN_STRIKING) + mtmp->mwandexp = TRUE; return 2; case MUSE_SCR_EARTH: { /* TODO: handle steeds */ @@ -1967,7 +2013,7 @@ use_offensive(struct monst *mtmp) * are not objects. Also set dknown in mthrowu.c. */ if (cansee(mtmp->mx, mtmp->my)) { - otmp->dknown = 1; + observe_object(otmp); pline_mon(mtmp, "%s hurls %s!", Monnam(mtmp), singular(otmp, doname)); } @@ -2400,7 +2446,7 @@ use_misc(struct monst *mtmp) mquaffmsg(mtmp, otmp); /* format monster's name before altering its visibility */ Strcpy(nambuf, mon_nam(mtmp)); - mon_set_minvis(mtmp); + mon_set_minvis(mtmp, !otmp->cursed ? FALSE : TRUE); if (vismon && mtmp->minvis) { /* was seen, now invisible */ if (canspotmon(mtmp)) { pline("%s body takes on a %s transparency.", @@ -2413,6 +2459,17 @@ use_misc(struct monst *mtmp) } if (oseen) makeknown(otmp->otyp); + } else if (vismon && !mtmp->minvis) { + /* cursed potion; mon tried to make itself invisible but failed */ + pline("%s briefly seems to be transparent.", Monnam(mtmp)); + /* we could call map_invisible() before the pline(), then + newsym() after; unseen monster glyph would be visible during + the pline, but hero would forget any remembered object under + the monster */ + } else if (!vismon && canseemon(mtmp)) { + /* cursed potion; this won't happen because a monster will only + drink a potion of invisibility when not already invisible */ + pline("%s suddenly appears!", Monnam(mtmp)); } if (otmp->otyp == POT_INVISIBILITY) { if (otmp->cursed) @@ -2494,7 +2551,7 @@ use_misc(struct monst *mtmp) int where_to = rn2(4); struct obj *obj = uwep; const char *hand; - char the_weapon[BUFSZ]; + char the_weapon[BUFSZ], hand_buf[BUFSZ]; if (!obj || !canletgo(obj, "") || (u.twoweap && canletgo(uswapwep, "") && rn2(2))) @@ -2506,10 +2563,12 @@ use_misc(struct monst *mtmp) hand = body_part(HAND); if (bimanual(obj)) hand = makeplural(hand); + (void) strncpy(hand_buf, hand, sizeof hand_buf - 1); + hand_buf[sizeof hand_buf - 1] = '\0'; if (vismon) pline_mon(mtmp, "%s flicks a bullwhip towards your %s!", - Monnam(mtmp), hand); + Monnam(mtmp), hand_buf); if (obj->otyp == HEAVY_IRON_BALL) { pline("%s fails to wrap around %s.", The_whip, the_weapon); return 1; @@ -2518,7 +2577,7 @@ use_misc(struct monst *mtmp) the_weapon); if (welded(obj)) { pline("%s welded to your %s%c", - !is_plural(obj) ? "It is" : "They are", hand, + !is_plural(obj) ? "It is" : "They are", hand_buf, !obj->bknown ? '!' : '.'); /* obj->bknown = 1; */ /* welded() takes care of this */ where_to = 0; @@ -2537,7 +2596,7 @@ use_misc(struct monst *mtmp) switch (where_to) { case 1: /* onto floor beneath mon */ pline_mon(mtmp, "%s yanks %s from your %s!", Monnam(mtmp), - the_weapon, hand); + the_weapon, hand_buf); place_object(obj, mtmp->mx, mtmp->my); break; case 2: /* onto floor beneath you */ @@ -3129,7 +3188,7 @@ muse_unslime( vis |= canseemon(mon); /* burning potion may improve visibility */ if (vis) { if (!Unaware) - obj->dknown = 1; /* hero is watching mon drink obj */ + observe_object(obj); /* hero is watching mon drink obj */ pline("%s quaffs a burning %s", saw_lit ? upstart(strcpy(Pronoun, mhe(mon))) : Monnam(mon), simpleonames(obj)); diff --git a/src/nhlobj.c b/src/nhlobj.c index e06fc4b59..a0535074c 100644 --- a/src/nhlobj.c +++ b/src/nhlobj.c @@ -210,7 +210,7 @@ l_obj_objects_to_table(lua_State *L) nhl_add_table_entry_int(L, "name_known", o->oc_name_known); nhl_add_table_entry_int(L, "merge", o->oc_merge); nhl_add_table_entry_int(L, "uses_known", o->oc_uses_known); - nhl_add_table_entry_int(L, "pre_discovered", o->oc_pre_discovered); + nhl_add_table_entry_int(L, "encountered", o->oc_encountered); nhl_add_table_entry_int(L, "magic", o->oc_magic); nhl_add_table_entry_int(L, "charged", o->oc_charged); nhl_add_table_entry_int(L, "unique", o->oc_unique); @@ -345,12 +345,13 @@ DISABLE_WARNING_UNREACHABLE_CODE /* create a new object via wishing routine */ /* local o = obj.new("rock"); */ +/* local o = obj.new({ id = "food ration", class = "%" }); */ staticfn int l_obj_new_readobjnam(lua_State *L) { int argc = lua_gettop(L); - if (argc == 1) { + if (argc == 1 && lua_type(L, 1) == LUA_TSTRING) { char buf[BUFSZ]; struct obj *otmp; @@ -360,6 +361,22 @@ l_obj_new_readobjnam(lua_State *L) otmp = NULL; (void) l_obj_push(L, otmp); return 1; + } else if (argc == 1 && lua_type(L, 1) == LUA_TTABLE) { + short id = get_table_objtype(L); + xint16 class = get_table_objclass(L); + struct obj *otmp; + + if (id >= FIRST_OBJECT) { + otmp = mksobj(id, TRUE, FALSE); + } else { + class = def_char_to_objclass(class); + if (class >= MAXOCLASSES) + class = RANDOM_CLASS; + otmp = mkobj(class, FALSE); + } + lua_pop(L, 1); + (void) l_obj_push(L, otmp); + return 1; } else nhl_error(L, "l_obj_new_readobjname: Wrong args"); /*NOTREACHED*/ diff --git a/src/nhlsel.c b/src/nhlsel.c index 306820cf9..9b5ef24c4 100644 --- a/src/nhlsel.c +++ b/src/nhlsel.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 nhlua.c $NHDT-Date: 1737545957 2025/01/22 03:39:17 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.64 $ */ +/* NetHack 3.7 nhlua.c $NHDT-Date: 1769840272 2026/01/30 22:17:52 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.69 $ */ /* Copyright (c) 2018 by Pasi Kallinen */ /* NetHack may be freely redistributed. See license for details. */ @@ -33,6 +33,7 @@ staticfn int l_selection_circle(lua_State *); staticfn int l_selection_ellipse(lua_State *); staticfn int l_selection_gradient(lua_State *); staticfn int l_selection_iterate(lua_State *); +staticfn int l_selection_size_description(lua_State *L); staticfn int l_selection_gc(lua_State *); staticfn int l_selection_not(lua_State *); staticfn int l_selection_and(lua_State *); @@ -709,6 +710,10 @@ l_selection_match(lua_State *L) for (x = 1; x < sel->wid; x++) selection_setpoint(x, y, sel, mapfrag_match(mf, x,y) ? 1 : 0); + /* unless the (0, 1) coordinate is a match, this would wind up with a + selection with lx=COLNO, hx=0, etc, so fix the boundaries */ + selection_recalc_bounds(sel); + mapfrag_free(&mf); return 1; @@ -909,6 +914,8 @@ l_selection_gradient(lua_State *L) return 1; } +DISABLE_WARNING_UNREACHABLE_CODE + /* sel:iterate(function(x,y) ... end); * The x, y coordinates passed to the function are map- or room-relative * rather than absolute, unless there has been no previous map or room @@ -949,6 +956,27 @@ l_selection_iterate(lua_State *L) return 0; } +/* local txt = sel:describe_size(); */ +/* gives a textual description of the selection size */ +staticfn int +l_selection_size_description(lua_State *L) +{ + int argc = lua_gettop(L); + + if (argc == 1) { + struct selectionvar *sel = l_selection_check(L, 1); + char buf[BUFSZ]; + + lua_pushstring(L, selection_size_description(sel, buf)); + return 1; + } else { + nhl_error(L, "wrong parameters"); + /*NOTREACHED*/ + } + return 0; +} + +RESTORE_WARNING_UNREACHABLE_CODE static const struct luaL_Reg l_selection_methods[] = { { "new", l_selection_new }, @@ -974,6 +1002,7 @@ static const struct luaL_Reg l_selection_methods[] = { { "iterate", l_selection_iterate }, { "bounds", l_selection_getbounds }, { "room", l_selection_room }, + { "describe_size", l_selection_size_description }, { NULL, NULL } }; diff --git a/src/nhlua.c b/src/nhlua.c index b43e4c089..96970f73c 100644 --- a/src/nhlua.c +++ b/src/nhlua.c @@ -31,10 +31,13 @@ struct e; staticfn int nhl_dump_fmtstr(lua_State *); #endif /* DUMPLOG */ staticfn int nhl_dnum_name(lua_State *); +staticfn int nhl_int_to_pm_name(lua_State *); +staticfn int nhl_int_to_obj_name(lua_State *); staticfn int nhl_stairways(lua_State *); staticfn int nhl_pushkey(lua_State *); staticfn int nhl_doturn(lua_State *); staticfn int nhl_debug_flags(lua_State *); +staticfn int nhl_flip_level(lua_State *); staticfn int nhl_timer_has_at(lua_State *); staticfn int nhl_timer_peek_at(lua_State *); staticfn int nhl_timer_stop_at(lua_State *); @@ -1163,6 +1166,51 @@ nhl_dnum_name(lua_State *L) return 1; } +/* return gender-neutral monster type name by integer value, + or empty string if outside LOW_PM - HIGH_PM range */ +/* local montypename = int_to_pmname(12); */ +staticfn int +nhl_int_to_pm_name(lua_State *L) +{ + int argc = lua_gettop(L); + + if (argc == 1) { + lua_Integer i = luaL_checkinteger(L, 1); + + if (i >= LOW_PM && i <= HIGH_PM) + lua_pushstring(L, mons[i].pmnames[NEUTRAL]); + else + lua_pushstring(L, ""); + } else + nhl_error(L, "Expected an integer parameter"); + return 1; +} + +/* convert integer to object type name and class */ +/* local oname,oclass = int_to_objname(25); */ +staticfn int +nhl_int_to_obj_name(lua_State *L) +{ + int argc = lua_gettop(L); + + if (argc == 1) { + char buf[8]; + lua_Integer i = luaL_checkinteger(L, 1); + + if (i >= 0 && i < NUM_OBJECTS && OBJ_NAME(objects[i])) { + lua_pushstring(L, OBJ_NAME(objects[i])); + buf[0] = def_oc_syms[(int)objects[i].oc_class].sym; + buf[1] = '\0'; + lua_pushstring(L, buf); + } else { + lua_pushstring(L, ""); + lua_pushstring(L, ""); + } + } else + nhl_error(L, "Expected an integer parameter"); + return 2; +} + DISABLE_WARNING_UNREACHABLE_CODE /* because nhl_error() does not return */ @@ -1380,7 +1428,10 @@ nhl_pushkey(lua_State *L) if (argc == 1) { const char *key = luaL_checkstring(L, 1); - cmdq_add_key(CQ_CANNED, key[0]); + while (*key) { + cmdq_add_key(CQ_CANNED, *key); + key++; + } } return 0; @@ -1443,6 +1494,28 @@ nhl_debug_flags(lua_State *L) iflags.debug_overwrite_stairs = (boolean) val; } + /* prevent pline going out to the UI */ + val = get_table_boolean_opt(L, "prevent_pline", -1); + if (val != -1) { + iflags.debug_prevent_pline = (boolean) val; + } + + return 0; +} + +/* flip level */ +/* nh.flip_level(n); */ +staticfn int +nhl_flip_level(lua_State *L) +{ + int argc = lua_gettop(L); + int flp = 0; + + if (argc == 1) + flp = lua_tointeger(L, 1); + + flip_level(flp, !gi.in_mklev); + return 0; } @@ -1816,11 +1889,14 @@ static const struct luaL_Reg nhl_functions[] = { { "dump_fmtstr", nhl_dump_fmtstr }, #endif /* DUMPLOG */ { "dnum_name", nhl_dnum_name }, + { "int_to_pmname", nhl_int_to_pm_name }, + { "int_to_objname", nhl_int_to_obj_name }, { "variable", nhl_variable }, { "stairways", nhl_stairways }, { "pushkey", nhl_pushkey }, { "doturn", nhl_doturn }, { "debug_flags", nhl_debug_flags }, + { "flip_level", nhl_flip_level }, { NULL, NULL } }; @@ -1830,6 +1906,11 @@ static const struct { } nhl_consts[] = { { "COLNO", COLNO }, { "ROWNO", ROWNO }, + { "NUMMONS", NUMMONS }, + { "LOW_PM", LOW_PM }, + { "HIGH_PM", HIGH_PM }, + { "FIRST_OBJECT", FIRST_OBJECT }, + { "LAST_OBJECT", NUM_OBJECTS-1 }, #ifdef DLB { "DLB", 1 }, #else @@ -2216,7 +2297,11 @@ nhl_init(nhl_sandbox_info *sbi) /* It would be nice to import EXPECTED from each build system. XXX */ /* And it would be nice to do it only once, but it's cheap. */ #ifndef NHL_VERSION_EXPECTED -#define NHL_VERSION_EXPECTED 50406 +#if LUA_VERSION_NUM >= 505 +#define NHL_VERSION_EXPECTED 50500 +#else +#define NHL_VERSION_EXPECTED 50408 +#endif #endif #ifdef NHL_SANDBOX @@ -2978,7 +3063,11 @@ nhlL_newstate(nhl_sandbox_info *sbi, const char *name) nud->sid = ++gl.lua_sid; } - lua_State *L = lua_newstate(nhl_alloc, nud); + lua_State *L = lua_newstate(nhl_alloc, nud +#if LUA_VERSION_NUM >= 505 + , 0 +#endif + ); if (!L) panic("NULL lua_newstate"); diff --git a/src/o_init.c b/src/o_init.c index 1185c177f..5bb82d2e2 100644 --- a/src/o_init.c +++ b/src/o_init.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 o_init.c $NHDT-Date: 1756520041 2025/08/29 18:14:01 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.96 $ */ +/* NetHack 3.7 o_init.c $NHDT-Date: 1771216675 2026/02/15 20:37:55 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.101 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -14,6 +14,7 @@ staticfn int QSORTCALLBACK discovered_cmp(const genericptr, const genericptr); staticfn char *sortloot_descr(int, char *); staticfn char *disco_typename(int); staticfn void disco_append_typename(char *, int); +staticfn void disco_fmt_uniq(int, char *outbuf) NONNULLARG2; staticfn void disco_output_sorted(winid, char **, int, boolean); staticfn char *oclass_to_name(char, char *); @@ -194,7 +195,7 @@ init_objects(void) bases[class] through bases[class+1]-1 for all classes (except for ILLOBJ_CLASS which is separated from WEAPON_CLASS by generic objects); second extra entry is to prevent an - explained crash in doclassdisco(), where the code ended up + unexplained crash in doclassdisco(), where the code ended up attempting to process non-existent class MAXOCLASSES; the [MAXOCLASSES+1] element gives that non-class 0 objects when traversing objects[] from bases[X] through bases[X+1]-1 */ @@ -377,7 +378,7 @@ savenames(NHFILE *nhfp) unsigned int len; if (update_file(nhfp)) { - for (i = 0; i < (MAXOCLASSES + 2); ++i) { + for (i = 0; i < (MAXOCLASSES + 2); ++i) { Sfo_int(nhfp, &svb.bases[i], "names-bases"); } for (i = 0; i < NUM_OBJECTS; ++i) { @@ -436,41 +437,58 @@ restnames(NHFILE *nhfp) } #ifndef SFCTOOL +/* make the object dknown and mark it as encountered */ +void +observe_object(struct obj *obj) +{ + int oindx = obj->otyp; + + /* skip for generic objects and for STRANGE_OBJECT */ + if (oindx >= FIRST_OBJECT && !Hallucination) { + obj->dknown = 1; + discover_object(oindx, FALSE, TRUE, FALSE); + } +} + void discover_object( - int oindx, - boolean mark_as_known, - boolean credit_hero) + int oindx, /* type of object */ + boolean mark_as_known, /* discover the type */ + boolean mark_as_encountered, /* mark the type as having been seen/felt */ + boolean credit_hero) /* exercise wisdom */ { - if (!objects[oindx].oc_name_known + if (oindx < FIRST_OBJECT) /* don't discover generic objects */ + return; + + if ((!objects[oindx].oc_name_known && mark_as_known) + || (!objects[oindx].oc_encountered && mark_as_encountered) || (Role_if(PM_SAMURAI) && Japanese_item_name(oindx, (const char *) 0))) { int dindx, acls = objects[oindx].oc_class; /* Loop thru disco[] 'til we find the target (which may have been uname'd) or the next open slot; one or the other will be found - before we reach the next class... - */ + before we reach the next class... */ for (dindx = svb.bases[acls]; svd.disco[dindx] != 0; dindx++) if (svd.disco[dindx] == oindx) break; svd.disco[dindx] = oindx; - /* if already known, we forced an item with a Japanese name into - disco[] but don't want to exercise wisdom or update perminv */ - if (objects[oindx].oc_name_known) - return; + if (mark_as_encountered) + objects[oindx].oc_encountered = 1; - if (mark_as_known) { + if (!objects[oindx].oc_name_known && mark_as_known) { objects[oindx].oc_name_known = 1; if (credit_hero) exercise(A_WIS, TRUE); - } - /* !in_moveloop => initial inventory, gameover => final disclosure */ - if (program_state.in_moveloop && !program_state.gameover) { - if (objects[oindx].oc_class == GEM_CLASS) - gem_learned(oindx); /* could affect price of unpaid gems */ - update_inventory(); + + /* !in_moveloop => initial inventory, + gameover => final disclosure */ + if (program_state.in_moveloop && !program_state.gameover) { + if (objects[oindx].oc_class == GEM_CLASS) + gem_learned(oindx); /* could affect price of unpaid gems */ + update_inventory(); + } } } } @@ -479,7 +497,7 @@ discover_object( void undiscover_object(int oindx) { - if (!objects[oindx].oc_name_known) { + if (!objects[oindx].oc_name_known && !objects[oindx].oc_encountered) { int dindx, acls = objects[oindx].oc_class; boolean found = FALSE; @@ -501,7 +519,6 @@ undiscover_object(int oindx) if (objects[oindx].oc_class == GEM_CLASS) gem_learned(oindx); /* ok, it's actually been unlearned */ - update_inventory(); } } @@ -514,16 +531,21 @@ interesting_to_discover(int i) if (Role_if(PM_SAMURAI) && Japanese_item_name(i, (const char *) 0)) return TRUE; - /* Pre-discovered objects are now printed with a '*' */ + /* Objects that were discovered without encountering them are now printed + with a '*' */ return (boolean) (objects[i].oc_uname != (char *) 0 - || (objects[i].oc_name_known + || ((objects[i].oc_name_known + || objects[i].oc_encountered) && OBJ_DESCR(objects[i]) != (char *) 0)); } /* items that should stand out once they're known */ static const short uniq_objs[] = { - AMULET_OF_YENDOR, SPE_BOOK_OF_THE_DEAD, CANDELABRUM_OF_INVOCATION, + AMULET_OF_YENDOR, + /* same order as major oracularity; alphabetical when fully IDed */ BELL_OF_OPENING, + SPE_BOOK_OF_THE_DEAD, + CANDELABRUM_OF_INVOCATION, }; /* discoveries qsort comparison function */ @@ -550,7 +572,7 @@ sortloot_descr(int otyp, char *outbuf) o = cg.zeroobj; o.otyp = otyp; o.oclass = objects[otyp].oc_class; - o.dknown = 1; + o.dknown = 1; /* not observe_object, this isn't a real object */ o.known = (objects[otyp].oc_name_known || !objects[otyp].oc_uses_known) ? 1 : 0; o.corpsenm = NON_PM; /* suppress statue and figurine details */ @@ -666,16 +688,20 @@ disco_typename(int otyp) return result; } -/* append typename(dis) to buf[], possibly truncating in the process */ +/* append typename(dis) to buf[], possibly truncating in the process; + also append price quote information if it fits */ staticfn void disco_append_typename(char *buf, int dis) { - unsigned len = (unsigned) strlen(buf); + size_t len = strlen(buf); char *p, *typnm = disco_typename(dis); + size_t typnm_len = strlen(typnm); + char *eos; - if (len + (unsigned) strlen(typnm) < BUFSZ) { + if (len + typnm_len < BUFSZ) { /* ordinary */ Strcat(buf, typnm); + eos = buf + len + typnm_len; } else if ((p = strrchr(typnm, '(')) != 0 && p > typnm && p[-1] == ' ' && strchr(p, ')') != 0) { /* typename() returned "really long user-applied name (actual type)" @@ -684,10 +710,30 @@ disco_append_typename(char *buf, int dis) --p; /* back up to space in front of open paren */ (void) strncat(buf, typnm, BUFSZ - 1 - (len + (unsigned) strlen(p))); Strcat(buf, p); + eos = buf + strlen(buf); } else { /* unexpected; just truncate from end of typename */ (void) strncat(buf, typnm, BUFSZ - 1 - len); + eos = buf + strlen(buf); } + + append_price_quote(buf, &eos, dis); +} + +/* minor fixup for Book of the Dead needed in more than one place */ +staticfn void +disco_fmt_uniq(int uidx, char *outbuf) +{ + Sprintf(outbuf, " %s", objects[uidx].oc_name_known + ? OBJ_NAME(objects[uidx]) + : OBJ_DESCR(objects[uidx])); + /* in the spellbooks section of main discoveries list, encountered + but not fully discovered Book of the Dead is shown as + "spellbook (papyrus)" like other encountered but not discovered books; + in the unique/relics section we want "papyrus spellbook" instead */ + if (!objects[uidx].oc_name_known + && objects[uidx].oc_class == SPBOOK_CLASS) + Strcat(outbuf, " spellbook"); } /* sort and output sorted_lines to window and free the lines */ @@ -718,10 +764,11 @@ int dodiscovered(void) /* free after Robert Viduya */ { winid tmpwin; - char *s, *p, oclass, prev_class, + char *s, oclass, prev_class, classes[MAXOCLASSES], buf[BUFSZ], *sorted_lines[NUM_OBJECTS]; /* overkill */ - int i, dis, ct, uniq_ct, arti_ct, sorted_ct; + const char *p; + int i, dis, ct, uniq_ct, arti_ct, sorted_ct, uidx; long sortindx; // should be ptrdiff_t, but we don't require that exists boolean alphabetized, alphabyclass, lootsort; @@ -742,17 +789,27 @@ dodiscovered(void) /* free after Robert Viduya */ putstr(tmpwin, 0, buf); putstr(tmpwin, 0, ""); - /* gather "unique objects" into a pseudo-class; note that they'll - also be displayed individually within their regular class */ + /* + * FIXME? + * relics and artifacts don't obey player's sort order even though + * the header line states that they're shown in such-and-such order. + */ + + /* gather "unique objects", also called "relics", into a pseudo-class; + they'll also be displayed individually within their regular class */ uniq_ct = 0; - for (i = dis = 0; i < SIZE(uniq_objs); i++) - if (objects[uniq_objs[i]].oc_name_known) { + for (i = dis = 0; i < SIZE(uniq_objs); i++) { + uidx = uniq_objs[i]; + if (objects[uidx].oc_name_known + || (objects[uidx].oc_encountered && uidx != AMULET_OF_YENDOR)) { if (!dis++) - putstr(tmpwin, iflags.menu_headings.attr, "Unique items"); + putstr(tmpwin, iflags.menu_headings.attr, + "Unique items or Relics"); ++uniq_ct; - Sprintf(buf, " %s", OBJ_NAME(objects[uniq_objs[i]])); + disco_fmt_uniq(uidx, buf); putstr(tmpwin, 0, buf); } + } /* display any known artifacts as another pseudo-class */ arti_ct = disp_artifact_discoveries(tmpwin); @@ -784,7 +841,7 @@ dodiscovered(void) /* free after Robert Viduya */ prev_class = oclass; } } - Strcpy(buf, objects[dis].oc_pre_discovered ? "* " : " "); + Strcpy(buf, objects[dis].oc_encountered ? " " : "* "); if (lootsort) (void) sortloot_descr(dis, &buf[2]); disco_append_typename(buf, dis); @@ -827,22 +884,25 @@ oclass_to_name(char oclass, char *buf) return buf; } -/* the #knownclass command - show discovered object types for one class */ +/* the #knownclass command - show discovered object types for one class; + in addition to actual object classes, supports pseudo-class 'a' for + discovered artifacts and 'u' (or 'r', for "relics") for unique items */ int doclassdisco(void) { static NEARDATA const char prompt[] = "View discoveries for which sort of objects?", havent_discovered_any[] = "haven't discovered any %s yet.", - unique_items[] = "unique items", + unique_items[] = "unique items or relics", artifact_items[] = "artifacts"; winid tmpwin = WIN_ERR; menu_item *pick_list = 0; anything any; - char *p, *s, c, oclass, menulet, allclasses[MAXOCLASSES], - discosyms[2 + MAXOCLASSES + 1], buf[BUFSZ], + char *s, c, oclass, menulet, allclasses[MAXOCLASSES], + discosyms[3 + MAXOCLASSES + 1], buf[BUFSZ], *sorted_lines[NUM_OBJECTS]; /* overkill */ - int i, ct, dis, xtras, sorted_ct; + const char *p; + int i, ct, dis, xtras, sorted_ct, uidx; boolean traditional, alphabetized, lootsort; int clr = NO_COLOR; @@ -866,25 +926,40 @@ doclassdisco(void) any = cg.zeroany; menulet = 'a'; - /* check whether we've discovered any unique objects */ - for (i = 0; i < SIZE(uniq_objs); i++) - if (objects[uniq_objs[i]].oc_name_known) { + /* + * FIXME? + * relics and artifacts don't obey player's sort order even though + * the header line states that they're shown in such-and-such order. + */ + + /* check whether we've discovered any unique objects (primarily the + invocation items; the Guidebook calls unique items "relics" but the + Amulet of Yendor is unique too so we haven't made a blanket change + from 'u' to 'r') */ + for (i = 0; i < SIZE(uniq_objs); i++) { + uidx = uniq_objs[i]; + if (objects[uidx].oc_name_known + || (objects[uidx].oc_encountered && uidx != AMULET_OF_YENDOR)) { Strcat(discosyms, "u"); if (!traditional) { any.a_int = 'u'; - add_menu(tmpwin, &nul_glyphinfo, &any, menulet++, - 0, ATR_NONE, clr, unique_items, MENU_ITEMFLAGS_NONE); + /* FIXME: having 'r' as an accelerator to provide an unseen + synonym works but doesn't make much sense since the main + selector is 'a' (implicit lootabc) rather than 'u' */ + add_menu(tmpwin, &nul_glyphinfo, &any, menulet++, 'r', + ATR_NONE, clr, unique_items, MENU_ITEMFLAGS_NONE); } break; } + } /* check whether we've discovered any artifacts */ if (disp_artifact_discoveries(WIN_ERR) > 0) { Strcat(discosyms, "a"); if (!traditional) { any.a_int = 'a'; - add_menu(tmpwin, &nul_glyphinfo, &any, menulet++, - 0, ATR_NONE, clr, artifact_items, MENU_ITEMFLAGS_NONE); + add_menu(tmpwin, &nul_glyphinfo, &any, menulet++, 0, + ATR_NONE, clr, artifact_items, MENU_ITEMFLAGS_NONE); } } @@ -924,14 +999,14 @@ doclassdisco(void) /* have player choose a class */ c = '\0'; /* class not chosen yet */ if (traditional) { - char allclasses_plustwo[sizeof allclasses + 2]; + char allclasses_plustwo[sizeof allclasses + 3]; /* we'll prompt even if there's only one viable class; we add all nonviable classes as unseen acceptable choices so player can ask for discoveries of any class whether it has discoveries or not */ - Sprintf(allclasses_plustwo, "%s%c%c", allclasses, 'u', 'a'); + Sprintf(allclasses_plustwo, "%s%c%c%c", allclasses, 'a', 'u', 'r'); for (s = allclasses_plustwo, xtras = 0; *s; ++s) { - c = (*s == 'u' || *s == 'a') ? *s : def_oc_syms[(int) *s].sym; + c = strchr("aur", *s) ? *s : def_oc_syms[(int) *s].sym; if (!strchr(discosyms, c)) { if (!xtras++) (void) strkitten(discosyms, '\033'); @@ -970,14 +1045,19 @@ doclassdisco(void) ct = 0; switch (c) { case 'u': + case 'r': putstr(tmpwin, iflags.menu_headings.attr, upstart(strcpy(buf, unique_items))); - for (i = 0; i < SIZE(uniq_objs); i++) - if (objects[uniq_objs[i]].oc_name_known) { + for (i = 0; i < SIZE(uniq_objs); i++) { + uidx = uniq_objs[i]; + if (objects[uidx].oc_name_known + || (objects[uidx].oc_encountered + && uidx != AMULET_OF_YENDOR)) { ++ct; - Sprintf(buf, " %s", OBJ_NAME(objects[uniq_objs[i]])); + disco_fmt_uniq(uidx, buf); putstr(tmpwin, 0, buf); } + } if (!ct) You(havent_discovered_any, unique_items); break; @@ -1011,7 +1091,7 @@ doclassdisco(void) ++i) { if ((dis = svd.disco[i]) != 0 && interesting_to_discover(dis)) { ++ct; - Strcpy(buf, objects[dis].oc_pre_discovered ? "* " : " "); + Strcpy(buf, objects[dis].oc_encountered ? " " : "* "); if (lootsort) (void) sortloot_descr(dis, &buf[2]); disco_append_typename(buf, dis); @@ -1027,12 +1107,14 @@ doclassdisco(void) } else if (sorted_ct) { qsort(sorted_lines, sorted_ct, sizeof (char *), discovered_cmp); for (i = 0; i < sorted_ct; ++i) { - p = sorted_lines[i]; + char *sl; + + sl = sorted_lines[i]; if (lootsort) { - p[6] = p[0]; /* '*' or ' ' */ - p += 6; + sl[6] = sl[0]; /* '*' or ' ' */ + sl += 6; } - putstr(tmpwin, 0, p); + putstr(tmpwin, 0, sl); free(sorted_lines[i]), sorted_lines[i] = 0; } } @@ -1055,6 +1137,7 @@ rename_disco(void) anything any; menu_item *selected = 0; int clr = NO_COLOR; + char buf[BUFSZ]; any = cg.zeroany; tmpwin = create_nhwindow(NHW_MENU); @@ -1088,9 +1171,10 @@ rename_disco(void) prev_class = oclass; } any.a_int = dis; + *buf = '\0'; + disco_append_typename(buf, dis); add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, - disco_typename(dis), MENU_ITEMFLAGS_NONE); + ATR_NONE, clr, buf, MENU_ITEMFLAGS_NONE); } } if (ct == 0) { @@ -1113,7 +1197,7 @@ rename_disco(void) odummy.oclass = objects[dis].oc_class; odummy.quan = 1L; odummy.known = !objects[dis].oc_uses_known; - odummy.dknown = 1; + odummy.dknown = 1; /* not observe_object: it isn't real */ docall(&odummy); } } diff --git a/src/objnam.c b/src/objnam.c index e05ea1c12..1246029fd 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -625,7 +625,7 @@ xname_flags( if (!nn && ocl->oc_uses_known && ocl->oc_unique) obj->known = 0; if (!Blind && !gd.distantname) - obj->dknown = 1; + observe_object(obj); if (Role_if(PM_CLERIC)) obj->bknown = 1; /* avoid set_bknown() to bypass update_inventory() */ @@ -1057,6 +1057,8 @@ minimal_xname(struct obj *obj) bareobj = cg.zeroobj; bareobj.otyp = otyp; bareobj.oclass = obj->oclass; + /* not observe_object, either the hero observed the object already or this + is overriding ID and shouldn't discover the object */ bareobj.dknown = (obj->dknown || iflags.override_ID) ? 1 : 0; /* suppress known except for amulets (needed for fakes and real A-of-Y) */ bareobj.known = (obj->oclass == AMULET_CLASS) @@ -1184,7 +1186,8 @@ add_erosion_words(struct obj *obj, char *prefix) : is_corrodeable(obj) ? "corrodeproof " : is_flammable(obj) ? "fireproof " : is_crackable(obj) ? "tempered " /* hardened */ - : ""); + : is_rottable(obj) ? "rotproof " + : ""); } /* used to prevent rust on items where rust makes no difference */ @@ -1656,6 +1659,8 @@ doname_base( Sprintf(pricebuf, "%ld %s", quotedprice, currency(quotedprice)); ConcatF2(bp, 0, " (%s, %s)", obj->unpaid ? "unpaid" : "contents", pricebuf); + + record_price_quote(obj->otyp, quotedprice / obj->quan, TRUE); } else if (with_price) { /* on floor or in container on floor */ int nochrg = 0; long price = get_cost_of_shop_item(obj, &nochrg); @@ -1669,6 +1674,11 @@ doname_base( } else if (nochrg > 0) { Concat(bp, 0, " (no charge)"); } + + if (price) + record_price_quote(obj->otyp, price / obj->quan, TRUE); + } else if (iflags.pricequotes && !objects[obj->otyp].oc_name_known) { + append_price_quote(bp, &bp_eos, obj->otyp); } if (!strncmp(prefix, "a ", 2)) { @@ -1945,7 +1955,8 @@ killer_xname(struct obj *obj) save_oname = ONAME(obj); /* killer name should be more specific than general xname; however, exact - info like blessed/cursed and rustproof makes things be too verbose */ + info like blessed/cursed and rustproof makes things be too verbose; set + dknown (not observe_object) because dead characters don't observe */ obj->known = obj->dknown = 1; obj->bknown = obj->rknown = obj->greased = 0; /* if character is a priest[ess], bknown will get toggled back on */ @@ -2182,7 +2193,8 @@ the(const char *str) insert_the = TRUE; } else { /* Probably a proper name, might not need an article */ - char *tmp, *named, *called; + char *named, *called; + const char *tmp; int l; /* some objects have capitalized adjectives in their names */ diff --git a/src/options.c b/src/options.c index 090300fe1..a2dc7cedf 100644 --- a/src/options.c +++ b/src/options.c @@ -56,21 +56,13 @@ NEARDATA struct accessibility_data a11y; #include "optlist.h" #undef NHOPT_PROTO -#define NHOPT_ENUM -enum opt { - opt_prefix_only = -1, -#include "optlist.h" - OPTCOUNT -}; -#undef NHOPT_ENUM - #define NHOPT_PARSE static struct allopt_t allopt_init[] = { #include "optlist.h" {(const char *) 0, OptS_Advanced, 0, 0, 0, set_in_sysconf, BoolOpt, No, No, No, No, Term_False, 0, (boolean *) 0, (int (*)(int, int, boolean, char *, char *)) 0, - (char *) 0, (const char *) 0, (const char *) 0, 0, 0, 0 } + (char *) 0, (const char *) 0, (const char *) 0, 0, 0, 0, TRUE } }; #undef NHOPT_PARSE @@ -94,16 +86,6 @@ enum optn_result { enum requests { do_nothing, do_init, do_set, do_handler, get_val, get_cnf_val }; -/* these aren't the same as set_xxx in optlist.h */ -enum option_phases { - builtin_opt=1,/* compiled-in default value of an option */ - syscf_opt, /* sysconf setting of an option, overrides builtin */ - rc_file_opt, /* player's run-time config file setting, overrides syscf */ - environ_opt, /* player's environment NETHACKOPTIONS, overrides rc_file */ - cmdline_opt, /* program invocation command-line, overrides environ */ - play_opt, /* 'O' command, interactively set so overrides all */ - num_opt_phases -}; static struct allopt_t allopt[SIZE(allopt_init)]; @@ -390,6 +372,7 @@ staticfn boolean parse_role_opt(int, boolean, const char *, char *, char **); staticfn char *get_cnf_role_opt(int); staticfn unsigned int longest_option_name(int, int); staticfn int doset_simple_menu(void); +staticfn void reset_needed_visuals(void); staticfn void doset_add_menu(winid, const char *, const char *, int, int); staticfn int handle_add_list_remove(const char *, int); staticfn void all_options_conds(strbuf_t *); @@ -594,7 +577,7 @@ parseoptions( * placed that number into each option's allopt[n].minmatch. * */ - if (!got_match) + if (!got_match && allopt[i].name) got_match = match_optname(opts, allopt[i].name, allopt[i].minmatch, TRUE); if (got_match) { @@ -633,7 +616,8 @@ parseoptions( /* allow optfn's to test whether they were called from parseoptions() */ program_state.in_parseoptions++; - if (got_match && matchidx >= 0) { + if (got_match && (matchidx >= 0 && matchidx < OPTCOUNT) + && !allopt[matchidx].disregarded) { duplicate = duplicate_opt_detection(matchidx); if (duplicate && !allopt[matchidx].dupeok) complain_about_duplicate(matchidx); @@ -683,7 +667,9 @@ parseoptions( } } - if (optresult == optn_silenterr) + if (optresult == optn_silenterr + || (got_match && allopt[matchidx].disregarded) + || (!got_match && config_unmatched_ignored())) return FALSE; if (pfx_match && optresult == optn_err) { char pfxbuf[BUFSZ], *pfxp; @@ -1518,7 +1504,8 @@ optfn_disclose( DISCLOSE_NO_WITHOUT_PROMPT, DISCLOSE_SPECIAL_WITHOUT_PROMPT, '\0' }; - char c, *dop; + char c; + const char *dop; c = lowc(*op); if (c == 'k') @@ -1991,6 +1978,8 @@ optfn_map_mode( */ op = string_for_opt(opts, negated); if (op != empty_optstr && !negated) { + int save_map_mode = iflags.wc_map_mode; + if (!strcmpi(op, "tiles")) iflags.wc_map_mode = MAP_MODE_TILES; else if (!strncmpi(op, "ascii4x6", sizeof "ascii4x6" - 1)) @@ -2025,6 +2014,11 @@ optfn_map_mode( allopt[optidx].name, op); return optn_err; } + if (wc_supported("map_mode")) { + if (!iflags.wc_map_mode + || save_map_mode != iflags.wc_map_mode) + preference_update("map_mode"); + } } else if (negated) { bad_negation(allopt[optidx].name, TRUE); return optn_err; @@ -4969,17 +4963,20 @@ optfn_windowtype( * _end_ because comma-separated option strings are processed from * right to left. */ - if (iflags.windowtype_locked) - return optn_ok; + if (!iflags.window_inited) { + if (iflags.windowtype_locked) + return optn_ok; - if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE)) - != empty_optstr) { - nmcpy(gc.chosen_windowtype, op, WINTYPELEN); - if (!iflags.windowtype_deferred) { - choose_windows(gc.chosen_windowtype); + if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE)) + != empty_optstr) { + nmcpy(gc.chosen_windowtype, op, WINTYPELEN); + if (!iflags.windowtype_deferred) { + choose_windows(gc.chosen_windowtype); + } + } else { + return optn_err; } - } else - return optn_err; + } return optn_ok; } if (req == get_val || req == get_cnf_val) { @@ -5191,7 +5188,7 @@ pfxfn_IBM_(int optidx UNUSED, int req, boolean negated UNUSED, * (Use optidx to reference the specific option) */ -staticfn int +int optfn_boolean( int optidx, int req, boolean negated, char *opts, char *op) @@ -5334,6 +5331,7 @@ optfn_boolean( disp.botl = TRUE; break; case opt_fixinv: + case opt_price_quotes: case opt_sortpack: case opt_implicit_uncursed: case opt_wizweight: @@ -5491,9 +5489,6 @@ can_set_perm_invent(void) #ifdef TTY_PERM_INVENT if ((WINDOWPORT(tty) -#ifdef WIN32 - || WINDOWPORT(safestartup) -#endif ) && !go.opt_initial) { perm_invent_toggled(FALSE); /* perm_invent_toggled() @@ -7063,29 +7058,14 @@ txt2key(char *txt) void initoptions(void) { - int i; - /* * Most places that call initoptions_init()/initoptions() would * have the calls next to each other, so instead of adding * initoptions_init() everywhere, just add it where it's needed in * a non-adjacent place and call it here for all the other cases. */ - if(go.opt_phase != builtin_opt) + if (go.opt_phase != builtin_opt) initoptions_init(); - - /* - * Call each option function with an init flag and give it a chance - * to make any preparations that it might require. We do this - * whether or not the option itself is ever specified; that's - * irrelevant for the init call. Doing this allows the prep code for - * option settings to remain adjacent to, and in the same function as, - * the code that processes those options. - */ - for (i = 0; i < OPTCOUNT; ++i) { - if (allopt[i].optfn) - (*allopt[i].optfn)(i, do_init, FALSE, empty_optstr, empty_optstr); - } #ifdef SYSCF /* someday there may be other SYSCF alternatives besides text file */ #ifdef SYSCF_FILE @@ -7127,9 +7107,7 @@ initoptions_init(void) go.opt_phase = builtin_opt; /* Did I need to move this here? */ /* initialize the function pointers for saving the game */ sf_init(); - memcpy(allopt, allopt_init, sizeof(allopt)); - determine_ambiguities(); - + allopt_array_init(); /* if windowtype has been specified on the command line, set it up early so windowtype-specific options use it as their base */ if (gc.cmdline_windowsys) { @@ -7283,6 +7261,27 @@ initoptions_init(void) /* since this is done before init_objects(), do partial init here */ objects[SLIME_MOLD].oc_name_idx = SLIME_MOLD; nmcpy(svp.pl_fruit, OBJ_NAME(objects[SLIME_MOLD]), PL_FSIZ); + +#ifdef SYSCF +/* someday there may be other SYSCF alternatives besides text file */ +#ifdef SYSCF_FILE + /* If SYSCF_FILE is specified, it _must_ exist... */ + assure_syscf_file(); + config_error_init(TRUE, SYSCF_FILE, FALSE); + + /* ... and _must_ parse correctly. */ + go.opt_phase = syscf_opt; + if (!read_config_file(SYSCF_FILE, set_in_sysconf)) { + if (config_error_done() && !iflags.initoptions_noterminate) + nh_terminate(EXIT_FAILURE); + } + config_error_done(); + /* + * TODO [maybe]: parse the sysopt entries which are space-separated + * lists of usernames into arrays with one name per element. + */ +#endif +#endif /* SYSCF */ } /* @@ -7303,71 +7302,9 @@ initoptions_init(void) */ void initoptions_finish(void) -{ - nhsym sym = 0; - char *opts = 0, *xtraopts = 0; -#ifndef MAC - const char *envname, *namesrc, *nameval; +{ nhsym sym = 0; - /* getenv() instead of nhgetenv(): let total length of options be long; - parseoptions() will check each individually */ - envname = "NETHACKOPTIONS"; - opts = getenv(envname); - if (!opts) { - /* fall back to original name; discouraged */ - envname = "HACKOPTIONS"; - opts = getenv(envname); - } - - if (gc.cmdline_rcfile) { - namesrc = "command line"; - nameval = gc.cmdline_rcfile; - xtraopts = opts; - if (opts && (*opts == '/' || *opts == '\\' || *opts == '@')) - xtraopts = 0; /* NETHACKOPTIONS is a file name; ignore it */ - } else if (opts && (*opts == '/' || *opts == '\\' || *opts == '@')) { - /* NETHACKOPTIONS is a file name; use that instead of the default */ - if (*opts == '@') - ++opts; /* @filename */ - namesrc = envname; - nameval = opts; - xtraopts = 0; - } else -#endif /* !MAC */ - /*else*/ { - /* either no NETHACKOPTIONS or it wasn't a file name; - read the default configuration file */ - nameval = namesrc = 0; - xtraopts = opts; - } - - /* seemingly arbitrary name length restriction is to prevent error - messages, if any were to be delivered while accessing the file, - from potentially overflowing buffers */ - if (nameval && (int) strlen(nameval) >= BUFSZ / 2) { - go.opt_phase = rc_file_opt; - config_error_init(TRUE, namesrc, FALSE); - config_error_add( - "nethackrc file name \"%.40s\"... too long; using default", - nameval); - config_error_done(); - nameval = namesrc = 0; /* revert to default nethackrc */ - } - - config_error_init(TRUE, nameval, nameval ? CONFIG_ERROR_SECURE : FALSE); - (void) read_config_file(nameval, set_in_config); - config_error_done(); - if (xtraopts) { - /* NETHACKOPTIONS is present and not a file name */ - go.opt_phase = environ_opt; - config_error_init(FALSE, envname, FALSE); - (void) parseoptions(xtraopts, TRUE, FALSE); - config_error_done(); - } - - if (gc.cmdline_rcfile) - free((genericptr_t) gc.cmdline_rcfile), gc.cmdline_rcfile = 0; - /*[end of nethackrc handling]*/ + rcfile(); (void) fruitadd(svp.pl_fruit, (struct fruit *) 0); /* @@ -7398,12 +7335,10 @@ initoptions_finish(void) * Option processing can take place before a user-decided WindowPort * is even initialized, so check for that too. */ - if (!WINDOWPORT(safestartup)) { - if (iflags.hilite_delta && !wc2_supported("statushilites")) { - raw_printf("Status highlighting not supported for %s interface.", - windowprocs.name); - iflags.hilite_delta = 0; - } + if (iflags.hilite_delta && !wc2_supported("statushilites")) { + raw_printf("Status highlighting not supported for %s interface.", + windowprocs.name); + iflags.hilite_delta = 0; } #endif update_rest_on_space(); @@ -7418,18 +7353,16 @@ initoptions_finish(void) && wc_supported("tiled_map")) iflags.wc_ascii_map = FALSE, iflags.wc_tiled_map = TRUE; - if (iflags.wc_tiled_map && !opt_set_in_config[opt_color]) - iflags.wc_color = TRUE; - if (iflags.wc_ascii_map && !iflags.wc_color - && !opt_set_in_config[opt_bgcolors]) - iflags.bgcolors = FALSE; - +#ifdef ENHANCED_SYMBOLS if (glyphid_cache_status()) free_glyphid_cache(); - apply_customizations(gc.currentgraphics, - (do_custom_colors | do_custom_symbols)); + apply_customizations(gc.currentgraphics, do_custom_symbols); +#endif go.opt_initial = FALSE; + return; +} +#if 0 /* * Do these after clearing the 'opt_initial' flag. */ @@ -7444,7 +7377,38 @@ initoptions_finish(void) if (can_set_perm_invent()) iflags.perm_invent = TRUE; } - return; +} +#endif + +void +allopt_array_init(void) +{ + int i; + static boolean options_array_inited_already = FALSE; + + if (!options_array_inited_already) { + memcpy(allopt, allopt_init, sizeof(allopt)); + determine_ambiguities(); + for (i = 0; allopt[i].name; i++) { + if (allopt[i].addr) + *(allopt[i].addr) = allopt[i].initval; + } + heed_all_options(); + /* + * Call each option function with an init flag and give it a chance + * to make any preparations that it might require. We do this + * whether or not the option itself is ever specified; that's + * irrelevant for the init call. Doing this allows the prep code for + * option settings to remain adjacent to, and in the same function as, + * the code that processes those options. + */ + for (i = 0; i < OPTCOUNT; ++i) { + if (allopt[i].optfn) + (*allopt[i].optfn)(i, do_init, FALSE, empty_optstr, + empty_optstr); + } + options_array_inited_already = TRUE; + } } /* @@ -7681,7 +7645,7 @@ parsebindings(char *bindings) } /* extended command? */ - if (!bind_key(key, bind)) { + if (!bind_key(key, bind, TRUE)) { config_error_add("Unknown key binding command '%s'", bind); return FALSE; } @@ -8898,7 +8862,7 @@ doset(void) /* changing options via menu by Per Liboriussen */ enhance_menu_text(buf, sizeof buf, pass, bool_p, &allopt[i]); add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, buf, MENU_ITEMFLAGS_NONE); + ATR_NONE, clr, buf, MENU_ITEMFLAGS_SKIPINVERT); } add_menu_str(tmpwin, ""); @@ -8963,6 +8927,7 @@ doset(void) /* changing options via menu by Per Liboriussen */ if (opt_indx < -1) opt_indx++; /* -1 offset for select_menu() */ opt_indx -= indexoffset; + assert(IndexOk(opt_indx, allopt)); if (allopt[opt_indx].opttyp == BoolOpt) { /* boolean option */ Sprintf(buf, "%s%s", *allopt[opt_indx].addr ? "!" : "", @@ -9011,6 +8976,15 @@ doset(void) /* changing options via menu by Per Liboriussen */ goto rerun; } + reset_needed_visuals(); + return ECMD_OK; +} + +#undef HELP_IDX + +staticfn void +reset_needed_visuals(void) +{ if (go.opt_need_glyph_reset) { reset_glyphmap(gm_optionchange); } @@ -9038,11 +9012,13 @@ doset(void) /* changing options via menu by Per Liboriussen */ if (disp.botl || disp.botlx) { bot(); } - return ECMD_OK; + go.opt_need_redraw = FALSE; + go.opt_need_glyph_reset = FALSE; + go.opt_reset_customcolors = FALSE; + go.opt_reset_customsymbols = FALSE; + go.opt_update_basic_palette = FALSE; } -#undef HELP_IDX - /* doset(#optionsfull command) menu entries for compound options */ staticfn void doset_add_menu( @@ -9091,7 +9067,7 @@ doset_add_menu( indent = !any.a_int ? " " : ""; Sprintf(buf, fmtstr, indent, option, value); add_menu(win, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, buf, MENU_ITEMFLAGS_NONE); + ATR_NONE, clr, buf, MENU_ITEMFLAGS_SKIPINVERT); } @@ -9303,6 +9279,29 @@ dotogglepickup(void) return ECMD_OK; } +/* toggle any (settable in-game) boolean option by name */ +int +toggle_bool_option(const char *p) +{ + int i; + int ret = ECMD_FAIL; + + for (i = 0; i < OPTCOUNT; i++) + if (!strncmpi(allopt[i].name, p, strlen(p)) + && allopt[i].opttyp == BoolOpt + && allopt[i].setwhere == set_in_game + && allopt[i].addr != 0) { + char buf[BUFSZ]; + + Sprintf(buf, "%s%s", *allopt[i].addr ? "!" : "", allopt[i].name); + if (parseoptions(buf, FALSE, FALSE)) + ret = ECMD_OK; + + reset_needed_visuals(); + } + return ret; +} + int add_autopickup_exception(const char *mapping) { @@ -10183,6 +10182,39 @@ enhance_menu_text( return; } +void +heed_all_options(void) +{ + int i; + + for (i = 0; i < OPTCOUNT; i++) + allopt[i].disregarded = FALSE; +} + +void +disregard_all_options(void) +{ + int i; + + for (i = 0; i < OPTCOUNT ; i++) + allopt[i].disregarded = TRUE; +} + +void +heed_this_option(enum opt optidx) +{ + if (optidx >= 0 && optidx < (enum opt) OPTCOUNT) + allopt[optidx].disregarded = FALSE; +} +void +disregard_this_option(enum opt optidx) +{ + if (optidx >= 0 && optidx < (enum opt) OPTCOUNT) + allopt[optidx].disregarded = TRUE; +} + + + #undef OPTIONS_HEADING #undef CONFIG_SLOT diff --git a/src/pager.c b/src/pager.c index 061937687..82b8bc7eb 100644 --- a/src/pager.c +++ b/src/pager.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 pager.c $NHDT-Date: 1737013431 2025/01/15 23:43:51 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.287 $ */ +/* NetHack 3.7 pager.c $NHDT-Date: 1774846177 2026/03/29 20:49:37 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.296 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2018. */ /* NetHack may be freely redistributed. See license for details. */ @@ -366,11 +366,11 @@ object_from_map( && (fakeobj || otmp->where == OBJ_FLOOR) /* not buried */ /* terrain mode views what's already known, doesn't learn new stuff */ && !iflags.terrainmode) /* so don't set dknown when in terrain mode */ - otmp->dknown = 1; /* if a pile, clearly see the top item only */ + observe_object(otmp); /* if a pile, clearly see the top item only */ if (fakeobj && mtmp && mimic_obj && (otmp->dknown || (M_AP_FLAG(mtmp) & M_AP_F_DKNOWN))) { mtmp->m_ap_type |= M_AP_F_DKNOWN; - otmp->dknown = 1; + observe_object(otmp); } *obj_p = otmp; return fakeobj; /* when True, caller needs to dealloc *obj_p */ @@ -394,11 +394,17 @@ look_at_object( otmp->where = OBJ_FREE; /* object_from_map set it to OBJ_FLOOR */ dealloc_obj(otmp), otmp = NULL; /* has no contents */ } - } else + } else { Strcpy(buf, something); /* sanity precaution */ + } if (otmp && otmp->where == OBJ_BURIED) Strcat(buf, " (buried)"); + /* check TREE before STONE due to level.flags.arboreal */ + else if (IS_TREE(levl[x][y].typ)) + /* "dangling": "hanging" could imply that it's growing on this tree */ + Snprintf(eos(buf), BUFSZ - strlen(buf), " %s in a tree", + (otmp && is_treefruit(otmp)) ? "dangling" : "stuck"); else if (levl[x][y].typ == STONE || levl[x][y].typ == SCORR) Strcat(buf, " embedded in stone"); else if (IS_WALL(levl[x][y].typ) || levl[x][y].typ == SDOOR) @@ -1137,7 +1143,8 @@ add_cmap_descr( const char **firstmatch, /* output: pointer to 1st matching description */ char *out_str) /* input/output: current description gets appended */ { - char *mbuf = NULL, *p; + char *mbuf = NULL; + const char *p; int absidx = abs(idx); if (glyph == NO_GLYPH) { @@ -1594,6 +1601,9 @@ do_screen_description( *for_supplement = pm; if (!strcmp(look_buf, "ice")) (void) ice_descr(cc.x, cc.y, look_buf); + if (!strcmp(look_buf, "staircase down") + && on_level(&u.uz, &qstart_level) && !ok_to_quest()) + Strcpy(look_buf, "blocked staircase down"); if (look_buf[0] != '\0') *firstmatch = look_buf; @@ -1700,61 +1710,93 @@ do_look(int mode, coord *click_cc) any = cg.zeroany; win = create_nhwindow(NHW_MENU); start_menu(win, MENU_BEHAVE_STANDARD); + + /* + * Originally this was just a y|n question about whether to + * use the cursor or to type a word. When other choices were + * added, it was changed to be a menu. Using 'y' and 'n' as + * unshown accelerators keeps backwards compatibility with + * the old y|n behavior. + * + * Initially the menu included a third choice and always used + * 'a', 'b', and 'c'. Then it was changed to be controlled by + * the 'lootabc' option instead, defaulting to '/', 'i', '?' + * when that's false. Eventually additional entries have been + * introduced. + * + * When lootabc is set, abandon the 'y'|'n' compatibility in + * favor of newer '/' and '?' compatibility instead. + */ + any.a_char = '/'; - /* 'y' and 'n' to keep backwards compatibility with previous - versions: "Specify unknown object by cursor?" */ add_menu(win, &nul_glyphinfo, &any, - flags.lootabc ? 0 : any.a_char, 'y', ATR_NONE, + flags.lootabc ? 0 : any.a_char, + flags.lootabc ? '/' : 'y', ATR_NONE, clr, "something on the map", MENU_ITEMFLAGS_NONE); any.a_char = 'i'; add_menu(win, &nul_glyphinfo, &any, + /* [don't use 'i' as lootabc group accelerator because + it will make the regular 'i' choice inaccessible] */ flags.lootabc ? 0 : any.a_char, 0, ATR_NONE, clr, "something you're carrying", MENU_ITEMFLAGS_NONE); any.a_char = '?'; add_menu(win, &nul_glyphinfo, &any, - flags.lootabc ? 0 : any.a_char, 'n', ATR_NONE, + flags.lootabc ? 0 : any.a_char, + flags.lootabc ? '?' : 'n', ATR_NONE, clr, "something else (by symbol or name)", MENU_ITEMFLAGS_NONE); if (!u.uswallow && !Hallucination) { any = cg.zeroany; add_menu_str(win, ""); - /* these options work sensibly for the swallowed case, - but there's no reason for the player to use them then; + /* these options work sensibly for swallowed case, but + there's no reason for player to use them then because + the swallowed display hides all applicable targets; objects work fine when hallucinating, but screen symbol/monster class letter doesn't match up with bogus monster type, so suppress when hallucinating */ any.a_char = 'm'; add_menu(win, &nul_glyphinfo, &any, - flags.lootabc ? 0 : any.a_char, 0, ATR_NONE, + flags.lootabc ? 0 : any.a_char, + flags.lootabc ? any.a_char : 0, ATR_NONE, clr, "nearby monsters", MENU_ITEMFLAGS_NONE); any.a_char = 'M'; add_menu(win, &nul_glyphinfo, &any, - flags.lootabc ? 0 : any.a_char, 0, ATR_NONE, clr, - "all monsters shown on map", MENU_ITEMFLAGS_NONE); + flags.lootabc ? 0 : any.a_char, + flags.lootabc ? any.a_char : 0, ATR_NONE, + clr, "all monsters shown on map", + MENU_ITEMFLAGS_NONE); any.a_char = 'o'; add_menu(win, &nul_glyphinfo, &any, - flags.lootabc ? 0 : any.a_char, 0, ATR_NONE, + flags.lootabc ? 0 : any.a_char, + flags.lootabc ? any.a_char : 0, ATR_NONE, clr, "nearby objects", MENU_ITEMFLAGS_NONE); any.a_char = 'O'; add_menu(win, &nul_glyphinfo, &any, - flags.lootabc ? 0 : any.a_char, 0, ATR_NONE, clr, - "all objects shown on map", MENU_ITEMFLAGS_NONE); + flags.lootabc ? 0 : any.a_char, + flags.lootabc ? any.a_char : 0, ATR_NONE, + clr, "all objects shown on map", + MENU_ITEMFLAGS_NONE); any.a_char = 't'; add_menu(win, &nul_glyphinfo, &any, - flags.lootabc ? 0 : any.a_char, '^', ATR_NONE, + flags.lootabc ? 0 : any.a_char, + flags.lootabc ? any.a_char : '^', ATR_NONE, clr, "nearby traps", MENU_ITEMFLAGS_NONE); any.a_char = 'T'; add_menu(win, &nul_glyphinfo, &any, - flags.lootabc ? 0 : any.a_char, '\"', ATR_NONE, + flags.lootabc ? 0 : any.a_char, + flags.lootabc ? any.a_char : '\"', ATR_NONE, clr, "all seen or remembered traps", MENU_ITEMFLAGS_NONE); any.a_char = 'e'; add_menu(win, &nul_glyphinfo, &any, - flags.lootabc ? 0 : any.a_char, '`', ATR_NONE, + flags.lootabc ? 0 : any.a_char, + /* [don't use 'e' as lootabc group accelerator] */ + flags.lootabc ? 0 : '`', ATR_NONE, clr, "nearby engravings", MENU_ITEMFLAGS_NONE); any.a_char = 'E'; add_menu(win, &nul_glyphinfo, &any, - flags.lootabc ? 0 : any.a_char, '|', ATR_NONE, + flags.lootabc ? 0 : any.a_char, + flags.lootabc ? any.a_char : '|', ATR_NONE, clr, "all seen or remembered engravings", MENU_ITEMFLAGS_NONE); } diff --git a/src/pickup.c b/src/pickup.c index b74119402..331933137 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 pickup.c $NHDT-Date: 1720074481 2024/07/04 06:28:01 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.374 $ */ +/* NetHack 3.7 pickup.c $NHDT-Date: 1773373633 2026/03/12 19:47:13 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.386 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1805,7 +1805,6 @@ pickup_object( long count, /* if non-zero, pick up a subset of this amount */ boolean telekinesis) /* not picking it up directly by hand */ { - unsigned save_how_lost; int res; if (obj->quan < count) { @@ -1816,7 +1815,7 @@ pickup_object( /* In case of auto-pickup, where we haven't had a chance to look at it yet; affects docall(SCR_SCARE_MONSTER). */ if (!Blind) - obj->dknown = 1; + observe_object(obj); if (obj == uchain) { /* do not pick up attached chain */ return 0; @@ -1862,16 +1861,12 @@ pickup_object( } } - save_how_lost = obj->how_lost; /* obj has either already passed autopick_testobj or we are explicitly - picking it off the floor, so override obj->how_lost; otherwise we - couldn't pick up a thrown, stolen, or dropped item that was split - off from a carried stack even while still carrying the rest of the - stack unless we have at least one free slot available */ - obj->how_lost &= ~LOSTOVERRIDEMASK; /* affects merge_choice() */ + picking it off the floor, so addinv() will override obj->how_lost; + otherwise we couldn't pick up a thrown, stolen, or dropped item that + was split off from a carried stack even while still carrying the + rest of the stack unless we have at least one free slot available */ res = lift_object(obj, (struct obj *) 0, &count, telekinesis); - obj->how_lost = save_how_lost; /* even when res > 0, - * in case we call splitobj() below */ if (res <= 0) return res; @@ -1881,7 +1876,6 @@ pickup_object( if (obj->quan != count && obj->otyp != LOADSTONE) obj = splitobj(obj, count); - obj->how_lost &= ~LOSTOVERRIDEMASK; obj = pick_obj(obj); if (uwep && uwep == obj) @@ -1972,10 +1966,9 @@ pickup_prinv( } /* - * prints a message if encumbrance changed since the last check and - * returns the new encumbrance value (from near_capacity()). + * prints a message if encumbrance changed since the last check */ -int +void encumber_msg(void) { int newcap = near_capacity(); @@ -2018,7 +2011,6 @@ encumber_msg(void) } go.oldcap = newcap; - return newcap; } /* Is there a container at x,y. Optional: return count of containers at x,y */ @@ -2865,9 +2857,19 @@ observe_quantum_cat(struct obj *box, boolean makecat, boolean givemsg) } box->owt = weight(box); box->spe = 0; + + if (!svc.context.mon_moving) { + /* give experience points for releasing live cat; slightly + different amount from what is given for "killing" it */ + more_experienced(10, 20); /* 10:current exp; 20:score bonus */ + newexplevel(); + } } } else { box->spe = 0; /* now an ordinary box (with a cat corpse inside) */ + if (givemsg) + pline_The("%s inside the box is dead!", + Hallucination ? rndmonnam((char *) 0) : "housecat"); if (deadcat) { /* set_corpsenm() will start the rot timer that was removed when makemon() created SchroedingersBox; start it from @@ -2875,10 +2877,14 @@ observe_quantum_cat(struct obj *box, boolean makecat, boolean givemsg) deadcat->age = svm.moves; set_corpsenm(deadcat, PM_HOUSECAT); deadcat = oname(deadcat, sc, ONAME_NO_FLAGS); + + if (!svc.context.mon_moving) { + /* give experience points for the death of the cat since + that has been finalized by the hero opening the box */ + more_experienced(20, 10); /* 20:current exp; 10:score bonus */ + newexplevel(); + } } - if (givemsg) - pline_The("%s inside the box is dead!", - Hallucination ? rndmonnam((char *) 0) : "housecat"); } nhUse(deadcat); return; @@ -3821,7 +3827,7 @@ tipcontainer(struct obj *box) /* or bag */ if (targetbox) targetbox->owt = weight(targetbox); if (srcheld || dstheld) - (void) encumber_msg(); + encumber_msg(); } if (srcheld || dstheld) diff --git a/src/pline.c b/src/pline.c index 48f8fc89f..79dd8b28b 100644 --- a/src/pline.c +++ b/src/pline.c @@ -66,6 +66,9 @@ putmesg(const char *line) { int attr = ATR_NONE; + if (iflags.debug_prevent_pline) + return; + if ((gp.pline_flags & URGENT_MESSAGE) != 0 && (windowprocs.wincap2 & WC2_URGENT_MESG) != 0) attr |= ATR_URGENT; @@ -80,7 +83,7 @@ putmesg(const char *line) void set_msg_dir(int dir) { - dtoxy(&a11y.msg_loc, dir); + dirtocoord(&a11y.msg_loc, dir); a11y.msg_loc.x += u.ux; a11y.msg_loc.y += u.uy; } diff --git a/src/polyself.c b/src/polyself.c index 4ab183113..973cc053b 100644 --- a/src/polyself.c +++ b/src/polyself.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 polyself.c $NHDT-Date: 1740534595 2025/02/25 17:49:55 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.223 $ */ +/* NetHack 3.7 polyself.c $NHDT-Date: 1772101811 2026/02/26 02:30:11 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.227 $ */ /* Copyright (C) 1987, 1988, 1989 by Ken Arromdee */ /* NetHack may be freely redistributed. See license for details. */ @@ -201,7 +201,8 @@ polyman(const char *fmt, const char *arg) { boolean sticking = (sticks(gy.youmonst.data) && u.ustuck && !u.uswallow), was_mimicking = (U_AP_TYPE != M_AP_NOTHING); - boolean was_blind = !!Blind; + boolean was_blind = !!Blind, + had_see_invis = !!See_invisible; if (Upolyd) { u.acurr = u.macurr; /* restore old attribs */ @@ -245,6 +246,9 @@ polyman(const char *fmt, const char *arg) done(GENOCIDED); } + if (!!See_invisible ^ had_see_invis) + set_mimic_blocking(); /* See_invisible just toggled */ + if (u.twoweap && !could_twoweap(gy.youmonst.data)) untwoweapon(); @@ -425,7 +429,7 @@ newman(void) done(DIED); /* must have been life-saved to get here */ newuhs(FALSE); - (void) encumber_msg(); /* used to be done by redist_attr() */ + encumber_msg(); /* used to be done by redist_attr() */ return; /* lifesaved */ } } @@ -454,7 +458,7 @@ newman(void) disp.botl = TRUE; see_monsters(); - (void) encumber_msg(); + encumber_msg(); retouch_equipment(2); if (!uarmg) @@ -646,7 +650,7 @@ polyself(int psflags) re-converting scales to mail poses risk of evaporation due to over enchanting */ uarm->otyp += GRAY_DRAGON_SCALES - GRAY_DRAGON_SCALE_MAIL; - uarm->dknown = 1; + observe_object(uarm); disp.botl = TRUE; /* AC is changing */ } uskin = uarm; @@ -1012,7 +1016,7 @@ polymon(int mntmp) disp.botl = TRUE; gv.vision_full_recalc = 1; see_monsters(); - (void) encumber_msg(); + encumber_msg(); retouch_equipment(2); /* this might trigger a recursive call to polymon() [stone golem @@ -1172,15 +1176,9 @@ break_armor(void) if ((otmp = uarmc) != 0 /* mummy wrapping adapts to small and very big sizes */ && (otmp->otyp != MUMMY_WRAPPING || !WrappingAllowed(uptr))) { - if (otmp->oartifact) { - Your("%s falls off!", cloak_simple_name(otmp)); - (void) Cloak_off(); - dropp(otmp); - } else { - Your("%s tears apart!", cloak_simple_name(otmp)); - (void) Cloak_off(); - useup(otmp); - } + pline_The("clasp on your %s breaks open!", cloak_simple_name(otmp)); + (void) Cloak_off(); + dropp(otmp); } if (uarmu) { Your("shirt rips to shreds!"); @@ -1371,7 +1369,7 @@ rehumanize(void) return; /* don't rehumanize after all */ } else if (uamul && uamul->otyp == AMULET_OF_UNCHANGING) { Your("%s %s!", simpleonames(uamul), otense(uamul, "fail")); - uamul->dknown = 1; + observe_object(uamul); makeknown(AMULET_OF_UNCHANGING); } } @@ -1398,7 +1396,7 @@ rehumanize(void) disp.botl = TRUE; gv.vision_full_recalc = 1; - (void) encumber_msg(); + encumber_msg(); if (was_flying && !Flying && u.usteed) You("and %s return gently to the %s.", mon_nam(u.usteed), surface(u.ux, u.uy)); diff --git a/src/potion.c b/src/potion.c index a5cf0a1f4..c79498f90 100644 --- a/src/potion.c +++ b/src/potion.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 potion.c $NHDT-Date: 1737605675 2025/01/22 20:14:35 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.274 $ */ +/* NetHack 3.7 potion.c $NHDT-Date: 1770949988 2026/02/12 18:33:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.279 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -666,6 +666,9 @@ peffect_restore_ability(struct obj *otmp) WEAK or worse, but that's handled via ATEMP(A_STR) now */ if (ABASE(i) < lim) { ABASE(i) = lim; + /* reset stat abuse (but not exercise) to 0 as well */ + AEXE(i) = max(AEXE(i), 0); + disp.botl = TRUE; /* only first found if not blessed */ if (!otmp->blessed) @@ -826,6 +829,10 @@ peffect_invisibility(struct obj *otmp) if (otmp->cursed) { pline("For some reason, you feel your presence is known."); aggravate(); + + /* doing this gives temporary invisibility, but removes permanent + invisibility */ + HInvis &= ~FROMOUTSIDE; } } @@ -833,6 +840,7 @@ staticfn void peffect_see_invisible(struct obj *otmp) { int msg = Invisible && !Blind; + int permchance = 10 - (HInvis ? 3 : 0) - (HSee_invisible ? 6 : 0); gp.potion_unkn++; if (otmp->cursed) @@ -855,7 +863,7 @@ peffect_see_invisible(struct obj *otmp) */ make_blinded(0L, TRUE); } - if (otmp->blessed) + if (otmp->blessed && !rn2(permchance)) HSee_invisible |= FROMOUTSIDE; else incr_itimeout(&HSee_invisible, rn1(100, 750)); @@ -1772,12 +1780,22 @@ potionhit(struct monst *mon, struct obj *obj, int how) mon->mconf = TRUE; break; case POT_INVISIBILITY: { - boolean sawit = canspotmon(mon); + boolean sawit = canspotmon(mon), + cursed_potion = obj->cursed ? TRUE : FALSE; - angermon = FALSE; - mon_set_minvis(mon); - if (sawit && !canspotmon(mon) && cansee(mon->mx, mon->my)) - map_invisible(mon->mx, mon->my); + angermon = mon->minvis && cursed_potion; + mon_set_minvis(mon, cursed_potion); + if (sawit && !canspotmon(mon)) { + if (cansee(mon->mx, mon->my)) + map_invisible(mon->mx, mon->my); + } else if (sawit && cursed_potion) { + pline("%s briefly seems to be transparent.", Monnam(mon)); + /* see use_misc(muse.c) for comment about map_invisible() */ + } else if (!sawit && canspotmon(mon)) { + /* if an invisible mon glyph was present, mon_set_minvis()'s + newsym() has gotten rid of it */ + pline("%s appears!", Monnam(mon)); + } break; } case POT_SLEEPING: @@ -2732,10 +2750,10 @@ potion_dip(struct obj *obj, struct obj *potion) else singlepotion->cursed = obj->cursed; /* odiluted left as-is */ singlepotion->bknown = FALSE; - if (Blind) { - singlepotion->dknown = FALSE; - } else { - singlepotion->dknown = !Hallucination; + singlepotion->dknown = FALSE; /* provisionally */ + if (!Blind) { + if (!Hallucination) + observe_object(singlepotion); *newbuf = '\0'; if (mixture == POT_WATER && singlepotion->dknown) Sprintf(newbuf, "clears"); @@ -2755,7 +2773,7 @@ potion_dip(struct obj *obj, struct obj *potion) struct obj fakeobj; fakeobj = cg.zeroobj; - fakeobj.dknown = 1; + fakeobj.dknown = 1; /* no need to observe_object */ fakeobj.otyp = old_otyp; fakeobj.oclass = POTION_CLASS; docall(&fakeobj); diff --git a/src/pray.c b/src/pray.c index d2b505b05..0c38f9f0a 100644 --- a/src/pray.c +++ b/src/pray.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 pray.c $NHDT-Date: 1727250729 2024/09/25 07:52:09 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.220 $ */ +/* NetHack 3.7 pray.c $NHDT-Date: 1762680996 2025/11/09 01:36:36 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.244 $ */ /* Copyright (c) Benson I. Margulies, Mike Stephenson, Steve Linhart, 1989. */ /* NetHack may be freely redistributed. See license for details. */ @@ -215,7 +215,7 @@ in_trouble(void) return TROUBLE_STARVING; if (region_danger()) return TROUBLE_REGION; - if (critically_low_hp(FALSE)) + if ((!Upolyd || Unchanging) && critically_low_hp(FALSE)) return TROUBLE_HIT; if (ismnum(u.ulycn)) return TROUBLE_LYCANTHROPE; @@ -548,7 +548,7 @@ fix_worst_trouble(int trouble) disp.botl = TRUE; } } - (void) encumber_msg(); + encumber_msg(); break; case TROUBLE_BLIND: { /* handles deafness as well as blindness */ char msgbuf[BUFSZ]; @@ -658,15 +658,15 @@ god_zaps_you(aligntyp resp_god) */ if (uarms && !(EReflecting & W_ARMS) && !(EDisint_resistance & W_ARMS)) - (void) destroy_arm(uarms); + (void) disintegrate_arm(uarms); if (uarmc && !(EReflecting & W_ARMC) && !(EDisint_resistance & W_ARMC)) - (void) destroy_arm(uarmc); + (void) disintegrate_arm(uarmc); if (uarm && !(EReflecting & W_ARM) && !(EDisint_resistance & W_ARM) && !uarmc) - (void) destroy_arm(uarm); + (void) disintegrate_arm(uarm); if (uarmu && !uarm && !uarmc) - (void) destroy_arm(uarmu); + (void) disintegrate_arm(uarmu); if (!Disint_resistance) { fry_by_god(resp_god, TRUE); monstunseesu(M_SEEN_DISINT); @@ -877,7 +877,7 @@ gcrownu(void) * even if hero doesn't know book */ bless(obj); obj->bknown = 1; /* ok to skip set_bknown() */ - obj->dknown = 1; + observe_object(obj); at_your_feet(upstart(ansimpleoname(obj))); dropy(obj); u.ugifts++; @@ -921,7 +921,7 @@ gcrownu(void) ; /* already got bonus above */ } else if (obj && in_hand) { Your("%s goes snicker-snack!", xname(obj)); - obj->dknown = 1; + observe_object(obj); } else if (!already_exists) { obj = mksobj(LONG_SWORD, FALSE, FALSE); obj = oname(obj, artiname(ART_VORPAL_BLADE), @@ -947,7 +947,7 @@ gcrownu(void) ; /* already got bonus above */ } else if (obj && in_hand) { Your("%s hums ominously!", swordbuf); - obj->dknown = 1; + observe_object(obj); } else if (!already_exists) { obj = mksobj(RUNESWORD, FALSE, FALSE); obj = oname(obj, artiname(ART_STORMBRINGER), @@ -1050,7 +1050,8 @@ give_spell(void) } obfree(otmp, (struct obj *) 0); /* discard the book */ } else { - otmp->dknown = 1; /* not bknown */ + observe_object(otmp); + /* don't set bknown */ /* discovering blank paper will make it less likely to be given again; small chance to arbitrarily discover some other book type without having to read it first */ @@ -1261,7 +1262,7 @@ pleased(aligntyp g_align) if (ABASE(A_STR) < AMAX(A_STR)) { ABASE(A_STR) = AMAX(A_STR); disp.botl = TRUE; /* before potential message */ - (void) encumber_msg(); + encumber_msg(); } if (u.uhunger < 900) init_uhunger(); @@ -1822,7 +1823,7 @@ bestow_artifact(uchar max_giftvalue) /* make sure we can use this weapon */ unrestrict_weapon_skill(weapon_type(otmp)); if (!Hallucination && !Blind) { - otmp->dknown = 1; + observe_object(otmp); makeknown(otmp->otyp); discover_artifact(otmp->oartifact); } @@ -1858,7 +1859,7 @@ dosacrifice(void) You("are not %s an altar.", (Levitation || Flying) ? "over" : "on"); return ECMD_OK; - } else if (Confusion || Stunned || Hallucination) { + } else if (Confusion || Stunned) { You("are too impaired to perform the rite."); return ECMD_OK; } @@ -2205,12 +2206,12 @@ dopray(void) if (ParanoidPray) { ok = paranoid_query(ParanoidConfirm, "Are you sure you want to pray?"); - +#if 0 /* clear command recall buffer; otherwise ^A to repeat p(ray) would do so without confirmation (if 'ok') or do nothing (if '!ok') */ cmdq_clear(CQ_REPEAT); cmdq_add_ec(CQ_REPEAT, dopray); - +#endif if (!ok) /* declined the "are you sure?" confirmation */ return ECMD_OK; } diff --git a/src/priest.c b/src/priest.c index 10e83accc..cb480a757 100644 --- a/src/priest.c +++ b/src/priest.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 priest.c $NHDT-Date: 1726862063 2024/09/20 19:54:23 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.103 $ */ +/* NetHack 3.7 priest.c $NHDT-Date: 1764567778 2025/11/30 21:42:58 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.106 $ */ /* Copyright (c) Izchak Miller, Steve Linhart, 1989. */ /* NetHack may be freely redistributed. See license for details. */ @@ -7,7 +7,7 @@ /* these match the categorizations shown by enlightenment */ #define ALGN_SINNED (-4) /* worse than strayed (-1..-3) */ -#define ALGN_PIOUS 14 /* better than fervent (9..13) */ +#define ALGN_DEVOUT 14 /* better than fervent (9..13) */ staticfn boolean histemple_at(struct monst *, coordxy, coordxy); staticfn boolean has_shrine(struct monst *); @@ -46,8 +46,7 @@ move_special(struct monst *mtmp, boolean in_his_shop, schar appr, coordxy nx, ny, nix, niy; schar i; schar chcnt, cnt; - coord poss[9]; - long info[9]; + struct mfndposdata mfp; long ninfo = 0; long allowflags; #if 0 /* dead code; see below */ @@ -64,11 +63,11 @@ move_special(struct monst *mtmp, boolean in_his_shop, schar appr, nix = omx; niy = omy; allowflags = mon_allowflags(mtmp); - cnt = mfndpos(mtmp, poss, info, allowflags); + cnt = mfndpos(mtmp, &mfp, allowflags); if (mtmp->isshk && avoid && uondoor) { /* perhaps we cannot avoid him */ for (i = 0; i < cnt; i++) - if (!(info[i] & NOTONL)) + if (!(mfp.info[i] & NOTONL)) goto pick_move; avoid = FALSE; } @@ -77,18 +76,18 @@ move_special(struct monst *mtmp, boolean in_his_shop, schar appr, pick_move: chcnt = 0; for (i = 0; i < cnt; i++) { - nx = poss[i].x; - ny = poss[i].y; + nx = mfp.poss[i].x; + ny = mfp.poss[i].y; if (IS_ROOM(levl[nx][ny].typ) || (mtmp->isshk && (!in_his_shop || ESHK(mtmp)->following))) { - if (avoid && (info[i] & NOTONL) && !(info[i] & ALLOW_M)) + if (avoid && (mfp.info[i] & NOTONL) && !(mfp.info[i] & ALLOW_M)) continue; if ((!appr && !rn2(++chcnt)) || (appr && GDIST(nx, ny) < GDIST(nix, niy)) - || (info[i] & ALLOW_M)) { + || (mfp.info[i] & ALLOW_M)) { nix = nx; niy = ny; - ninfo = info[i]; + ninfo = mfp.info[i]; } } } @@ -480,7 +479,7 @@ intemple(int roomno) other_time = &epri_p->peaceful_time; } else { msg1 = "experience %s sense of peace."; - msg2 = (u.ualign.record >= ALGN_PIOUS) ? "a" : "an unusual"; + msg2 = (u.ualign.record >= ALGN_DEVOUT) ? "a" : "an unusual"; this_time = &epri_p->peaceful_time; other_time = &epri_p->hostile_time; } @@ -560,6 +559,14 @@ priest_talk(struct monst *priest) { boolean coaligned = p_coaligned(priest); boolean strayed = (u.ualign.record < 0); + unsigned *cheapskate = NULL; + if (EPRI(priest)) cheapskate = &EPRI(priest)->cheapskate_count; + + /* + * Note: we won't be called if hero is Deaf [since dochat() will + * return before calling domonnoise()], so we don't need to check + * for that before the various calls to verbalize() here. + */ /* KMH, conduct */ if (!u.uconduct.gnostic++) @@ -620,53 +627,87 @@ priest_talk(struct monst *priest) pline("%s is not interested.", Monnam(priest)); return; } else { + /* there's now some randomization in how much you need to donate, but + you are given suggested donation values that will guarantee + clairvoyance and protection respectively; with more gold visible + you need to donate more but get a greater effect; and if you + cheapskate out to rerandomize the donation amounts they will be + higher next time */ long offer; + long suggested = (u.ulevelpeak ? u.ulevelpeak : 1 ) * + rn1(101, 150 + (cheapskate ? *cheapskate : 0) * 40); + long quan = money_cnt(gi.invent) / (suggested * 3); + char buf[BUFSZ]; - pline("%s asks you for a contribution for the temple.", - Monnam(priest)); - if ((offer = bribe(priest)) == 0) { + if (quan < 1) + quan = 1; + + Sprintf(buf, "How much will you offer (suggested: %ld or %ld)?", + suggested * quan, suggested * quan * 2); + + if (flags.debug) + pline("%s asks you for a contribution for the temple (base %ld).", + Monnam(priest), suggested); + else + pline("%s asks you for a contribution for the temple.", + Monnam(priest)); + if ((offer = bribe(priest, buf)) == 0) { SetVoice(priest, 0, 80, 0); verbalize("Thou shalt regret thine action!"); if (coaligned) adjalign(-1); - } else if (offer < (u.ulevel * 200)) { + if (cheapskate) ++*cheapskate; + } else if (offer < suggested * quan) { if (money_cnt(gi.invent) > (offer * 2L)) { SetVoice(priest, 0, 80, 0); verbalize("Cheapskate."); + if (cheapskate) ++*cheapskate; } else { SetVoice(priest, 0, 80, 0); verbalize("I thank thee for thy contribution."); /* give player some token */ exercise(A_WIS, TRUE); } - } else if (offer < (u.ulevel * 400)) { + } else if (offer < suggested * quan * 2) { SetVoice(priest, 0, 80, 0); verbalize("Thou art indeed a pious individual."); if (money_cnt(gi.invent) < (offer * 2L)) { if (coaligned && u.ualign.record <= ALGN_SINNED) adjalign(1); - verbalize("I bestow upon thee a blessing."); - incr_itimeout(&HClairvoyant, rn1(500, 500)); } - } else if (offer < (u.ulevel * 600) - /* u.ublessed is only active when Protection is - enabled via something other than worn gear - (theft by gremlin clears the intrinsic but not - its former magnitude, making it recoverable) */ - && (!(HProtection & INTRINSIC) - || (u.ublessed < 20 - && (u.ublessed < 9 || !rn2(u.ublessed))))) { - SetVoice(priest, 0, 80, 0); - verbalize("Thou hast been rewarded for thy devotion."); + verbalize("I bestow upon thee a blessing."); + incr_itimeout(&HClairvoyant, rn1(500 * offer / suggested, + 500 * offer / suggested)); + } else if (offer < suggested * quan * 3) { + int orig_ublessed = u.ublessed; + + /* u.ublessed is only active when Protection is enabled via + something other than worn gear (theft by gremlin clears the + intrinsic but not its former magnitude, making it + recoverable) */ if (!(HProtection & INTRINSIC)) { HProtection |= FROMOUTSIDE; + orig_ublessed = -1; /* force "rewarded" message */ + } + + for (; offer >= (2 * suggested); offer -= (2 * suggested)) { if (!u.ublessed) u.ublessed = rn1(3, 2); - } else - u.ublessed++; + else if (u.ublessed < 20 && + (u.ublessed < 9 || !rn2(u.ublessed))) + u.ublessed++; + } + SetVoice(priest, 0, 80, 0); + if (u.ublessed > orig_ublessed) { + verbalize("Thou hast been rewarded for thy devotion."); + } else { + verbalize("Thy selfless generosity is deeply appreciated."); + } } else { SetVoice(priest, 0, 80, 0); verbalize("Thy selfless generosity is deeply appreciated."); + /* money_cnt check is preserved for futureproofing but probably + can't fail in the current code */ if (money_cnt(gi.invent) < (offer * 2L) && coaligned) { if (strayed && (svm.moves - u.ucleansed) > 5000L) { u.ualign.record = 0; /* cleanse thee */ @@ -898,6 +939,6 @@ restpriest(struct monst *mtmp, boolean ghostly) } #undef ALGN_SINNED -#undef ALGN_PIOUS +#undef ALGN_DEVOUT /*priest.c*/ diff --git a/src/quest.c b/src/quest.c index b554a664c..2b54baa86 100644 --- a/src/quest.c +++ b/src/quest.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 quest.c $NHDT-Date: 1687036547 2023/06/17 21:15:47 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.38 $ */ +/* NetHack 3.7 quest.c $NHDT-Date: 1774269965 2026/03/23 04:46:05 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.46 $ */ /* Copyright 1991, M. Stephenson */ /* NetHack may be freely redistributed. See license for details. */ @@ -127,7 +127,7 @@ artitouch(struct obj *obj) if (!Qstat(touched_artifact)) { /* in case we haven't seen the item yet (ie, currently blinded), this quest message describes it by name so mark it as seen */ - obj->dknown = 1; + observe_object(obj); /* only give this message once */ Qstat(touched_artifact) = TRUE; qt_pager("gotit"); @@ -339,6 +339,12 @@ chat_with_leader(struct monst *mtmp) com_pager("banished"); Qstat(pissed_off) = TRUE; expulsion(FALSE); + + /* being expelled is hardly an achievement but none of the + other livelog classifications fit */ + livelog_printf(LL_ACHIEVE, + "%s has expelled you from the quest", + noit_mon_nam(mtmp)); } } else if (purity == 0) { qt_pager("badalign"); @@ -349,6 +355,14 @@ chat_with_leader(struct monst *mtmp) qt_pager("assignquest"); exercise(A_WIS, TRUE); Qstat(got_quest) = TRUE; + + /* phrasing is a bit clumsy but allows #chronicle to provide a + clue to players who are reaching the quest for first time; + matters most for Home 1 that has stairs down which aren't + easily found */ + livelog_printf(LL_ACHIEVE, + "%s has granted access to proceed deeper into the quest", + noit_mon_nam(mtmp)); } } } diff --git a/src/read.c b/src/read.c index 5ebe8ab64..b422ee082 100644 --- a/src/read.c +++ b/src/read.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 read.c $NHDT-Date: 1715889745 2024/05/16 20:02:25 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.308 $ */ +/* NetHack 3.7 read.c $NHDT-Date: 1762577372 2025/11/07 20:49:32 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.323 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -22,6 +22,7 @@ staticfn int maybe_tame(struct monst *, struct obj *); staticfn boolean can_center_cloud(coordxy, coordxy); staticfn void display_stinking_cloud_positions(boolean); staticfn void seffect_enchant_armor(struct obj **); +staticfn boolean disintegrate_cursed_armor(void); staticfn void seffect_destroy_armor(struct obj **); staticfn void seffect_confuse_monster(struct obj **); staticfn void seffect_scare_monster(struct obj **); @@ -1187,12 +1188,40 @@ seffect_enchant_armor(struct obj **sobjp) useup(otmp); return; } - s = scursed ? -1 - : (otmp->spe >= 9) - ? (rn2(otmp->spe) == 0) - : sblessed - ? rnd(3 - otmp->spe / 3) - : 1; + if (s < -100) + s = -100; /* avoid integer overflow with very negative armor */ + + /* Base power of the enchantment: + + 2 for -1 to +0 armor; + 1 for +1 to +2 armor; + 0 for +3 to +4 armor, etc. + + When disenchanting, everything is done with reversed signs. */ + s = (4 - s) / 2; + + /* Elven/artifact and nonmagical armor is easier to enchant; + blessed scrolls are more effective. */ + if (special_armor) + ++s; + if (!objects[otmp->otyp].oc_magic) + ++s; + if (sblessed) + ++s; + + if (s <= 0) { + s = 0; + if (otmp->spe > 0 && !rn2(otmp->spe)) + s = 1; + } else { + s = rnd(s); + } + if (s > 11) + s = 11; /* unlikely but possible: avoids an overflow later */ + + if (scursed) + s = -s; + if (s >= 0 && Is_dragon_scales(otmp)) { unsigned was_lit = otmp->lamplit; int old_light = artifact_light(otmp) ? arti_light_radius(otmp) : 0; @@ -1260,6 +1289,37 @@ seffect_enchant_armor(struct obj **sobjp) Blind ? "again" : "unexpectedly"); } +/* destroy a random cursed armor worn by hero */ +staticfn boolean +disintegrate_cursed_armor(void) +{ + struct obj *armors[10]; + int idx = 0; + + armors[0] = NULL; + if (uarm && uarm->cursed) + armors[idx++] = uarm; + if (uarmc && uarmc->cursed) + armors[idx++] = uarmc; + if (uarmh && uarmh->cursed) + armors[idx++] = uarmh; + if (uarms && uarms->cursed) + armors[idx++] = uarms; + if (uarmg && uarmg->cursed) + armors[idx++] = uarmg; + if (uarmf && uarmf->cursed) + armors[idx++] = uarmf; + if (uarmu && uarmu->cursed) + armors[idx++] = uarmu; + if (!idx) + return FALSE; + + if (disintegrate_arm(armors[rn2(idx)])) + return TRUE; + + return FALSE; +} + staticfn void seffect_destroy_armor(struct obj **sobjp) { @@ -1289,7 +1349,21 @@ seffect_destroy_armor(struct obj **sobjp) otmp->oerodeproof = new_erodeproof ? 1 : 0; return; } - if (!scursed || !otmp || !otmp->cursed) { + + if (scursed) { + if (otmp && otmp->cursed) { + /* armor and scroll both cursed */ + pline("%s.", Yobjnam2(otmp, "vibrate")); + if (otmp->spe >= -6) { + otmp->spe += -1; + adj_abon(otmp, -1); + } + make_stunned((HStun & TIMEOUT) + (long) rn1(10, 10), TRUE); + } else if (disintegrate_arm(otmp)) { + gk.known = TRUE; + return; + } + } else { boolean gets_choice = (otmp && sobj && sobj->blessed && count_worn_armor() > 1); @@ -1303,9 +1377,14 @@ seffect_destroy_armor(struct obj **sobjp) /* check the return value, if user picked non-valid obj */ if (any_worn_armor_ok(atmp) == GETOBJ_SUGGEST) otmp = atmp; - } - - if (!destroy_arm(otmp)) { + if (disintegrate_arm(otmp)) { + gk.known = TRUE; + return; + } + } else if (sobj->blessed && disintegrate_cursed_armor()) { + gk.known = TRUE; + return; + } else if (!destroy_arm()) { strange_feeling(sobj, "Your skin itches."); *sobjp = 0; /* useup() in strange_feeling() */ exercise(A_STR, FALSE); @@ -1313,13 +1392,6 @@ seffect_destroy_armor(struct obj **sobjp) return; } else gk.known = TRUE; - } else { /* armor and scroll both cursed */ - pline("%s.", Yobjnam2(otmp, "vibrate")); - if (otmp->spe >= -6) { - otmp->spe += -1; - adj_abon(otmp, -1); - } - make_stunned((HStun & TIMEOUT) + (long) rn1(10, 10), TRUE); } } @@ -1559,6 +1631,7 @@ seffect_enchant_weapon(struct obj **sobjp) boolean scursed = sobj->cursed; boolean confused = (Confusion != 0); boolean old_erodeproof, new_erodeproof; + int s; /* [What about twoweapon mode? Proofing/repairing/enchanting both would be too powerful, but shouldn't we choose randomly between @@ -1591,11 +1664,12 @@ seffect_enchant_weapon(struct obj **sobjp) uwep->oerodeproof = new_erodeproof ? 1 : 0; return; } - if (!chwepon(sobj, scursed ? -1 - : !uwep ? 1 - : (uwep->spe >= 9) ? !rn2(uwep->spe) - : sblessed ? rnd(3 - uwep->spe / 3) - : 1)) + s = scursed ? -1 + : !uwep ? 1 /* guard further tests against null pointer */ + : (uwep->spe >= 9) ? (rn2(uwep->spe) == 0) /* usually 0, maybe 1 */ + : sblessed ? rnd(3 - uwep->spe / 3) /* >= 9 case prevents rnd(0) */ + : 1; /* uncursed */ + if (!chwepon(sobj, s)) *sobjp = 0; /* nothing enchanted: strange_feeling -> useup */ if (uwep) cap_spe(uwep); @@ -2755,7 +2829,7 @@ do_genocide( * 3 = forced genocide of player * 5 (4 | 1) = normal genocide from throne */ { - char buf[BUFSZ], promptbuf[QBUFSZ]; + char buf[BUFSZ], realbuf[BUFSZ], promptbuf[QBUFSZ]; int i, killplayer = 0; int mndx; struct permonst *ptr; @@ -2860,27 +2934,31 @@ do_genocide( } which = "all "; + Strcpy(realbuf, ptr->pmnames[NEUTRAL]); /* standard singular */ if (Hallucination) { - if (Upolyd) + /* hallucinate hero's type */ + if (Upolyd) { Strcpy(buf, pmname(gy.youmonst.data, flags.female ? FEMALE : MALE)); - else { + } else { Strcpy(buf, (flags.female && gu.urole.name.f) ? gu.urole.name.f - : gu.urole.name.m); + : gu.urole.name.m); buf[0] = lowc(buf[0]); } } else { - Strcpy(buf, ptr->pmnames[NEUTRAL]); /* standard singular */ + /* use actual type */ + Strcpy(buf, realbuf); if ((ptr->geno & G_UNIQ) && ptr != &mons[PM_HIGH_CLERIC]) which = !type_is_pname(ptr) ? "the " : ""; } + if (how & REALLY) { if (!num_genocides()) livelog_printf(LL_CONDUCT | LL_GENOCIDE, "performed %s first genocide (%s)", - uhis(), makeplural(buf)); + uhis(), makeplural(realbuf)); else - livelog_printf(LL_GENOCIDE, "genocided %s", makeplural(buf)); + livelog_printf(LL_GENOCIDE, "genocided %s", makeplural(realbuf)); /* setting no-corpse affects wishing and random tin generation */ svm.mvitals[mndx].mvflags |= (G_GENOD | G_NOCORPSE); @@ -2902,13 +2980,13 @@ do_genocide( } /* Polymorphed characters will die as soon as they're rehumanized. - */ - /* KMH -- Unchanging prevents rehumanization */ + KMH -- Unchanging prevents rehumanization. */ if (Upolyd && ptr != gy.youmonst.data) { delayed_killer(POLYMORPH, svk.killer.format, svk.killer.name); You_feel("%s inside.", udeadinside()); - } else + } else { done(GENOCIDED); + } } else if (ptr == gy.youmonst.data) { rehumanize(); } diff --git a/src/restore.c b/src/restore.c index 59bc231ad..33066d662 100644 --- a/src/restore.c +++ b/src/restore.c @@ -351,6 +351,9 @@ restmon(NHFILE *nhfp, struct monst *mtmp) if (buflen > 0) { newedog(mtmp); Sfi_edog(nhfp, EDOG(mtmp), "monst-edog"); + /* save or bones held a relative time */ + relative_time_to_moves(&EDOG(mtmp)->droptime); + relative_time_to_moves(&EDOG(mtmp)->hungrytime); /* sanity check to prevent rn2(0) */ if (EDOG(mtmp)->apport <= 0) { EDOG(mtmp)->apport = 1; @@ -550,6 +553,8 @@ restgamestate(NHFILE *nhfp) #endif /* SFCTOOL */ newgamecontext = svc.context; /* copy statically init'd context */ Sfi_context_info(nhfp, &svc.context, "gamestate-context"); + relative_time_to_moves(&svc.context.seer_turn); + relative_time_to_moves(&svc.context.digging.lastdigtime); svc.context.warntype.species = (ismnum(svc.context.warntype.speciesidx)) ? &mons[svc.context.warntype.speciesidx] : (struct permonst *) 0; @@ -1097,7 +1102,7 @@ getlev(NHFILE *nhfp, int pid, xint8 lev) for (c = 0; c < COLNO; ++c) { for (r = 0; r < ROWNO; ++r) { Sfi_schar(nhfp, &svl.lastseentyp[c][r], "lastseentyp"); - } + } } Sfi_long(nhfp, &svo.omoves, "lev-timestmp"); elapsed = (svm.moves - svo.omoves); @@ -1106,7 +1111,7 @@ getlev(NHFILE *nhfp, int pid, xint8 lev) Sfi_dest_area(nhfp, &svu.updest, "lev-updest"); Sfi_dest_area(nhfp, &svd.dndest, "lev-dndest"); Sfi_levelflags(nhfp, &svl.level.flags, "lev-level_flags"); - + rest_adjust_levelflags(); if (svd.doors) { free(svd.doors); svd.doors = 0; @@ -1208,7 +1213,7 @@ getlev(NHFILE *nhfp, int pid, xint8 lev) them is different now than when the level was saved */ restore_cham(mtmp); /* give hiders a chance to hide before their next move */ - if (ghostly || (elapsed > 00 && elapsed > (long) rnd(10))) + if (ghostly || (elapsed > 0L && elapsed > (long) rnd(10))) hide_monst(mtmp); } #endif /* !SFCTOOL */ @@ -1302,6 +1307,28 @@ getlev(NHFILE *nhfp, int pid, xint8 lev) program_state.in_getlev = FALSE; } +void +rest_adjust_levelflags(void) +{ + /* adjust timestamps */ + relative_time_to_moves(&svl.level.flags.stasis_until); +} +void +moves_to_relative_time(long *timestamp) +{ + long prevts = *timestamp; + + *timestamp = prevts - svm.moves; +} + +void +relative_time_to_moves(long *timestamp) +{ + long prevts = *timestamp; + + *timestamp = svm.moves + prevts; +} + /* "name-role-race-gend-algn" occurs very early in a save file; sometimes we want the whole thing, other times just "name" (for svp.plname[]) */ void diff --git a/src/rip.c b/src/rip.c index d482eb0a2..c9c64dfae 100644 --- a/src/rip.c +++ b/src/rip.c @@ -9,7 +9,8 @@ necessarily have to be used by a binary with multiple window-ports */ #if defined(TTY_GRAPHICS) || defined(X11_GRAPHICS) || defined(GEM_GRAPHICS) \ - || defined(DUMPLOG) || defined(CURSES_GRAPHICS) || defined(SHIM_GRAPHICS) + || defined(DUMPLOG) || defined(CURSES_GRAPHICS) || defined(SHIM_GRAPHICS) \ + || defined(AMII_GRAPHICS) #define TEXT_TOMBSTONE #endif #if defined(mac) || defined(__BEOS__) diff --git a/src/role.c b/src/role.c index e95d3f121..8d4feaab2 100644 --- a/src/role.c +++ b/src/role.c @@ -2084,6 +2084,9 @@ role_init(void) /* 0 or 1; no gods are neuter, nor is gender randomized */ svq.quest_status.godgend = !strcmpi(align_gtitle(alignmnt), "goddess"); + if (Role_if(PM_CLERIC)) + objects[SPE_LIGHT].oc_skill = P_CLERIC_SPELL; + #if 0 /* * Disable this fixup so that mons[] can be const. The only diff --git a/src/save.c b/src/save.c index 57c8bfb41..fd6dfae2f 100644 --- a/src/save.c +++ b/src/save.c @@ -31,7 +31,7 @@ staticfn void save_gamelog(NHFILE *); staticfn void savegamestate(NHFILE *); staticfn void savelev_core(NHFILE *, xint8); staticfn void save_msghistory(NHFILE *); - +staticfn void save_adjust_levelflags(void); #if defined(HANGUPHANDLING) #define HUP if (!program_state.done_hup) #else @@ -270,7 +270,12 @@ savegamestate(NHFILE *nhfp) program_state.saving++; /* caller should/did already set this... */ uid = (unsigned long) getuid(); Sfo_ulong(nhfp, &uid, "gamestate-uid"); + moves_to_relative_time(&svc.context.seer_turn); + moves_to_relative_time(&svc.context.digging.lastdigtime); Sfo_context_info(nhfp, &svc.context, "gamestate-context"); + relative_time_to_moves(&svc.context.seer_turn); + relative_time_to_moves(&svc.context.digging.lastdigtime); + Sfo_flag(nhfp, &flags, "gamestate-flags"); urealtime.finish_time = getnow(); urealtime.realtime += timet_delta(urealtime.finish_time, @@ -509,7 +514,9 @@ savelev_core(NHFILE *nhfp, xint8 lev) save_stairs(nhfp); Sfo_dest_area(nhfp, &svu.updest, "lev-updest"); Sfo_dest_area(nhfp, &svd.dndest, "lev-dndest"); + save_adjust_levelflags(); Sfo_levelflags(nhfp, &svl.level.flags, "lev-level_flags"); + rest_adjust_levelflags(); Sfo_int(nhfp, &svd.doors_alloc, "lev-doors_alloc"); /* don't rely on underlying write() behavior to write @@ -556,6 +563,13 @@ savelev_core(NHFILE *nhfp, xint8 lev) return; } +void +save_adjust_levelflags(void) +{ + /* adjust any timestamps */ + moves_to_relative_time(&svl.level.flags.stasis_until); +} + staticfn void savelevl(NHFILE *nhfp) { @@ -844,7 +858,12 @@ savemon(NHFILE *nhfp, struct monst *mtmp) buflen = EDOG(mtmp) ? (int) sizeof (struct edog) : 0; Sfo_int(nhfp, &buflen, "monst-edog_length"); if (buflen > 0) { + /* we only store relative times in save and bones */ + moves_to_relative_time(&EDOG(mtmp)->droptime); + moves_to_relative_time(&EDOG(mtmp)->hungrytime); Sfo_edog(nhfp, EDOG(mtmp), "monst-edog"); + relative_time_to_moves(&EDOG(mtmp)->droptime); + relative_time_to_moves(&EDOG(mtmp)->hungrytime); } buflen = EBONES(mtmp) ? (int) sizeof (struct ebones) : 0; Sfo_int(nhfp, &buflen, "monst-ebones_length"); @@ -1109,7 +1128,9 @@ freedynamicdata(void) freeroleoptvals(); /* saveoptvals(&tnhfp) */ cmdq_clear(CQ_CANNED); cmdq_clear(CQ_REPEAT); + cmdbind_freeall(); free_tutorial(); /* (only needed if quitting while in tutorial) */ + wish_history_flush(); /* per-turn data, but might get added to when freeing other stuff */ dobjsfree(); /* really free deleted objects */ diff --git a/src/selvar.c b/src/selvar.c index 5d08ca95e..a35b9f7bd 100644 --- a/src/selvar.c +++ b/src/selvar.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 selvar.c $NHDT-Date: 1709677544 2024/03/05 22:25:44 $ $NHDT-Branch: keni-mdlib-followup $:$NHDT-Revision: 1.360 $ */ +/* NetHack 3.7 selvar.c $NHDT-Date: 1769840272 2026/01/30 22:17:52 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.4 $ */ /* Copyright (c) 2024 by Pasi Kallinen */ /* NetHack may be freely redistributed. See license for details. */ @@ -189,11 +189,18 @@ selection_setpoint( return; if (c && !sel->bounds_dirty) { - if (sel->bounds.lx > x) sel->bounds.lx = x; - if (sel->bounds.ly > y) sel->bounds.ly = y; - if (sel->bounds.hx < x) sel->bounds.hx = x; - if (sel->bounds.hy < y) sel->bounds.hy = y; - } else { + if (sel->bounds.lx > x) + sel->bounds.lx = x; + if (sel->bounds.ly > y) + sel->bounds.ly = y; + if (sel->bounds.hx < x) + sel->bounds.hx = x; + if (sel->bounds.hy < y) + sel->bounds.hy = y; + + /* only set bounds_dirty if changing a point from 1 to 0; if changing + a point from 0 to 0, nothing has really changed with the bounds */ + } else if (sel->map[sel->wid * y + x] != 0) { sel->bounds_dirty = TRUE; } diff --git a/src/sfbase.c b/src/sfbase.c index 0922839d1..0cd70af4e 100644 --- a/src/sfbase.c +++ b/src/sfbase.c @@ -98,8 +98,6 @@ void sf_log(NHFILE *, const char *, size_t, int, char *); #define Sfvalue_uint16(a) sfvalue(a) #define Sfvalue_uint32(a) sfvalue(a) #define Sfvalue_uint64(a) sfvalue(a) -#define Sfvalue_size_t(a) sfvalue(a) -#define Sfvalue_time_t(a) sfvalue(a) #define Sfvalue_short(a) sfvalue(a) #define Sfvalue_ushort(a) sfvalue(a) #define Sfvalue_int(a) sfvalue(a) @@ -115,6 +113,8 @@ void sf_log(NHFILE *, const char *, size_t, int, char *); #define Sfvalue_boolean(a) sfvalue_boolean(a) #define Sfvalue_schar(a) sfvalue_schar(a) #define Sfvalue_bitfield(a) sfvalue_bitfield(a) +#define Sfvalue_time_t(a) sfvalue_time_t(a) +#define Sfvalue_size_t(a) sfvalue_size_t(a) #define SF_A(dtyp) \ void sfo_##dtyp(NHFILE *nhfp, dtyp *d_##dtyp, const char *myname) \ diff --git a/src/shk.c b/src/shk.c index 3463effbd..927352971 100644 --- a/src/shk.c +++ b/src/shk.c @@ -433,6 +433,65 @@ setpaid(struct monst *shkp) } } +/* Remembers that a shopkeeper has quoted a particular price for a + particular type of object. */ +void +record_price_quote(int otyp, unsigned long price, boolean buyprice) { + struct objclass *oc = &objects[otyp]; + if (buyprice) { + if (price > oc->oc_buy_maxseen) oc->oc_buy_maxseen = price; + if (price < oc->oc_buy_minseen) oc->oc_buy_minseen = price; + } else { + if (price > oc->oc_sell_maxseen) oc->oc_sell_maxseen = price; + if (price < oc->oc_sell_minseen) oc->oc_sell_minseen = price; + } +} + +/* Appends price-quote information to the given buffer, updating the + given end of string position. *eos mut be buf + strlen(buf). If the + update would make bug longer than BUFSZ, instead does nothing. */ +void +append_price_quote(char *buf, char **eos, int otyp) { + char buf2[BUFSZ]; + char *eos2 = buf2; + const char *sep = ""; + size_t len = *eos - buf; + size_t len2; + + if (objects[otyp].oc_sell_minseen > objects[otyp].oc_sell_maxseen && + objects[otyp].oc_buy_minseen > objects[otyp].oc_buy_maxseen) + return; + + eos2 += sprintf(eos2, " {"); + + if (objects[otyp].oc_buy_minseen < objects[otyp].oc_buy_maxseen) { + eos2 += sprintf(eos2, "buy %lu-%lu", + objects[otyp].oc_buy_minseen, + objects[otyp].oc_buy_maxseen); + sep = " "; + } else if (objects[otyp].oc_buy_minseen == objects[otyp].oc_buy_maxseen) { + eos2 += sprintf(eos2, "buy %lu", + objects[otyp].oc_buy_minseen); + sep = " "; + } + + if (objects[otyp].oc_sell_minseen < objects[otyp].oc_sell_maxseen) { + eos2 += sprintf(eos2, "%ssell %lu-%lu", sep, + objects[otyp].oc_sell_minseen, + objects[otyp].oc_sell_maxseen); + } else if (objects[otyp].oc_sell_minseen == objects[otyp].oc_sell_maxseen) { + eos2 += sprintf(eos2, "%ssell %lu", sep, + objects[otyp].oc_sell_minseen); + } + + eos2 += sprintf(eos2, "}"); + len2 = eos2 - buf2; + if (len2 < BUFSZ - len - 1) { + Strcpy(*eos, buf2); + *eos += len2; + } +} + staticfn long addupbill(struct monst *shkp) { @@ -1063,6 +1122,16 @@ tended_shop(struct mkroom *sroom) return !mtmp ? FALSE : (boolean) inhishop(mtmp); } +void +noisy_shop(struct mkroom *sroom) +{ + struct monst *mtmp = sroom->resident; + + if (mtmp && inhishop(mtmp)) { + wake_nearto(mtmp->mx, mtmp->my, 11 * 11); + } +} + staticfn struct bill_x * onbill(struct obj *obj, struct monst *shkp, boolean silent) { @@ -3275,7 +3344,7 @@ add_one_tobill( bp->bo_id = obj->o_id; bp->bquan = obj->quan; if (dummy) { /* a dummy object must be inserted into */ - bp->useup = TRUE; /* the gb.billobjs chain here. crucial for */ + bp->useup = TRUE; /* the gb.billobjs chain here. crucial for */ add_to_billobjs(obj); /* eating floorfood in shop. see eat.c */ } else bp->useup = FALSE; @@ -3290,6 +3359,7 @@ add_one_tobill( } eshkp->billct++; obj->unpaid = 1; + record_price_quote(obj->otyp, bp->price, TRUE); } staticfn void @@ -3350,7 +3420,7 @@ shk_names_obj( char *obj_name, fmtbuf[BUFSZ]; boolean was_unknown = !obj->dknown; - obj->dknown = TRUE; + observe_object(obj); /* Use real name for ordinary weapons/armor, and spell-less * scrolls/books (that is, blank and mail), but only if the * object is within the shk's area of interest/expertise. @@ -3482,6 +3552,9 @@ addtobill( if (!Deaf && !muteshk(shkp) && !silent) { char buf[BUFSZ]; + /* no need to update price quotes here; it was done by + add_one_tobill above */ + if (!ltmp) { pline("%s has no interest in %s.", Shknam(shkp), the(xname(obj))); return; @@ -3981,6 +4054,7 @@ sellobj( pline("%s cannot pay you at present.", Shknam(shkp)); Sprintf(qbuf, "Will you accept %ld %s in credit for ", tmpcr, currency(tmpcr)); + record_price_quote(obj->otyp, tmpcr / obj->quan, FALSE); c = ynaq(safe_qbuf(qbuf, qbuf, "?", obj, doname, thesimpleoname, (obj->quan == 1L) ? "that" : "those")); if (c == 'a') { @@ -4076,6 +4150,7 @@ sellobj( : and_its_contents) : "", one ? "it" : "them"); + record_price_quote(obj->otyp, offer / obj->quan, FALSE); (void) safe_qbuf(qbuf, qbuf, qsfx, obj, xname, simpleonames, one ? "that" : "those"); } else diff --git a/src/shknam.c b/src/shknam.c index ecf12f938..657487ed8 100644 --- a/src/shknam.c +++ b/src/shknam.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 shknam.c $NHDT-Date: 1736530208 2025/01/10 09:30:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.82 $ */ +/* NetHack 3.7 shknam.c $NHDT-Date: 1764109114 2025/11/25 22:18:34 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.86 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -682,6 +682,10 @@ shkinit(const struct shclass *shp, struct mkroom *sroom) mkmonmoney(shk, 1000L + 30L * (long) rnd(100)); /* initial capital */ if (shp->shknms == shkrings) (void) mongets(shk, TOUCHSTONE); + if (shp->shknms == shktools || shp->shknms == shkwands || + (shp->shknms == shkrings && rn2(2)) || + (shp->shknms == shkgeneral && rn2(5))) + (void) mongets(shk, SCR_CHARGING); nameshk(shk, shp->shknms); return sh; diff --git a/src/sit.c b/src/sit.c index 40628c1b0..6ae51fdec 100644 --- a/src/sit.c +++ b/src/sit.c @@ -7,7 +7,6 @@ #include "artifact.h" staticfn void throne_sit_effect(void); -staticfn void rndcurse_inner(boolean); staticfn int lay_an_egg(void); /* take away the hero's money */ @@ -265,9 +264,20 @@ special_throne_effect(int effect) { } break; case 6: - /* containers become cursed */ - rndcurse_inner(TRUE); + { + /* grease hands and inventory + + Same rules for which items can be affected as grease_ok in apply.c */ + struct obj *otmp; + + pline("A greasy liquid sprays all over you!"); + for (otmp = gi.invent; otmp; otmp = otmp->nobj) + if (otmp->oclass != COIN_CLASS) + otmp->greased = 1; + make_glib(rn1(101, 100)); + update_inventory(); break; + } case 7: /* lose an intrinsic */ attrcurse(); @@ -313,7 +323,8 @@ special_throne_effect(int effect) { break; } case 11: - /* polymorph effect (not blocked by magic resistance) */ + /* polymorph effect (not blocked by magic resistance, but other things + that protect from polymorphs work) */ pline("This throne was not meant for those such as you!"); You_feel("a change coming over you."); polyself(POLY_NOFLAGS); @@ -371,7 +382,8 @@ lay_an_egg(void) uegg->owt = weight(uegg); /* this sets hatch timers if appropriate */ set_corpsenm(uegg, egg_type_from_parent(u.umonnum, FALSE)); - uegg->known = uegg->dknown = 1; + uegg->known = 1; + observe_object(uegg); You("%s an egg.", eggs_in_water(gy.youmonst.data) ? "spawn" : "lay"); dropy(uegg); stackobj(uegg); @@ -551,12 +563,6 @@ dosit(void) /* curse a few inventory items at random! */ void rndcurse(void) -{ - rndcurse_inner(FALSE); -} - -staticfn void -rndcurse_inner(boolean prefer_containers) { int nobj = 0; int cnt, onum; @@ -578,13 +584,9 @@ rndcurse_inner(boolean prefer_containers) /* gold isn't subject to being cursed or blessed */ if (otmp->oclass == COIN_CLASS) continue; - if (prefer_containers && !otmp->cobj) - continue; nobj++; } cnt = rnd(6 / ((!!Antimagic) + (!!Half_spell_damage) + 1)); - if (prefer_containers) - cnt = nobj * 2; if (nobj) { for (; cnt > 0; cnt--) { onum = rnd(nobj); @@ -592,8 +594,6 @@ rndcurse_inner(boolean prefer_containers) /* as above */ if (otmp->oclass == COIN_CLASS) continue; - if (prefer_containers && !otmp->cobj) - continue; if (--onum == 0) break; /* found the target */ } diff --git a/src/sounds.c b/src/sounds.c index 599469de8..5e4a0e1cd 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -323,6 +323,7 @@ dosounds(void) "the chime of a cash register.", "Neiman and Marcus arguing!", }; You_hear1(shop_msg[rn2(2) + hallu]); + noisy_shop(sroom); } return; } diff --git a/src/sp_lev.c b/src/sp_lev.c index 3bccc196c..007d65404 100644 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -117,9 +117,7 @@ staticfn int find_montype(lua_State *, const char *, int *); staticfn int get_table_montype(lua_State *, int *); staticfn lua_Integer get_table_int_or_random(lua_State *, const char *, int); staticfn int get_table_buc(lua_State *); -staticfn int get_table_objclass(lua_State *); -staticfn int find_objtype(lua_State *, const char *); -staticfn int get_table_objtype(lua_State *); +staticfn int find_objtype(lua_State *, const char *, char); staticfn const char *get_mkroom_name(int) NONNULL; staticfn int get_table_roomtype_opt(lua_State *, const char *, int); staticfn int get_table_traptype_opt(lua_State *, const char *, int); @@ -2167,6 +2165,14 @@ create_monster(monster *m, struct mkroom *croom) if (vampshifted(mtmp) && m->appear != M_AP_MONSTER) (void) newcham(mtmp, &mons[mtmp->cham], NO_NC_FLAGS); } + if (m->m_lev_adj) { + if (mtmp->m_lev + m->m_lev_adj > 49) + mtmp->m_lev = 49; + else if (mtmp->m_lev + m->m_lev_adj < 0) + mtmp->m_lev = 0; + else + mtmp->m_lev += m->m_lev_adj; + } if (!(m->has_invent & DEFAULT_INVENT)) { /* guard against someone accidentally specifying e.g. quest nemesis * with custom inventory that lacks Bell or quest artifact but @@ -3235,6 +3241,7 @@ lspo_monster(lua_State *L) tmpmons.has_invent = DEFAULT_INVENT; tmpmons.waiting = 0; tmpmons.mm_flags = NO_MM_FLAGS; + tmpmons.m_lev_adj = 0; if (argc == 1 && lua_type(L, 1) == LUA_TSTRING) { const char *paramstr = luaL_checkstring(L, 1); @@ -3300,6 +3307,7 @@ lspo_monster(lua_State *L) tmpmons.stunned = get_table_boolean_opt(L, "stunned", FALSE); tmpmons.confused = get_table_boolean_opt(L, "confused", FALSE); tmpmons.waiting = get_table_boolean_opt(L, "waiting", FALSE); + tmpmons.m_lev_adj = get_table_int_opt(L, "m_lev_adj", 0); tmpmons.seentraps = 0; /* TODO: list of trap names to bitfield */ keep_default_invent = get_table_boolean_opt(L, "keep_default_invent", -1); @@ -3443,7 +3451,7 @@ get_table_buc(lua_State *L) return curse_state; } -staticfn int +int get_table_objclass(lua_State *L) { char *s = get_table_str_opt(L, "class", NULL); @@ -3455,13 +3463,14 @@ get_table_objclass(lua_State *L) return ret; } +/* find object otyp by text s (optionally considering oclass) */ staticfn int -find_objtype(lua_State *L, const char *s) +find_objtype(lua_State *L, const char *s, char oclass) { if (s && *s) { int i; const char *objname; - char class = 0; + char class = def_char_to_objclass(oclass); /* In objects.h, some item classes are defined without prefixes (such as "scroll of ") in their names, making some names (such @@ -3479,6 +3488,9 @@ find_objtype(lua_State *L, const char *s) { NULL, 0 } }; + if (class == MAXOCLASSES) + class = 0; + if (strstri(s, " of ")) { for (i = 0; class_prefixes[i].prefix; i++) { const char *p = class_prefixes[i].prefix; @@ -3523,11 +3535,12 @@ find_objtype(lua_State *L, const char *s) return STRANGE_OBJECT; } -staticfn int +int get_table_objtype(lua_State *L) { char *s = get_table_str_opt(L, "id", NULL); - int ret = find_objtype(L, s); + char oclass = get_table_objclass(L); + int ret = find_objtype(L, s, oclass); Free(s); return ret; @@ -3586,7 +3599,7 @@ lspo_object(lua_State *L) tmpobj.id = STRANGE_OBJECT; } else { tmpobj.class = -1; - tmpobj.id = find_objtype(L, paramstr); + tmpobj.id = find_objtype(L, paramstr, -1); } } else if (argc == 2 && lua_type(L, 1) == LUA_TSTRING && lua_type(L, 2) == LUA_TTABLE) { @@ -3599,7 +3612,7 @@ lspo_object(lua_State *L) tmpobj.id = STRANGE_OBJECT; } else { tmpobj.class = -1; - tmpobj.id = find_objtype(L, paramstr); + tmpobj.id = find_objtype(L, paramstr, -1); } } else if (argc == 3 && lua_type(L, 2) == LUA_TNUMBER && lua_type(L, 3) == LUA_TNUMBER) { @@ -3613,7 +3626,7 @@ lspo_object(lua_State *L) tmpobj.id = STRANGE_OBJECT; } else { tmpobj.class = -1; - tmpobj.id = find_objtype(L, paramstr); + tmpobj.id = find_objtype(L, paramstr, -1); } } else { lcheck_param_table(L); @@ -4477,7 +4490,7 @@ lspo_gold(lua_State *L) if (argc == 3) { amount = luaL_checkinteger(L, 1); x = gldx = luaL_checkinteger(L, 2); - y = gldy = luaL_checkinteger(L, 2); + y = gldy = luaL_checkinteger(L, 3); } else if (argc == 2 && lua_type(L, 2) == LUA_TTABLE) { amount = luaL_checkinteger(L, 1); (void) get_coord(L, 2, &gldx, &gldy); @@ -5982,8 +5995,13 @@ lspo_reset_level(lua_State *L) boolean wtower = In_W_tower(u.ux, u.uy, &u.uz); iflags.lua_testing = TRUE; - if (L) + if (L) { + if (gc.coder) { + Free(gc.coder); + gc.coder = NULL; + } create_des_coder(); + } makemap_prepost(TRUE, wtower); gi.in_mklev = TRUE; oinit(); /* assign level dependent obj probabilities */ diff --git a/src/spell.c b/src/spell.c index e3d7f2e12..43c2acd51 100644 --- a/src/spell.c +++ b/src/spell.c @@ -1,10 +1,11 @@ -/* NetHack 3.7 spell.c $NHDT-Date: 1725227807 2024/09/01 21:56:47 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.173 $ */ +/* NetHack 3.7 spell.c $NHDT-Date: 1769498874 2026/01/26 23:27:54 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.185 $ */ /* Copyright (c) M. Stephenson 1988 */ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" /* spellmenu arguments; 0..n-1 used as svs.spl_book[] index when swapping */ +#define SPELLMENU_DUMP (-3) #define SPELLMENU_CAST (-2) #define SPELLMENU_VIEW (-1) #define SPELLMENU_SORT (MAXSPELL) /* special menu entry */ @@ -99,7 +100,7 @@ staticfn void propagate_chain_lightning(struct chain_lightning_queue *, * Fighters find body armour & shield a little less limiting. * Headgear, Gauntlets and Footwear are not role-specific (but * still have an effect, except helm of brilliance, which is designed - * to permit magic-use). + * to permit magic use). */ #define uarmhbon 4 /* Metal helmets interfere with the mind */ @@ -190,7 +191,7 @@ confused_book(struct obj *spellbook) boolean gone = FALSE; if (!rn2(3) && spellbook->otyp != SPE_BOOK_OF_THE_DEAD) { - spellbook->in_use = TRUE; /* in case called from learn */ + spellbook->in_use = TRUE; /* in case called from learn() */ pline( "Being confused you have difficulties in controlling your actions."); display_nhwindow(WIN_MESSAGE, FALSE); @@ -224,7 +225,7 @@ deadbook_pacify_undead(struct monst *mtmp) } } -/* special effects for The Book of the Dead; reading it while blind is +/* special effects for the Book of the Dead; reading it while blind is allowed so that needs to be taken into account too */ staticfn void deadbook(struct obj *book2) @@ -234,7 +235,7 @@ deadbook(struct obj *book2) You("turn the pages of the Book of the Dead..."); makeknown(SPE_BOOK_OF_THE_DEAD); - book2->dknown = 1; /* in case blind now and hasn't been seen yet */ + observe_object(book2); /* in case blind now and hasn't been seen yet */ /* KMH -- Need ->known to avoid "_a_ Book of the Dead" */ book2->known = 1; if (invocation_pos(u.ux, u.uy) && !On_stairs(u.ux, u.uy)) { @@ -713,11 +714,12 @@ rejectcasting(void) staticfn boolean getspell(int *spell_no) { - int nspells, idx; + int nspells, idx, retry_limit; char ilet, lets[BUFSZ], qbuf[QBUFSZ]; struct _cmd_queue cq, *cmdq; - if (spellid(0) == NO_SPELL) { + nspells = num_spells(); + if (!nspells) { You("don't know any spells right now."); return FALSE; } @@ -728,7 +730,6 @@ getspell(int *spell_no) cq = *cmdq; free(cmdq); if (cq.typ == CMDQ_KEY) { - nspells = num_spells(); idx = spell_let_to_idx(cq.key); if (idx < 0 || idx >= nspells) return FALSE; @@ -740,27 +741,33 @@ getspell(int *spell_no) } if (flags.menu_style == MENU_TRADITIONAL) { - /* we know there is at least 1 known spell */ - nspells = num_spells(); - + /* if we get here, we know there is at least 1 known spell */ if (nspells == 1) Strcpy(lets, "a"); else if (nspells < 27) Sprintf(lets, "a-%c", 'a' + nspells - 1); else if (nspells == 27) - Sprintf(lets, "a-zA"); + Strcpy(lets, "a-zA"); /* this assumes that there are at most 52 spells... */ else Sprintf(lets, "a-zA-%c", 'A' + nspells - 27); - for (;;) { - Snprintf(qbuf, sizeof(qbuf), "Cast which spell? [%s *?]", - lets); + Snprintf(qbuf, sizeof qbuf, "Cast which spell? [%s *?]", lets); + for (retry_limit = 0; ; ++retry_limit) { + if (retry_limit == 10) { + /* limit is mainly to prevent the fuzzer from getting stuck + since hangup should hit the 'quitchars' case; fuzzer + would too, but after an arbitrary number of attempts */ + pline("That's enough tries."); + return FALSE; + } ilet = yn_function(qbuf, (char *) 0, '\0', TRUE); if (ilet == '*' || ilet == '?') break; /* use menu mode */ - if (strchr(quitchars, ilet)) + if (strchr(quitchars, ilet)) { + pline1(Never_mind); return FALSE; + } idx = spell_let_to_idx(ilet); if (idx < 0 || idx >= nspells) { @@ -885,13 +892,16 @@ skill_based_spellbook_id(void) break; case P_UNSKILLED: default: - known_up_to_level = 1; + /* paupers need more skill than this to ID books, but most wizards + know the basics */ + known_up_to_level = u.uroleplay.pauper ? 0 : 1; break; } if (objects[booktype].oc_level <= known_up_to_level) - /* makeknown(booktype) but don't exercise Wisdom */ - discover_object(booktype, TRUE, FALSE); + /* makeknown(booktype) but don't exercise Wisdom or mark as + encountered */ + discover_object(booktype, TRUE, FALSE, FALSE); } } @@ -1539,13 +1549,23 @@ spelleffects(int spell_otyp, boolean atme, boolean force) case SPE_CURE_BLINDNESS: healup(0, 0, FALSE, TRUE); break; - case SPE_CURE_SICKNESS: - if (Sick) - You("are no longer ill."); - if (Slimed) - make_slimed(0L, "The slime disappears!"); + case SPE_CURE_SICKNESS: { + boolean was_sick = !!Sick, was_slimed = !!Slimed; + + /* cure conditions (which updates status) before feedback */ healup(0, 0, TRUE, FALSE); + /* + * Sick + !Slimed -- You are no longer ill. + * !Sick + !Slimed -- You are not ill. + * !Sick + Slimed -- The slime disappears. + * Sick + Slimed -- You are no longer ill. The slime disappears. + */ + if (was_sick || !was_slimed) + You("are %s ill.", was_sick ? "no longer" : "not"); + if (was_slimed) + make_slimed(0L, "The slime disappears!"); break; + } case SPE_CREATE_FAMILIAR: (void) make_familiar((struct obj *) 0, u.ux, u.uy, FALSE); break; @@ -2034,13 +2054,27 @@ dovspell(void) DISABLE_WARNING_FORMAT_NONLITERAL +/* lists spells for endgame dumplog purposes */ +void +show_spells(void) +{ + int unused = SPELLMENU_DUMP; + if (spellid(0) == NO_SPELL) { + pline("You didn't know any spells."); + pline("%s", ""); + } else { + pline("Spells:"); + nhUse(dospellmenu("", SPELLMENU_DUMP, &unused)); + } +} + /* shows menu of known spells, with options to sort them. return FALSE on cancel, TRUE otherwise. spell_no is set to the internal spl_book index, if any selected */ staticfn boolean dospellmenu( const char *prompt, - int splaction, /* SPELLMENU_CAST, SPELLMENU_VIEW, or + int splaction, /* SPELLMENU_CAST, SPELLMENU_VIEW, SPELLMENU_DUMP or * svs.spl_book[] index */ int *spell_no) { @@ -2060,12 +2094,16 @@ dospellmenu( * The correct spacing of the columns when not using * tab separation depends on the following: * (1) that the font is monospaced, and - * (2) that selection letters are pre-pended to the - * given string and are of the form "a - ". + * (2) that selection letters are prepended to the + * given string and are of the form "a - ". + * For SPELLMENU_DUMP, (2) is untrue, so four spaces + * need to be subtracted. */ if (!iflags.menu_tab_sep) { - Sprintf(buf, "%-20s Level %-12s Fail Retention", - " Name", "Category"); + Sprintf(buf, "%s%-20s Level %-12s Fail Retention", + splaction == SPELLMENU_DUMP ? "" : " ", + "Name", + "Category"); fmt = "%-20s %2d %-12s %3d%% %9s"; sep = ' '; } else { @@ -2135,7 +2173,7 @@ staticfn int percent_success(int spell) { /* Intrinsic and learned ability are combined to calculate - * the probability of player's success at cast a given spell. + * the probability of player's success at casting a given spell. */ int chance, splcaster, special, statused; int difficulty; @@ -2186,13 +2224,13 @@ percent_success(int spell) /* Calculate learned ability */ - /* Players basic likelihood of being able to cast any spell + /* The player's basic likelihood of being able to cast any spell * is based of their `magic' statistic. (Int or Wis) */ chance = 11 * statused / 2; /* - * High level spells are harder. Easier for higher level casters. + * High-level spells are harder. Easier for higher-level casters. * The difficulty is based on the hero's level and their skill level * in that spell type. */ diff --git a/src/steal.c b/src/steal.c index f90d66a55..fd53ff1b7 100644 --- a/src/steal.c +++ b/src/steal.c @@ -601,7 +601,7 @@ steal(struct monst *mtmp, char *objnambuf) && mtmp->data->mlet == S_NYMPH) ++named; urgent_pline("%s stole %s.", named ? "She" : Monnambuf, doname(otmp)); - (void) encumber_msg(); + encumber_msg(); could_petrify = (otmp->otyp == CORPSE && touch_petrifies(&mons[otmp->corpsenm])); otmp->how_lost = LOST_STOLEN; @@ -643,7 +643,7 @@ mpickobj(struct monst *mtmp, struct obj *otmp) } /* don't want hidden light source inside the monster; assumes that engulfers won't have external inventories; whirly monsters cause - the light to be extinguished rather than letting it shine thru */ + the light to be extinguished rather than letting it shine through */ if (obj_sheds_light(otmp) && attacktype(mtmp->data, AT_ENGL)) { /* this is probably a burning object that you dropped or threw */ if (engulfing_u(mtmp) && !Blind) @@ -762,7 +762,7 @@ stealamulet(struct monst *mtmp) pline("%s steals %s!", Some_Monnam(mtmp), buf); if (can_teleport(mtmp->data) && !tele_restrict(mtmp)) (void) rloc(mtmp, RLOC_MSG); - (void) encumber_msg(); + encumber_msg(); } } @@ -799,7 +799,7 @@ maybe_absorb_item( otense(obj, "are"), hand_s); } freeinv(obj); - (void) encumber_msg(); + encumber_msg(); } else { /* not carried; presumably thrown or kicked */ if (canspotmon(mon)) diff --git a/src/steed.c b/src/steed.c index 48d537b99..835a8562f 100644 --- a/src/steed.c +++ b/src/steed.c @@ -213,8 +213,8 @@ mount_steed( pline("Maybe you should find a designated driver."); return (FALSE); } - /* While riding Wounded_legs refers to the steed's, - * not the hero's legs. + /* While riding, Wounded_legs refers to the steed's + * legs, not the hero's legs. * That opens up a potential abuse where the player * can mount a steed, then dismount immediately to * heal leg damage, because leg damage is always @@ -471,7 +471,7 @@ landing_spot( (void) memset((genericptr_t) try, 0, sizeof try); n = 0; - j = xytod(u.dx, u.dy); + j = xytodir(u.dx, u.dy); if (reason == DISMOUNT_KNOCKED && j != DIR_ERR) { /* we'll check preferred location first; if viable it'll be picked */ best_j = j; @@ -479,10 +479,10 @@ landing_spot( /* the two next best locations are checked second and third */ i = rn2(2); clockwise_j = DIR_RIGHT(j); /* (j + 1) % 8 */ - dtoxy(&cc, clockwise_j); + dirtocoord(&cc, clockwise_j); try[1 + i].x = cc.x, try[1 + i].y = cc.y; /* [1] or [2] */ counterclk_j = DIR_LEFT(j); /* (j + 8 - 1) % 8 */ - dtoxy(&cc, counterclk_j); + dirtocoord(&cc, counterclk_j); try[2 - i].x = cc.x, try[2 - i].y = cc.y; /* [2] or [1] */ n = 3; debugpline3("knock from saddle: best %s, next %s or %s", @@ -500,7 +500,7 @@ landing_spot( so odd j values are diagonal directions here */ if (reason == DISMOUNT_POLY && NODIAG(u.umonnum) && (j % 1) != 0) continue; - dtoxy(&cc, j); + dirtocoord(&cc, j); try[n++] = cc; } @@ -811,7 +811,7 @@ dismount_steed( (void) float_down(0L, W_SADDLE); gi.in_steed_dismounting = FALSE; disp.botl = TRUE; - (void) encumber_msg(); + encumber_msg(); gv.vision_full_recalc = 1; } else disp.botl = TRUE; diff --git a/src/teleport.c b/src/teleport.c index 3c3d6df3e..b235cb238 100644 --- a/src/teleport.c +++ b/src/teleport.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 teleport.c $NHDT-Date: 1736129950 2025/01/05 18:19:10 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.235 $ */ +/* NetHack 3.7 teleport.c $NHDT-Date: 1769342601 2026/01/25 04:03:21 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.239 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -34,8 +34,13 @@ noteleport_level(struct monst *mon) if (get_iter_mons(m_blocks_teleporting)) return TRUE; - /* natural no-teleport level */ - if (svl.level.flags.noteleport) + /* natural no-teleport level; covetous monsters can bypass these */ + if (svl.level.flags.noteleport && !is_covetous(mon->data)) + return TRUE; + + /* wand of stasis prevents teleportation while the effect is active + (even for covetous monsters) */ + if (svl.level.flags.stasis_until >= svm.moves) return TRUE; return FALSE; @@ -462,7 +467,7 @@ teleds(coordxy nux, coordxy nuy, int teleds_flags) * otherwise they are teleporting, so unplacebc(). * If they don't have to move the ball, then always "drag" whether or * not allow_drag is true, because we are calling that function, not - * to drag, but to move the chain. *However* there are some dumb + * to drag, but to move the chain. *However*, there are some dumb * special cases: * 0 0 * _X move east -----> X_ @@ -811,6 +816,12 @@ tele_to_rnd_pet(void) struct monst *mtmp, *pet = (struct monst *) 0; int cnt = 0; + if (noteleport_level(&gy.youmonst)) { + impossible("%s", "attempt to teleport hero to be near a pet" + " on no-teleport level"); + return; + } + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) if (!DEADMONSTER(mtmp) && mtmp->mtame && !mon_offmap(mtmp)) { cnt++; @@ -1353,7 +1364,7 @@ level_tele(void) d_level lsav; /* set specific death location; this also suppresses bones */ - lsav = u.uz; /* save current level, see below */ + lsav = u.uz; /* save current level; see below */ u.uz.dnum = 0; /* main dungeon */ u.uz.dlevel = (newlev <= -10) ? -10 : 0; /* heaven or surface */ done(DIED); @@ -1488,7 +1499,7 @@ tele_trap(struct trap *trap) return; in_tele_trap = TRUE; - if (In_endgame(&u.uz) || Antimagic) { + if (In_endgame(&u.uz) || Antimagic || noteleport_level(&gy.youmonst)) { if (Antimagic) shieldeff(u.ux, u.uy); You_feel("a wrenching sensation."); @@ -1952,8 +1963,11 @@ mtele_trap(struct monst *mtmp, struct trap *trap, int in_sight) { char *monname; - if (tele_restrict(mtmp)) + /* don't print feedback here: a monster stepping on a trap and not + teleporting from it isn't visible */ + if (noteleport_level(mtmp)) return; + if (teleport_pet(mtmp, FALSE)) { /* save name with pre-movement visibility */ monname = Monnam(mtmp); @@ -2251,7 +2265,12 @@ u_teleport_mon( { coord cc; - if (mtmp->ispriest && *in_rooms(mtmp->mx, mtmp->my, TEMPLE)) { + if (svl.level.flags.stasis_until >= svm.moves) { + if (give_feedback) + pline("A mysterious force prevents you teleporting %s!", + mon_nam(mtmp)); + return FALSE; + } else if (mtmp->ispriest && *in_rooms(mtmp->mx, mtmp->my, TEMPLE)) { if (give_feedback) pline("%s resists your magic!", Monnam(mtmp)); return FALSE; diff --git a/src/timeout.c b/src/timeout.c index 1b85d5398..af974c84e 100644 --- a/src/timeout.c +++ b/src/timeout.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 timeout.c $NHDT-Date: 1756531249 2025/08/29 21:20:49 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.205 $ */ +/* NetHack 3.7 timeout.c $NHDT-Date: 1776080125 2026/04/13 03:35:25 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.207 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2018. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1078,7 +1078,7 @@ hatch_egg(anything *arg, long timeout) * mind are: * + Create the hatched monster then place it on the migrating * mons list. This is tough because all makemon() is made - * to place the monster as well. Makemon() also doesn't lend + * to place the monster as well. Makemon() also doesn't lend * itself well to splitting off a "not yet placed" subroutine. * + Mark the egg as hatched, then place the monster when we * place the migrating objects. @@ -1232,7 +1232,7 @@ slip_or_trip(void) if (otmp && on_foot) { /* trip over something in particular */ /* If there is only one item, it will have just been named - during the move, so refer to by via pronoun; otherwise, + during the move, so refer to it by pronoun; otherwise, if the top item has been or can be seen, refer to it by name; if not, look for rocks to trip over; trip over anonymous "something" if there aren't any rocks. @@ -1532,7 +1532,7 @@ burn_object(anything *arg, long timeout) default: /* * Someone added fuel to the lamp while it was - * lit. Just fall through and let begin burn + * lit. Just fall through and let begin_burn() * handle the new age. */ break; @@ -1659,7 +1659,7 @@ burn_object(anything *arg, long timeout) default: /* * Someone added fuel (candles) to the menorah while - * it was lit. Just fall through and let begin burn + * it was lit. Just fall through and let begin_burn() * handle the new age. */ break; @@ -1875,7 +1875,7 @@ do_storms(void) } if (levl[u.ux][u.uy].typ == CLOUD) { - /* Inside a cloud during a thunder storm is deafening. */ + /* Inside a cloud during a thunderstorm is deafening. */ /* Even if already deaf, we sense the thunder's vibrations. */ Soundeffect(se_kaboom_boom_boom, 80); pline("Kaboom!!! Boom!! Boom!!"); @@ -1925,7 +1925,7 @@ do_storms(void) * Save all timers of range 'range'. Range is either global * or local. Global timers follow game play, local timers * are saved with a level. Object and monster timers are - * saved using their respective id's instead of pointers. + * saved using their respective ids instead of pointers. * * void restore_timers(NHFILE *, int range, long adjust) * Restore timers of range 'range'. If from a ghost pile, @@ -2112,6 +2112,14 @@ wiz_timeout_queue(void) if (any_visible_region()) { visible_region_summary(win); } + if (svl.level.flags.stasis_until >= svm.moves) { + putstr(win, 0, ""); + Sprintf(buf, "Level is no-teleport for %ld %s.", + svl.level.flags.stasis_until - svm.moves + 1L, + (svl.level.flags.stasis_until - svm.moves > 0L) + ? "turns" : "more turn"); + putstr(win, 0, buf); + } display_nhwindow(win, FALSE); destroy_nhwindow(win); @@ -2217,7 +2225,7 @@ run_timers(void) /* * Always use the first element. Elements may be added or deleted at - * any time. The list is ordered, we are done when the first element + * any time. The list is ordered; we are done when the first element * is in the future. */ while (gt.timer_base && gt.timer_base->timeout <= svm.moves) { @@ -2653,7 +2661,7 @@ maybe_write_timer(NHFILE *nhfp, int range, boolean write_it) * + timeouts that follow obj & monst that are migrating * * Level range: - * + timeouts that are level specific (e.g. storms) + * + timeouts that are level-specific (e.g. storms) * + timeouts that stay with the level (obj & monst) */ void diff --git a/src/topten.c b/src/topten.c index cb968a726..f7770791a 100644 --- a/src/topten.c +++ b/src/topten.c @@ -16,7 +16,7 @@ /* * Updating in place can leave junk at the end of the file in some - * circumstances (if it shrinks and the O.S. doesn't have a straightforward + * circumstances (if it shrinks and the OS doesn't have a straightforward * way to truncate it). The trailing junk is harmless and the code * which reads the scores will ignore it. */ @@ -385,6 +385,7 @@ writexlentry(FILE *rfile, struct toptenentry *tt, int how) Fprintf(rfile, "%cwish_cnt=%ld", XLOG_SEP, u.uconduct.wishes); Fprintf(rfile, "%carti_wish_cnt=%ld", XLOG_SEP, u.uconduct.wisharti); Fprintf(rfile, "%cbones=%ld", XLOG_SEP, u.uroleplay.numbones); + Fprintf(rfile, "%crerolls=%ld", XLOG_SEP, u.uroleplay.numrerolls); Fprintf(rfile, "\n"); #undef XLOG_SEP } @@ -400,6 +401,8 @@ encodexlogflags(void) e |= 1L << 1; if (!u.uroleplay.numbones) e |= 1L << 2; + if (u.uroleplay.reroll) + e |= 1L << 3; return e; } @@ -601,6 +604,7 @@ encode_extended_conducts(char *buf) add_achieveX(buf, "pauper", u.uroleplay.pauper); add_achieveX(buf, "bonesless", !flags.bones); add_achieveX(buf, "petless", !u.uconduct.pets); + add_achieveX(buf, "unrerolled", !u.uroleplay.reroll); return buf; } diff --git a/src/track.c b/src/track.c index f5b915f22..178e5a6f9 100644 --- a/src/track.c +++ b/src/track.c @@ -23,6 +23,10 @@ initrack(void) void settrack(void) { + if ((uleft && uleft->otyp == RIN_STEALTH) + || (uright && uright->otyp == RIN_STEALTH)) + return; + if (utcnt < UTSZ) utcnt++; if (utpnt == UTSZ) diff --git a/src/trap.c b/src/trap.c index 21adb2314..8444e9b30 100644 --- a/src/trap.c +++ b/src/trap.c @@ -16,6 +16,7 @@ staticfn boolean mu_maybe_destroy_web(struct monst *, boolean, struct trap *); staticfn struct obj *t_missile(int, struct trap *); staticfn boolean floor_trigger(int); staticfn boolean check_in_air(struct monst *, unsigned); +staticfn boolean wearing_iron_shoes(struct monst *); staticfn int trapeffect_arrow_trap(struct monst *, struct trap *, unsigned); staticfn int trapeffect_dart_trap(struct monst *, struct trap *, unsigned); staticfn int trapeffect_rocktrap(struct monst *, struct trap *, unsigned); @@ -548,7 +549,7 @@ maketrap(coordxy x, coordxy y, int typ) /* * some cases which can happen when digging - * down while phazing thru solid areas + * down while phasing thru solid areas */ } else if (lev->typ == STONE || lev->typ == SCORR) { (void) set_levltyp(x, y, CORR); @@ -1093,6 +1094,14 @@ check_in_air(struct monst *mtmp, unsigned trflags) || ((is_you ? Flying : is_flyer(mtmp->data)) && !plunged)); } +/* return TRUE if mtmp is wearing iron shoes */ +staticfn boolean +wearing_iron_shoes(struct monst *mtmp) +{ + struct obj *armf = which_armor(mtmp, W_ARMF); + return armf && armf->otyp == IRON_SHOES; +} + /* is trap ttmp harmless to monster mtmp? */ boolean m_harmless_trap(struct monst *mtmp, struct trap *ttmp) @@ -1206,7 +1215,7 @@ trapeffect_arrow_trap( } else { place_object(otmp, u.ux, u.uy); if (!Blind) - otmp->dknown = 1; + observe_object(otmp); stackobj(otmp); newsym(u.ux, u.uy); } @@ -1276,7 +1285,7 @@ trapeffect_dart_trap( } else { place_object(otmp, u.ux, u.uy); if (!Blind) - otmp->dknown = 1; + observe_object(otmp); stackobj(otmp); newsym(u.ux, u.uy); } @@ -1352,7 +1361,7 @@ trapeffect_rocktrap( harmless = TRUE; } if (!Blind) - otmp->dknown = 1; + observe_object(otmp); stackobj(otmp); newsym(u.ux, u.uy); /* map the rock */ @@ -1501,10 +1510,14 @@ trapeffect_bear_trap( } else { pline("%s bear trap closes on your %s!", A_Your[trap->madeby_u], body_part(FOOT)); - set_wounded_legs(rn2(2) ? RIGHT_SIDE : LEFT_SIDE, rn1(10, 10)); if (u.umonnum == PM_OWLBEAR || u.umonnum == PM_BUGBEAR) You("howl in anger!"); - losehp(Maybe_Half_Phys(dmg), "bear trap", KILLED_BY_AN); + if (wearing_iron_shoes(mtmp)) + pline("%s protects your leg.", Yname2(uarmf)); + else { + set_wounded_legs(rn2(2) ? RIGHT_SIDE : LEFT_SIDE, rn1(10, 10)); + losehp(Maybe_Half_Phys(dmg), "bear trap", KILLED_BY_AN); + } } exercise(A_DEX, FALSE); } else { @@ -1535,7 +1548,7 @@ trapeffect_bear_trap( seetrap(trap); } } - if (mtmp->mtrapped) + if (mtmp->mtrapped && !wearing_iron_shoes(mtmp)) trapkilled = thitm(0, mtmp, (struct obj *) 0, d(2, 4), FALSE); return trapkilled ? Trap_Killed_Mon : mtmp->mtrapped @@ -1813,6 +1826,9 @@ trapeffect_pit( unsigned int trflags) { int ttype = trap->ttyp; + /* relevant_spikes is initially always true for spiked pits, but + set to false if the spikes are found to not be relevant */ + boolean relevant_spikes = ttype == SPIKED_PIT; if (mtmp == &gy.youmonst) { boolean plunged = (trflags & TOOKPLUNGE) != 0; @@ -1880,7 +1896,10 @@ trapeffect_pit( } else if (u.umonnum == PM_PIT_VIPER || u.umonnum == PM_PIT_FIEND) { pline("How pitiful. Isn't that the pits?"); } - if (ttype == SPIKED_PIT) { + if (relevant_spikes && wearing_iron_shoes(mtmp)) { + pline("%s protects you from the sharp iron spikes.", Yname2(uarmf)); + relevant_spikes = FALSE; + } else if (relevant_spikes) { const char *predicament = "on a set of sharp iron spikes"; if (u.usteed) { @@ -1898,7 +1917,7 @@ trapeffect_pit( */ set_utrap((unsigned) rn1(6, 2), TT_PIT); if (!steedintrap(trap, (struct obj *) 0)) { - if (ttype == SPIKED_PIT) { + if (relevant_spikes) { int oldumort = u.umortality; losehp(Maybe_Half_Phys(rnd(conj_pit ? 4 : adj_pit ? 6 : 10)), @@ -1977,8 +1996,9 @@ trapeffect_pit( seetrap(trap); } mselftouch(mtmp, "Falling, ", FALSE); + if (wearing_iron_shoes(mtmp)) relevant_spikes = FALSE; if (DEADMONSTER(mtmp) || thitm(0, mtmp, (struct obj *) 0, - rnd((ttype == PIT) ? 6 : 10), FALSE)) + rnd(relevant_spikes ? 10 : 6), FALSE)) trapkilled = TRUE; return trapkilled ? Trap_Killed_Mon : mtmp->mtrapped @@ -2125,7 +2145,7 @@ trapeffect_web( /* time will be adjusted below */ set_utrap(1, TT_WEB); - /* Time stuck in the web depends on your/steed strength. */ + /* Time stuck in the web depends on your/steed's strength. */ { int tim, str = ACURR(A_STR); @@ -2303,6 +2323,24 @@ trapeffect_anti_magic( struct trap *trap, /* trap->ttyp == ANTI_MAGIC */ unsigned int trflags UNUSED) { + if (wearing_iron_shoes(mtmp)) { + struct obj *shoes = which_armor(mtmp, W_ARMF); + /* iron shoes protect against antimagic traps only if + positively enchanted; the trap drains the enchantment + rather than the wearer */ + if (shoes->spe > 0) { + /* no message if a monster does this, it isn't visible enough */ + if (mtmp == &gy.youmonst) { + seetrap(trap); + pline("A lethargic aura surrounds %s.", yname(shoes)); + costly_alteration(shoes, COST_DECHNT); + } + shoes->spe -= 1; + update_inventory(); + return Trap_Effect_Finished; + } + } + if (mtmp == &gy.youmonst) { int drain, halfd; boolean exclaim_it = FALSE; @@ -2415,6 +2453,10 @@ trapeffect_poly_trap( struct trap *trap, unsigned int trflags) { + static int possible_boots[] = { + ELVEN_BOOTS, KICKING_BOOTS, FUMBLE_BOOTS, LEVITATION_BOOTS, + JUMPING_BOOTS, SPEED_BOOTS, WATER_WALKING_BOOTS }; + if (mtmp == &gy.youmonst) { boolean viasitting = (trflags & VIASITTING) != 0; int steed_article = ARTICLE_THE; @@ -2435,7 +2477,14 @@ trapeffect_poly_trap( else Sprintf(verbbuf, "%s onto", u_locomotion("step")); You("%s a polymorph trap!", verbbuf); - if (Antimagic || Unchanging) { + if (wearing_iron_shoes(mtmp)) { + deltrap(trap); + pline("%s warps strangely.", Yname2(uarmf)); + poly_obj(uarmf, ROLL_FROM(possible_boots)); + update_inventory(); + if (uarmf) + prinv(NULL, uarmf, 0); + } else if (Antimagic || Unchanging) { shieldeff(u.ux, u.uy); You_feel("momentarily different."); /* Trap did nothing; don't remove it --KAA */ @@ -2449,7 +2498,22 @@ trapeffect_poly_trap( } else { boolean in_sight = canseemon(mtmp) || (mtmp == u.usteed); - if (resists_magm(mtmp)) { + if (wearing_iron_shoes(mtmp)) { + /* remove and readd the shoes to forcibly unwear them */ + struct obj *shoes = which_armor(mtmp, W_ARMF); + extract_from_minvent(mtmp, shoes, TRUE, TRUE); + if (mpickobj(mtmp, shoes)) { + impossible("re-equipping iron shoes destroyed them?"); + return Trap_Effect_Finished; + } + shoes = poly_obj(shoes, ROLL_FROM(possible_boots)); + /* now equip them again */ + if (shoes) { + mtmp->misc_worn_check |= W_ARMF; + shoes->owornmask = W_ARMF; + update_mon_extrinsics(mtmp, shoes, TRUE, TRUE); + } + } else if (resists_magm(mtmp)) { shieldeff_mon(mtmp); } else if (!resist(mtmp, WAND_CLASS, 0, NOTELL)) { (void) newcham(mtmp, (struct permonst *) 0, NC_SHOW_MSG); @@ -2466,6 +2530,13 @@ trapeffect_landmine( struct trap *trap, unsigned int trflags) { + int damage = rnd(16); + /* iron shoes protect against much of the damage from the + explosion, but you still take some damage (and wound legs) + because they can't fully block the blast */ + if (wearing_iron_shoes(mtmp)) + damage = (damage + 3) / 4; + if (mtmp == &gy.youmonst) { boolean already_seen = trap->tseen; boolean forcetrap = ((trflags & FORCETRAP) != 0 @@ -2491,8 +2562,8 @@ trapeffect_landmine( already_seen ? " land mine" : "it"); } else { /* prevent landmine from killing steed, throwing you to - * the ground, and you being affected again by the same - * mine because it hasn't been deleted yet + * the ground, and then that same landmine affecting you + * again because it hasn't been deleted yet */ static boolean recursive_mine = FALSE; @@ -2515,7 +2586,7 @@ trapeffect_landmine( blow_up_landmine() will remove pit afterwards if inappropriate */ trap->ttyp = PIT; trap->madeby_u = FALSE; - losehp(Maybe_Half_Phys(rnd(16)), "land mine", KILLED_BY_AN); + losehp(Maybe_Half_Phys(damage), "land mine", KILLED_BY_AN); blow_up_landmine(trap); if (steed_mid && saddle && !u.usteed) (void) keep_saddle_with_steedcorpse(steed_mid, fobj, saddle); @@ -2564,7 +2635,7 @@ trapeffect_landmine( /* explosion might have destroyed a drawbridge; don't dish out more damage if monster is already dead */ if (DEADMONSTER(mtmp) - || thitm(0, mtmp, (struct obj *) 0, rnd(16), FALSE)) { + || thitm(0, mtmp, (struct obj *) 0, damage, FALSE)) { trapkilled = TRUE; } else { /* monsters recursively fall into new pit */ @@ -3228,7 +3299,8 @@ launch_obj( newsym(x1, y1); /* in case you're using a pick-axe to chop the boulder that's being launched (perhaps a monster triggered it), destroy context so that - next dig attempt never thinks you're resuming previous effort */ + the next dig attempt never thinks that you're resuming + the previous effort */ if ((otyp == BOULDER || otyp == STATUE) && singleobj->ox == svc.context.digging.pos.x && singleobj->oy == svc.context.digging.pos.y) @@ -3907,7 +3979,7 @@ float_up(void) float_vs_flight(); /* set BFlying, also BLevitation if still trapped */ /* levitation gives maximum carrying capacity, so encumbrance state might be reduced */ - (void) encumber_msg(); + encumber_msg(); return; } @@ -3954,7 +4026,7 @@ float_down( : (u.utraptype == TT_BURIEDBALL) ? "chain" : (u.utraptype == TT_LAVA) ? "lava" : "ground"); /* TT_INFLOOR */ - (void) encumber_msg(); /* carrying capacity might have changed */ + encumber_msg(); /* carrying capacity might have changed */ return 0; } disp.botl = TRUE; @@ -3965,14 +4037,14 @@ float_down( * unless hero is stuck in floor */ if (Flying) { You("have stopped levitating and are now flying."); - (void) encumber_msg(); /* carrying capacity might have changed */ + encumber_msg(); /* carrying capacity might have changed */ return 1; } } if (u.uswallow) { You("float down, but you are still %s.", digests(u.ustuck->data) ? "swallowed" : "engulfed"); - (void) encumber_msg(); + encumber_msg(); return 1; } @@ -4056,7 +4128,7 @@ float_down( /* levitation gives maximum carrying capacity, so having it end potentially triggers greater encumbrance; do this after 'come down' messages, before trap activation or autopickup */ - (void) encumber_msg(); + encumber_msg(); /* can't rely on u.uz0 for detecting trap door-induced level change; it gets changed to reflect the new level before we can check it */ @@ -4663,8 +4735,8 @@ water_damage( water_damage_chain(obj->cobj, FALSE); return ER_DAMAGED; /* contents were damaged */ } else if (Waterproof_container(obj)) { - if (in_invent) { - pline_The("%s slides right off your %s.", hliquid("water"), ostr); + if (in_invent && !Blind && !Underwater) { + pline_The("%s cannot get into your %s.", hliquid("water"), ostr); gm.mentioned_water = !Hallucination; makeknown(obj->otyp); /* if an oilskin sack, discover it; doesn't * matter for chest, large box, ice box */ @@ -5738,7 +5810,7 @@ untrap_box( else pline("There's a trap on %s.", the(xname(box))); box->tknown = 1; - box->dknown = 1; + observe_object(box); if (!confused) exercise(A_WIS, TRUE); @@ -6400,7 +6472,7 @@ chest_trap( bot(); /* to get immediate botl re-display */ } - otmp->tknown = 1; /* hero knows chest is no longer trapped */ + obj->tknown = 1; /* hero knows chest is no longer trapped */ return FALSE; } @@ -6472,7 +6544,7 @@ conjoined_pits( return FALSE; dx = sgn(trap2->tx - trap1->tx); dy = sgn(trap2->ty - trap1->ty); - diridx = xytod(dx, dy); + diridx = xytodir(dx, dy); if (diridx != DIR_ERR) { adjidx = DIR_180(diridx); if ((trap1->conjoined & (1 << diridx)) @@ -6513,7 +6585,7 @@ adj_nonconjoined_pit(struct trap *adjtrap) if (trap_with_u && adjtrap && u.utrap && u.utraptype == TT_PIT && is_pit(trap_with_u->ttyp) && is_pit(adjtrap->ttyp)) { - if (xytod(u.dx, u.dy) != DIR_ERR) + if (xytodir(u.dx, u.dy) != DIR_ERR) return TRUE; } return FALSE; diff --git a/src/u_init.c b/src/u_init.c index e4d60dd62..dcf3e4077 100644 --- a/src/u_init.c +++ b/src/u_init.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 u_init.c $NHDT-Date: 1737620595 2025/01/23 00:23:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.113 $ */ +/* NetHack 3.7 u_init.c $NHDT-Date: 1769398807 2026/01/25 19:40:07 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.121 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2017. */ /* NetHack may be freely redistributed. See license for details. */ @@ -9,22 +9,25 @@ struct trobj { short trotyp; schar trspe; char trclass; - Bitfield(trquan, 6); - Bitfield(trbless, 2); + char trquan_min; + char trquan_max; + char trbless; }; +staticfn long trquan(const struct trobj *); staticfn struct obj *ini_inv_mkobj_filter(int, boolean); -staticfn short ini_inv_obj_substitution(struct trobj *, - struct obj *) NONNULLPTRS; -staticfn void ini_inv_adjust_obj(struct trobj *, - struct obj *) NONNULLPTRS; +staticfn short ini_inv_obj_substitution(const struct trobj *, + struct obj *) NONNULLPTRS; +staticfn boolean ini_inv_adjust_obj(const struct trobj *, + struct obj *) NONNULLPTRS; staticfn void ini_inv_use_obj(struct obj *) NONNULLARG1; -staticfn void ini_inv(struct trobj *) NONNULLARG1; +staticfn void ini_inv(const struct trobj *) NONNULLARG1; staticfn void knows_object(int, boolean); staticfn void knows_class(char); staticfn void u_init_role(void); staticfn void u_init_race(void); staticfn void pauper_reinit(void); +staticfn const struct def_skill *skills_for_role(void); staticfn void u_init_carry_attr_boost(void); staticfn boolean restricted_spell_discipline(int); @@ -36,177 +39,188 @@ staticfn boolean restricted_spell_discipline(int); * Initial inventory for the various roles. */ -static struct trobj Archeologist[] = { +static const struct trobj Archeologist[] = { /* if adventure has a name... idea from tan@uvm-gen */ - { BULLWHIP, 2, WEAPON_CLASS, 1, UNDEF_BLESS }, - { LEATHER_JACKET, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, - { FEDORA, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, - { FOOD_RATION, 0, FOOD_CLASS, 3, 0 }, - { PICK_AXE, UNDEF_SPE, TOOL_CLASS, 1, UNDEF_BLESS }, - { TINNING_KIT, UNDEF_SPE, TOOL_CLASS, 1, UNDEF_BLESS }, - { TOUCHSTONE, 0, GEM_CLASS, 1, 0 }, - { SACK, 0, TOOL_CLASS, 1, 0 }, - { 0, 0, 0, 0, 0 } + { BULLWHIP, 2, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { LEATHER_JACKET, 0, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { FEDORA, 0, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { FOOD_RATION, 0, FOOD_CLASS, 3, 3, 0 }, + { PICK_AXE, UNDEF_SPE, TOOL_CLASS, 1, 1, UNDEF_BLESS }, + { TINNING_KIT, UNDEF_SPE, TOOL_CLASS, 1, 1, UNDEF_BLESS }, + { TOUCHSTONE, 0, GEM_CLASS, 1, 1, 0 }, + { SACK, 0, TOOL_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; -static struct trobj Barbarian[] = { -#define B_MAJOR 0 /* two-handed sword or battle-axe */ -#define B_MINOR 1 /* matched with axe or short sword */ - { TWO_HANDED_SWORD, 0, WEAPON_CLASS, 1, UNDEF_BLESS }, - { AXE, 0, WEAPON_CLASS, 1, UNDEF_BLESS }, - { RING_MAIL, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, - { FOOD_RATION, 0, FOOD_CLASS, 1, 0 }, - { 0, 0, 0, 0, 0 } +static const struct trobj Barbarian_0[] = { + { TWO_HANDED_SWORD, 0, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { AXE, 0, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { RING_MAIL, 0, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { FOOD_RATION, 0, FOOD_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; -static struct trobj Cave_man[] = { -#define C_AMMO 2 - { CLUB, 1, WEAPON_CLASS, 1, UNDEF_BLESS }, - { SLING, 2, WEAPON_CLASS, 1, UNDEF_BLESS }, - { FLINT, 0, GEM_CLASS, 15, UNDEF_BLESS }, /* quan is variable */ - { ROCK, 0, GEM_CLASS, 3, 0 }, /* yields 18..33 */ - { LEATHER_ARMOR, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, - { 0, 0, 0, 0, 0 } +static const struct trobj Barbarian_1[] = { + { BATTLE_AXE, 0, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { SHORT_SWORD, 0, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { RING_MAIL, 0, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { FOOD_RATION, 0, FOOD_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; -static struct trobj Healer[] = { - { SCALPEL, 0, WEAPON_CLASS, 1, UNDEF_BLESS }, - { LEATHER_GLOVES, 1, ARMOR_CLASS, 1, UNDEF_BLESS }, - { STETHOSCOPE, 0, TOOL_CLASS, 1, 0 }, - { POT_HEALING, 0, POTION_CLASS, 4, UNDEF_BLESS }, - { POT_EXTRA_HEALING, 0, POTION_CLASS, 4, UNDEF_BLESS }, - { WAN_SLEEP, UNDEF_SPE, WAND_CLASS, 1, UNDEF_BLESS }, +static const struct trobj Cave_man[] = { + { CLUB, 1, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { SLING, 2, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { FLINT, 0, GEM_CLASS, 10, 20, UNDEF_BLESS }, + { ROCK, 0, GEM_CLASS, 3, 3, 0 }, /* yields 18..33 */ + { LEATHER_ARMOR, 0, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { 0, 0, 0, 0, 0, 0 } +}; +static const struct trobj Healer[] = { + { SCALPEL, 0, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { LEATHER_GLOVES, 1, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { STETHOSCOPE, 0, TOOL_CLASS, 1, 1, 0 }, + { POT_HEALING, 0, POTION_CLASS, 4, 4, UNDEF_BLESS }, + { POT_EXTRA_HEALING, 0, POTION_CLASS, 4, 4, UNDEF_BLESS }, + { WAN_SLEEP, UNDEF_SPE, WAND_CLASS, 1, 1, UNDEF_BLESS }, /* always blessed, so it's guaranteed readable */ - { SPE_HEALING, 0, SPBOOK_CLASS, 1, 1 }, - { SPE_EXTRA_HEALING, 0, SPBOOK_CLASS, 1, 1 }, - { SPE_STONE_TO_FLESH, 0, SPBOOK_CLASS, 1, 1 }, - { APPLE, 0, FOOD_CLASS, 5, 0 }, - { 0, 0, 0, 0, 0 } + { SPE_HEALING, 0, SPBOOK_CLASS, 1, 1, 1 }, + { SPE_EXTRA_HEALING, 0, SPBOOK_CLASS, 1, 1, 1 }, + { SPE_STONE_TO_FLESH, 0, SPBOOK_CLASS, 1, 1, 1 }, + { APPLE, 0, FOOD_CLASS, 5, 5, 0 }, + { 0, 0, 0, 0, 0, 0 } }; -static struct trobj Knight[] = { - { LONG_SWORD, 1, WEAPON_CLASS, 1, UNDEF_BLESS }, - { LANCE, 1, WEAPON_CLASS, 1, UNDEF_BLESS }, - { RING_MAIL, 1, ARMOR_CLASS, 1, UNDEF_BLESS }, - { HELMET, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, - { SMALL_SHIELD, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, - { LEATHER_GLOVES, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, - { APPLE, 0, FOOD_CLASS, 10, 0 }, - { CARROT, 0, FOOD_CLASS, 10, 0 }, - { 0, 0, 0, 0, 0 } +static const struct trobj Knight[] = { + { LONG_SWORD, 1, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { LANCE, 1, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { RING_MAIL, 1, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { HELMET, 0, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { SMALL_SHIELD, 0, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { LEATHER_GLOVES, 0, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { APPLE, 0, FOOD_CLASS, 10, 10, 0 }, + { CARROT, 0, FOOD_CLASS, 10, 10, 0 }, + { 0, 0, 0, 0, 0, 0 } }; -static struct trobj Monk[] = { -#define M_BOOK 2 - { LEATHER_GLOVES, 2, ARMOR_CLASS, 1, UNDEF_BLESS }, - { ROBE, 1, ARMOR_CLASS, 1, UNDEF_BLESS }, - { UNDEF_TYP, UNDEF_SPE, SPBOOK_CLASS, 1, 1 }, - { UNDEF_TYP, UNDEF_SPE, SCROLL_CLASS, 1, UNDEF_BLESS }, - { POT_HEALING, 0, POTION_CLASS, 3, UNDEF_BLESS }, - { FOOD_RATION, 0, FOOD_CLASS, 3, 0 }, - { APPLE, 0, FOOD_CLASS, 5, UNDEF_BLESS }, - { ORANGE, 0, FOOD_CLASS, 5, UNDEF_BLESS }, +static const struct trobj Monk[] = { + { LEATHER_GLOVES, 2, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { ROBE, 1, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { UNDEF_TYP, UNDEF_SPE, SCROLL_CLASS, 1, 1, UNDEF_BLESS }, + { POT_HEALING, 0, POTION_CLASS, 3, 3, UNDEF_BLESS }, + { FOOD_RATION, 0, FOOD_CLASS, 3, 3, 0 }, + { APPLE, 0, FOOD_CLASS, 5, 5, UNDEF_BLESS }, + { ORANGE, 0, FOOD_CLASS, 5, 5, UNDEF_BLESS }, /* Yes, we know fortune cookies aren't really from China. They were - * invented by George Jung in Los Angeles, California, USA in 1916. - */ - { FORTUNE_COOKIE, 0, FOOD_CLASS, 3, UNDEF_BLESS }, - { 0, 0, 0, 0, 0 } + invented by George Jung in Los Angeles, California, USA in 1916. */ + { FORTUNE_COOKIE, 0, FOOD_CLASS, 3, 3, UNDEF_BLESS }, + { 0, 0, 0, 0, 0, 0 } }; -static struct trobj Priest[] = { - { MACE, 1, WEAPON_CLASS, 1, 1 }, - { ROBE, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, - { SMALL_SHIELD, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, - { POT_WATER, 0, POTION_CLASS, 4, 1 }, /* holy water */ - { CLOVE_OF_GARLIC, 0, FOOD_CLASS, 1, 0 }, - { SPRIG_OF_WOLFSBANE, 0, FOOD_CLASS, 1, 0 }, - { UNDEF_TYP, UNDEF_SPE, SPBOOK_CLASS, 2, UNDEF_BLESS }, - { 0, 0, 0, 0, 0 } +static const struct trobj Priest[] = { + { MACE, 1, WEAPON_CLASS, 1, 1, 1 }, + { ROBE, 0, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { SMALL_SHIELD, 0, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { POT_WATER, 0, POTION_CLASS, 4, 4, 1 }, /* holy water */ + { CLOVE_OF_GARLIC, 0, FOOD_CLASS, 1, 1, 0 }, + { SPRIG_OF_WOLFSBANE, 0, FOOD_CLASS, 1, 1, 0 }, + { UNDEF_TYP, UNDEF_SPE, SPBOOK_CLASS, 2, 2, UNDEF_BLESS }, + { 0, 0, 0, 0, 0, 0 } }; -static struct trobj Ranger[] = { -#define RAN_BOW 1 -#define RAN_TWO_ARROWS 2 -#define RAN_ZERO_ARROWS 3 - { DAGGER, 1, WEAPON_CLASS, 1, UNDEF_BLESS }, - { BOW, 1, WEAPON_CLASS, 1, UNDEF_BLESS }, - { ARROW, 2, WEAPON_CLASS, 50, UNDEF_BLESS }, - { ARROW, 0, WEAPON_CLASS, 30, UNDEF_BLESS }, - { CLOAK_OF_DISPLACEMENT, 2, ARMOR_CLASS, 1, UNDEF_BLESS }, - { CRAM_RATION, 0, FOOD_CLASS, 4, 0 }, - { 0, 0, 0, 0, 0 } +static const struct trobj Ranger[] = { + { DAGGER, 1, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { BOW, 1, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { ARROW, 2, WEAPON_CLASS, 50, 59, UNDEF_BLESS }, + { ARROW, 0, WEAPON_CLASS, 30, 39, UNDEF_BLESS }, + { CLOAK_OF_DISPLACEMENT, 2, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { CRAM_RATION, 0, FOOD_CLASS, 4, 4, 0 }, + { 0, 0, 0, 0, 0, 0 } }; -static struct trobj Rogue[] = { -#define R_DAGGERS 1 - { SHORT_SWORD, 0, WEAPON_CLASS, 1, UNDEF_BLESS }, - { DAGGER, 0, WEAPON_CLASS, 10, 0 }, /* quan is variable */ - { LEATHER_ARMOR, 1, ARMOR_CLASS, 1, UNDEF_BLESS }, - { POT_SICKNESS, 0, POTION_CLASS, 1, 0 }, - { LOCK_PICK, 0, TOOL_CLASS, 1, 0 }, - { SACK, 0, TOOL_CLASS, 1, 0 }, - { 0, 0, 0, 0, 0 } +static const struct trobj Rogue[] = { + { SHORT_SWORD, 0, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { DAGGER, 0, WEAPON_CLASS, 6, 15, 0 }, + { LEATHER_ARMOR, 1, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { POT_SICKNESS, 0, POTION_CLASS, 1, 1, 0 }, + { LOCK_PICK, 0, TOOL_CLASS, 1, 1, 0 }, + { SACK, 0, TOOL_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; -static struct trobj Samurai[] = { -#define S_ARROWS 3 - { KATANA, 0, WEAPON_CLASS, 1, UNDEF_BLESS }, - { SHORT_SWORD, 0, WEAPON_CLASS, 1, UNDEF_BLESS }, /* wakizashi */ - { YUMI, 0, WEAPON_CLASS, 1, UNDEF_BLESS }, - { YA, 0, WEAPON_CLASS, 25, UNDEF_BLESS }, /* variable quan */ - { SPLINT_MAIL, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, - { 0, 0, 0, 0, 0 } +static const struct trobj Samurai[] = { + { KATANA, 0, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { SHORT_SWORD, 0, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, /* wakizashi */ + { YUMI, 0, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { YA, 0, WEAPON_CLASS, 26, 45, UNDEF_BLESS }, + { SPLINT_MAIL, 0, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { 0, 0, 0, 0, 0, 0 } }; -static struct trobj Tourist[] = { -#define T_DARTS 0 - { DART, 2, WEAPON_CLASS, 25, UNDEF_BLESS }, /* quan is variable */ - { UNDEF_TYP, UNDEF_SPE, FOOD_CLASS, 10, 0 }, - { POT_EXTRA_HEALING, 0, POTION_CLASS, 2, UNDEF_BLESS }, - { SCR_MAGIC_MAPPING, 0, SCROLL_CLASS, 4, UNDEF_BLESS }, - { HAWAIIAN_SHIRT, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, - { EXPENSIVE_CAMERA, UNDEF_SPE, TOOL_CLASS, 1, 0 }, - { CREDIT_CARD, 0, TOOL_CLASS, 1, 0 }, - { 0, 0, 0, 0, 0 } +static const struct trobj Tourist[] = { + { DART, 2, WEAPON_CLASS, 21, 40, UNDEF_BLESS }, + { UNDEF_TYP, UNDEF_SPE, FOOD_CLASS, 10, 10, 0 }, + { POT_EXTRA_HEALING, 0, POTION_CLASS, 2, 2, UNDEF_BLESS }, + { SCR_MAGIC_MAPPING, 0, SCROLL_CLASS, 4, 4, UNDEF_BLESS }, + { HAWAIIAN_SHIRT, 0, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { EXPENSIVE_CAMERA, UNDEF_SPE, TOOL_CLASS, 1, 1, 0 }, + { CREDIT_CARD, 0, TOOL_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; -static struct trobj Valkyrie[] = { - { SPEAR, 1, WEAPON_CLASS, 1, UNDEF_BLESS }, - { DAGGER, 0, WEAPON_CLASS, 1, UNDEF_BLESS }, - { SMALL_SHIELD, 3, ARMOR_CLASS, 1, UNDEF_BLESS }, - { FOOD_RATION, 0, FOOD_CLASS, 1, 0 }, - { 0, 0, 0, 0, 0 } +static const struct trobj Valkyrie[] = { + { SPEAR, 1, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { DAGGER, 0, WEAPON_CLASS, 1, 1, UNDEF_BLESS }, + { SMALL_SHIELD, 3, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { FOOD_RATION, 0, FOOD_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; -static struct trobj Wizard[] = { - { QUARTERSTAFF, 1, WEAPON_CLASS, 1, 1 }, - { CLOAK_OF_MAGIC_RESISTANCE, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, - { UNDEF_TYP, UNDEF_SPE, WAND_CLASS, 1, UNDEF_BLESS }, - { UNDEF_TYP, UNDEF_SPE, RING_CLASS, 2, UNDEF_BLESS }, - { UNDEF_TYP, UNDEF_SPE, POTION_CLASS, 3, UNDEF_BLESS }, - { UNDEF_TYP, UNDEF_SPE, SCROLL_CLASS, 3, UNDEF_BLESS }, - { SPE_FORCE_BOLT, 0, SPBOOK_CLASS, 1, 1 }, - { UNDEF_TYP, UNDEF_SPE, SPBOOK_CLASS, 1, UNDEF_BLESS }, - { MAGIC_MARKER, 19, TOOL_CLASS, 1, 0 }, /* actually spe = 18 + d4 */ - { 0, 0, 0, 0, 0 } +static const struct trobj Wizard[] = { + { QUARTERSTAFF, 1, WEAPON_CLASS, 1, 1, 1 }, + { CLOAK_OF_MAGIC_RESISTANCE, 0, ARMOR_CLASS, 1, 1, UNDEF_BLESS }, + { UNDEF_TYP, UNDEF_SPE, WAND_CLASS, 1, 1, UNDEF_BLESS }, + { UNDEF_TYP, UNDEF_SPE, RING_CLASS, 2, 2, UNDEF_BLESS }, + { UNDEF_TYP, UNDEF_SPE, POTION_CLASS, 3, 3, UNDEF_BLESS }, + { UNDEF_TYP, UNDEF_SPE, SCROLL_CLASS, 3, 3, UNDEF_BLESS }, + { SPE_FORCE_BOLT, 0, SPBOOK_CLASS, 1, 1, 1 }, + { UNDEF_TYP, UNDEF_SPE, SPBOOK_CLASS, 1, 1, UNDEF_BLESS }, + { MAGIC_MARKER, 19, TOOL_CLASS, 1, 1, 0 }, /* actually spe = 18 + d4 */ + { 0, 0, 0, 0, 0, 0 } }; /* * Optional extra inventory items. */ -static struct trobj Tinopener[] = { { TIN_OPENER, 0, TOOL_CLASS, 1, 0 }, - { 0, 0, 0, 0, 0 } }; -static struct trobj Magicmarker[] = { { MAGIC_MARKER, 19, TOOL_CLASS, 1, 0 }, - { 0, 0, 0, 0, 0 } }; -static struct trobj Lamp[] = { { OIL_LAMP, 1, TOOL_CLASS, 1, 0 }, - { 0, 0, 0, 0, 0 } }; -static struct trobj Blindfold[] = { { BLINDFOLD, 0, TOOL_CLASS, 1, 0 }, - { 0, 0, 0, 0, 0 } }; -static struct trobj Instrument[] = { { WOODEN_FLUTE, 0, TOOL_CLASS, 1, 0 }, - { 0, 0, 0, 0, 0 } }; -static struct trobj Xtra_food[] = { { UNDEF_TYP, UNDEF_SPE, FOOD_CLASS, 2, 0}, - { 0, 0, 0, 0, 0 } }; -static struct trobj Leash[] = { { LEASH, 0, TOOL_CLASS, 1, 0 }, - { 0, 0, 0, 0, 0 } }; -static struct trobj Towel[] = { { TOWEL, 0, TOOL_CLASS, 1, 0 }, - { 0, 0, 0, 0, 0 } }; -static struct trobj Wishing[] = { { WAN_WISHING, 3, WAND_CLASS, 1, 0 }, - { 0, 0, 0, 0, 0 } }; -static struct trobj Money[] = { { GOLD_PIECE, 0, COIN_CLASS, 1, 0 }, - { 0, 0, 0, 0, 0 } }; +static const struct trobj Healing_book[] = + { { SPE_HEALING, UNDEF_SPE, SPBOOK_CLASS, 1, 1, 1 }, + { 0, 0, 0, 0, 0, 0 } }; +static const struct trobj Protection_book[] = + { { SPE_PROTECTION, UNDEF_SPE, SPBOOK_CLASS, 1, 1, 1 }, + { 0, 0, 0, 0, 0, 0 } }; +static const struct trobj Confuse_monster_book[] = + { { SPE_CONFUSE_MONSTER, UNDEF_SPE, SPBOOK_CLASS, 1, 1, 1 }, + { 0, 0, 0, 0, 0, 0 } }; +static const struct trobj Tinopener[] = + { { TIN_OPENER, 0, TOOL_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; +static const struct trobj Magicmarker[] = + { { MAGIC_MARKER, 19, TOOL_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; +static const struct trobj Lamp[] = + { { OIL_LAMP, 1, TOOL_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; +static const struct trobj Blindfold[] = + { { BLINDFOLD, 0, TOOL_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; +static const struct trobj Xtra_food[] = + { { UNDEF_TYP, UNDEF_SPE, FOOD_CLASS, 2, 2, 0}, + { 0, 0, 0, 0, 0, 0 } }; +static const struct trobj Leash[] = + { { LEASH, 0, TOOL_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; +static const struct trobj Towel[] = + { { TOWEL, 0, TOOL_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; +static const struct trobj Wishing[] = + { { WAN_WISHING, 3, WAND_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; +static const struct trobj Money[] = + { { GOLD_PIECE, 0, COIN_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; /* race-based substitutions for initial inventory; the weaker cloak for elven rangers is intentional--they shoot better */ -static struct inv_sub { +static const struct inv_sub { short race_pm, item_otyp, subs_otyp; } inv_subs[] = { { PM_ELF, DAGGER, ELVEN_DAGGER }, @@ -562,8 +576,8 @@ knows_object(int obj, boolean override_pauper) { if (u.uroleplay.pauper && !override_pauper) return; - discover_object(obj, TRUE, FALSE); - objects[obj].oc_pre_discovered = 1; /* not a "discovery" */ + /* mark as known, but not yet encountered */ + discover_object(obj, TRUE, FALSE, FALSE); } /* Know ordinary (non-magical) objects of a certain class, @@ -589,8 +603,10 @@ knows_class(char sym) */ for (ct = svb.bases[(uchar) sym]; ct < svb.bases[(uchar) sym + 1]; ct++) { - /* not flagged as magic but shouldn't be pre-discovered */ - if (ct == CORNUTHAUM || ct == DUNCE_CAP) + /* not flagged as magic but shouldn't be pre-discovered + (small shields look the same as two types of magical shield; + cornuthaum / dunce cap look the same as each other) */ + if (ct == CORNUTHAUM || ct == DUNCE_CAP || ct == SMALL_SHIELD) continue; if (sym == WEAPON_CLASS) { odummy.otyp = ct; /* update 'o' */ @@ -612,7 +628,11 @@ knows_class(char sym) } } -/* role-specific initializations */ +/* role-specific initializations, mostly inventory + + other things may be initialised here, but the function might run more than + once, so any non-inventory initialisations should be nonrandom and + idempotent (i.e. doing them twice is OK) */ staticfn void u_init_role(void) { @@ -641,24 +661,20 @@ u_init_role(void) knows_object(TOUCHSTONE, FALSE); /* FALSE: don't override pauper here, * but TOUCHSTONE will be made known * in pauper_reinit() */ - skill_init(Skill_A); break; case PM_BARBARIAN: if (rn2(100) >= 50) { /* see above comment */ - Barbarian[B_MAJOR].trotyp = BATTLE_AXE; - Barbarian[B_MINOR].trotyp = SHORT_SWORD; + ini_inv(Barbarian_0); + } else { + ini_inv(Barbarian_1); } - ini_inv(Barbarian); if (!rn2(6)) ini_inv(Lamp); knows_class(WEAPON_CLASS); /* excluding polearms */ knows_class(ARMOR_CLASS); - skill_init(Skill_B); break; case PM_CAVE_DWELLER: - Cave_man[C_AMMO].trquan = rn1(11, 10); /* 10..20 */ ini_inv(Cave_man); - skill_init(Skill_C); break; case PM_HEALER: u.umoney0 = rn1(1000, 1001); @@ -666,7 +682,6 @@ u_init_role(void) if (!rn2(25)) ini_inv(Lamp); knows_object(POT_FULL_HEALING, FALSE); - skill_init(Skill_H); break; case PM_KNIGHT: ini_inv(Knight); @@ -674,15 +689,14 @@ u_init_role(void) knows_class(ARMOR_CLASS); /* give knights chess-like mobility--idea from wooledge@..cwru.edu */ HJumping |= FROMOUTSIDE; - skill_init(Skill_K); break; case PM_MONK: { - static short M_spell[] = { - SPE_HEALING, SPE_PROTECTION, SPE_CONFUSE_MONSTER + static const struct trobj *M_spell[] = { + Healing_book, Protection_book, Confuse_monster_book }; - Monk[M_BOOK].trotyp = M_spell[rn2(90) / 30]; /* [0..2] */ ini_inv(Monk); + ini_inv(M_spell[rn2(90) / 30]); /* [0..2] */ if (!rn2(4)) ini_inv(Magicmarker); else if (!rn2(10)) @@ -690,7 +704,6 @@ u_init_role(void) knows_class(ARMOR_CLASS); /* sufficiently martial-arts oriented item to ignore language issue */ knows_object(SHURIKEN, FALSE); - skill_init(Skill_Mon); break; } case PM_CLERIC: /* priest/priestess */ @@ -700,7 +713,6 @@ u_init_role(void) else if (!rn2(10)) ini_inv(Lamp); knows_object(POT_WATER, TRUE); /* override pauper */ - skill_init(Skill_P); /* KMH, conduct -- * Some may claim that this isn't agnostic, since they * are literally "priests" and they have holy water. @@ -710,14 +722,10 @@ u_init_role(void) */ break; case PM_RANGER: - Ranger[RAN_TWO_ARROWS].trquan = rn1(10, 50); - Ranger[RAN_ZERO_ARROWS].trquan = rn1(10, 30); ini_inv(Ranger); knows_class(WEAPON_CLASS); /* bows, arrows, spears only */ - skill_init(Skill_Ran); break; case PM_ROGUE: - Rogue[R_DAGGERS].trquan = rn1(10, 6); u.umoney0 = 0; ini_inv(Rogue); if (!rn2(5)) @@ -726,10 +734,8 @@ u_init_role(void) * but sack will be made known in * pauper_reinit() */ knows_class(WEAPON_CLASS); /* daggers only */ - skill_init(Skill_R); break; case PM_SAMURAI: - Samurai[S_ARROWS].trquan = rn1(20, 26); ini_inv(Samurai); if (!rn2(5)) ini_inv(Blindfold); @@ -745,10 +751,8 @@ u_init_role(void) samarai an advantage of knowing several items in advance */ knows_object(i, FALSE); } - skill_init(Skill_S); break; case PM_TOURIST: - Tourist[T_DARTS].trquan = rn1(20, 21); u.umoney0 = rnd(1000); ini_inv(Tourist); if (!rn2(25)) @@ -759,7 +763,6 @@ u_init_role(void) ini_inv(Towel); else if (!rn2(20)) ini_inv(Magicmarker); - skill_init(Skill_T); break; case PM_VALKYRIE: ini_inv(Valkyrie); @@ -767,21 +770,24 @@ u_init_role(void) ini_inv(Lamp); knows_class(WEAPON_CLASS); /* excludes polearms */ knows_class(ARMOR_CLASS); - skill_init(Skill_V); break; case PM_WIZARD: ini_inv(Wizard); if (!rn2(5)) ini_inv(Blindfold); - skill_init(Skill_W); break; default: /* impossible */ break; } + + gn.nocreate = STRANGE_OBJECT; + gn.nocreate2 = STRANGE_OBJECT; + gn.nocreate3 = STRANGE_OBJECT; + gn.nocreate4 = STRANGE_OBJECT; } -/* race-specific initializations */ +/* race-specific initializations, same restrictions as u_init_role */ staticfn void u_init_race(void) { @@ -797,9 +803,12 @@ u_init_race(void) * get only non-magic instruments. */ if (Role_if(PM_CLERIC) || Role_if(PM_WIZARD)) { - static int trotyp[] = { WOODEN_FLUTE, TOOLED_HORN, WOODEN_HARP, - BELL, BUGLE, LEATHER_DRUM }; - Instrument[0].trotyp = ROLL_FROM(trotyp); + static const int trotyp[] = + { WOODEN_FLUTE, TOOLED_HORN, WOODEN_HARP, + BELL, BUGLE, LEATHER_DRUM }; + const struct trobj Instrument[] = + { { ROLL_FROM(trotyp), 0, TOOL_CLASS, 1, 1, 0 }, + { 0, 0, 0, 0, 0, 0 } }; ini_inv(Instrument); } @@ -854,7 +863,7 @@ u_init_race(void) } } -/* for 'pauper' aka 'unpreparsed'; take away any skills (bare-handed combat, +/* for 'pauper' aka 'unprepared'; take away any skills (bare-handed combat, riding) that are better than unskilled; learn the book (without carrying it or knowing its spell yet) for some key spells */ staticfn void @@ -930,8 +939,9 @@ u_init_carry_attr_boost(void) } } +/* initialise u, except inventory, attributes, skills and discoveries */ void -u_init(void) +u_init_misc(void) { int i; struct u_roleplay tmpuroleplay = u.uroleplay; /* set by rcfile options */ @@ -1004,7 +1014,7 @@ u_init(void) /* * For now, everyone starts out with a night vision range of 1 and - * their xray range disabled. + * their xray_range disabled. */ u.nv_range = 1; u.xray_range = -1; @@ -1017,44 +1027,19 @@ u_init(void) if (u.uroleplay.blind) HBlinded |= FROMOUTSIDE; /* set PermaBlind */ - u_init_role(); - u_init_race(); - if (u.uroleplay.pauper) - pauper_reinit(); - /* roughly based on distribution in human population */ u.uhandedness = rn2(10) ? RIGHT_HANDED : LEFT_HANDED; - if (discover) - ini_inv(Wishing); - - if (wizard) - read_wizkit(); - - if (u.umoney0) - ini_inv(Money); - u.umoney0 += hidden_gold(TRUE); /* in case sack has gold in it */ - - find_ac(); /* get initial ac value */ - init_attr(75); /* init attribute values */ - vary_init_attr(); /* minor variation to attrs */ - u_init_carry_attr_boost(); max_rank_sz(); /* set max str size for class ranks */ - /* If we have at least one spell, force starting Pw to be enough, - so hero can cast the level 1 spell they should have */ - if (num_spells() && (u.uenmax < SPELL_LEV_PW(1))) - u.uen = u.uenmax = u.uenpeak = u.ueninc[u.ulevel] = SPELL_LEV_PW(1); - return; } -/* skills aren't initialized, so we use the role-specific skill lists */ -staticfn boolean -restricted_spell_discipline(int otyp) +/* the appropriate set of skills for the role */ +staticfn const struct def_skill * +skills_for_role(void) { const struct def_skill *skills; - int this_skill = spell_skilltype(otyp); switch (Role_switch) { case PM_ARCHEOLOGIST: @@ -1097,10 +1082,20 @@ restricted_spell_discipline(int otyp) skills = Skill_W; break; default: - skills = 0; /* lint suppression */ + panic("No skills found for role"); break; } + return skills; +} + +/* skills aren't initialized, so we use the role-specific skill lists */ +staticfn boolean +restricted_spell_discipline(int otyp) +{ + const struct def_skill *skills = skills_for_role(); + int this_skill = spell_skilltype(otyp); + while (skills && skills->skill != P_NONE) { if (skills->skill == this_skill) return FALSE; @@ -1109,6 +1104,15 @@ restricted_spell_discipline(int otyp) return TRUE; } +/* randomizes the quantity given a trobj description */ +staticfn long +trquan(const struct trobj *trop) +{ + if (!trop->trquan_min) + return 1; + return trop->trquan_min + rn2(trop->trquan_max - trop->trquan_min + 1); +} + /* create random object of certain class, filtering out too powerful items */ staticfn struct obj * ini_inv_mkobj_filter(int oclass, boolean got_level1_spellbook) @@ -1175,7 +1179,7 @@ ini_inv_mkobj_filter(int oclass, boolean got_level1_spellbook) /* substitute object with something else based on race. only changes otyp, and returns it. */ staticfn short -ini_inv_obj_substitution(struct trobj *trop, struct obj *obj) +ini_inv_obj_substitution(const struct trobj *trop, struct obj *obj) { if (gu.urace.mnum != PM_HUMAN) { int i; @@ -1198,15 +1202,19 @@ ini_inv_obj_substitution(struct trobj *trop, struct obj *obj) return obj->otyp; } -staticfn void -ini_inv_adjust_obj(struct trobj *trop, struct obj *obj) +/* returns: TRUE to stop generating items from this trobj, + FALSE for normal behaviour */ +staticfn boolean +ini_inv_adjust_obj(const struct trobj *trop, struct obj *obj) { + boolean stop = FALSE; if (trop->trclass == COIN_CLASS) { /* no "blessed" or "identified" money */ obj->quan = u.umoney0; } else { if (objects[obj->otyp].oc_uses_known) obj->known = 1; + /* not observe_object during startup, that's handled later */ obj->dknown = obj->bknown = obj->rknown = 1; if (Is_container(obj) || obj->otyp == STATUE) { obj->cknown = obj->lknown = 1; @@ -1216,8 +1224,8 @@ ini_inv_adjust_obj(struct trobj *trop, struct obj *obj) if (obj->opoisoned && u.ualign.type != A_CHAOTIC) obj->opoisoned = 0; if (obj->oclass == WEAPON_CLASS || obj->oclass == TOOL_CLASS) { - obj->quan = (long) trop->trquan; - trop->trquan = 1; + obj->quan = trquan(trop); + stop = TRUE; } else if (obj->oclass == GEM_CLASS && is_graystone(obj) && obj->otyp != FLINT) { obj->quan = 1L; @@ -1238,6 +1246,7 @@ ini_inv_adjust_obj(struct trobj *trop, struct obj *obj) } /* defined after setting otyp+quan + blessedness */ obj->owt = weight(obj); + return stop; } /* initial inventory: wear, wield, learn the spell/obj */ @@ -1246,9 +1255,9 @@ ini_inv_use_obj(struct obj *obj) { /* Make the type known if necessary */ if (OBJ_DESCR(objects[obj->otyp]) && obj->known) - discover_object(obj->otyp, TRUE, FALSE); + discover_object(obj->otyp, TRUE, TRUE, FALSE); if (obj->otyp == OIL_LAMP) - discover_object(POT_OIL, TRUE, FALSE); + discover_object(POT_OIL, TRUE, TRUE, FALSE); if (obj->oclass == ARMOR_CLASS) { if (is_shield(obj) && !uarms && !(uwep && bimanual(uwep))) { @@ -1289,15 +1298,17 @@ ini_inv_use_obj(struct obj *obj) } staticfn void -ini_inv(struct trobj *trop) +ini_inv(const struct trobj *trop) { struct obj *obj; int otyp; boolean got_sp1 = FALSE; /* got a level 1 spellbook? */ + long quan; if (u.uroleplay.pauper) /* pauper gets no items */ return; + quan = trquan(trop); while (trop->trclass) { otyp = (int) trop->trotyp; if (otyp != UNDEF_TYP) { @@ -1305,8 +1316,8 @@ ini_inv(struct trobj *trop) } else { /* UNDEF_TYP */ obj = ini_inv_mkobj_filter(trop->trclass, got_sp1); otyp = obj->otyp; - /* Heavily relies on the fact that 1) we create wands - * before rings, 2) that we create rings before + /* Heavily relies on the facts that 1) we create wands + * before rings, that 2) we create rings before * spellbooks, and that 3) not more than 1 object of a * particular symbol is to be prohibited. (For more * objects, we need more nocreate variables...) @@ -1339,33 +1350,71 @@ ini_inv(struct trobj *trop) continue; } - ini_inv_adjust_obj(trop, obj); + if (ini_inv_adjust_obj(trop, obj)) + quan = 1; obj = addinv(obj); - ini_inv_use_obj(obj); - /* First spellbook should be level 1 - did we get it? */ if (obj->oclass == SPBOOK_CLASS && objects[obj->otyp].oc_level == 1) got_sp1 = TRUE; - if (--trop->trquan) + if (--quan) continue; /* make a similar object */ trop++; + quan = trquan(trop); } } +/* initialise starting inventory and attributes + + this function can be run multiple times and will overwrite the effects of + previous runs */ +void +u_init_inventory_attrs(void) +{ + gl.lastinvnr = 51; + while (gi.invent) + useupall(gi.invent); + + u.umoney0 = 0; + u_init_role(); + u_init_race(); + + if (discover) + ini_inv(Wishing); + + if (u.umoney0) + ini_inv(Money); + u.umoney0 += hidden_gold(TRUE); /* in case sack has gold in it */ + + init_attr(75); /* init attribute values */ + vary_init_attr(); /* minor variation to attrs */ + u_init_carry_attr_boost(); +} + +/* side effects of starting inventory (e.g. discovering it) and skills (both + those based on role and those based on starting inventory) */ +void +u_init_skills_discoveries(void) +{ + struct obj *otmp; + for (otmp = gi.invent; otmp; otmp = otmp->nobj) + ini_inv_use_obj(otmp); + + skill_init(skills_for_role()); + if (u.uroleplay.pauper) + pauper_reinit(); + + /* If we have at least one spell, force starting Pw to be enough, + so hero can cast the level 1 spell they should have */ + if (num_spells() && (u.uenmax < SPELL_LEV_PW(1))) + u.uen = u.uenmax = u.uenpeak = u.ueninc[u.ulevel] = SPELL_LEV_PW(1); + + find_ac(); /* get initial ac value */ +} + #undef UNDEF_TYP #undef UNDEF_SPE #undef UNDEF_BLESS -#undef B_MAJOR -#undef B_MINOR -#undef C_AMMO -#undef M_BOOK -#undef RAN_BOW -#undef RAN_TWO_ARROWS -#undef RAN_ZERO_ARROWS -#undef R_DAGGERS -#undef S_ARROWS -#undef T_DARTS /*u_init.c*/ diff --git a/src/uhitm.c b/src/uhitm.c index cbf797e8e..af4e8ca8a 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -26,6 +26,7 @@ staticfn void hmon_hitmon_barehands(struct _hitmon_data *, struct monst *) NONNULLARG12; staticfn void hmon_hitmon_weapon_ranged(struct _hitmon_data *, struct monst *, struct obj *) NONNULLARG123; +staticfn boolean backstabbable(struct monst *) NONNULLARG1; staticfn void hmon_hitmon_weapon_melee(struct _hitmon_data *, struct monst *, struct obj *) NONNULLARG123; staticfn void hmon_hitmon_weapon(struct _hitmon_data *, struct monst *, @@ -198,7 +199,7 @@ attack_checks( return FALSE; if (svc.context.forcefight) { - /* Do this in the caller, after we checked that the monster + /* Do this in the caller, after we have checked that the monster * didn't die from the blow. Reason: putting the 'I' there * causes the hero to forget the square's contents since * both 'I' and remembered contents are stored in .glyph. @@ -372,7 +373,8 @@ find_roll_to_hit( *role_roll_penalty = 0; /* default is `none' */ - tmp = 1 + Luck + abon() + find_mac(mtmp) + u.uhitinc + tmp = 1 + abon() + find_mac(mtmp) + u.uhitinc + + (sgn(Luck) * ((abs(Luck) + 2) / 3)) + maybe_polyd(gy.youmonst.data->mlevel, u.ulevel); /* some actions should occur only once during multiple attacks */ @@ -662,7 +664,7 @@ hitum_cleave( int count, umort, x = u.ux, y = u.uy; /* find the direction toward primary target */ - i = xytod(u.dx, u.dy); + i = xytodir(u.dx, u.dy); if (i == DIR_ERR) { impossible("hitum_cleave: unknown target direction [%d,%d,%d]?", u.dx, u.dy, u.dz); @@ -914,6 +916,20 @@ hmon_hitmon_weapon_ranged( } } +/* can monster be stabbed in the back? */ +staticfn boolean +backstabbable(struct monst *mon) +{ + return !amorphous(mon->data) + && !is_whirly(mon->data) + && !noncorporeal(mon->data) + && mon->data->mlet != S_BLOB + && mon->data->mlet != S_EYE + && mon->data->mlet != S_FUNGUS + && canseemon(mon) + && (mon->mflee || helpless(mon)); +} + staticfn void hmon_hitmon_weapon_melee( struct _hitmon_data *hmd, @@ -928,13 +944,20 @@ hmon_hitmon_weapon_melee( hmd->dmg = dmgval(obj, mon); /* a minimal hit doesn't exercise proficiency */ hmd->train_weapon_skill = (hmd->dmg > 1); + + /* Healer with anatomy knowledge */ + if (Role_if(PM_HEALER) && hmd->hand_to_hand + && obj->oclass == WEAPON_CLASS + && objects[obj->otyp].oc_skill == P_KNIFE) + hmd->dmg += min(3, svm.mvitals[monsndx(mon->data)].died / 6); + /* special attack actions */ if (!hmd->train_weapon_skill || mon == u.ustuck || u.twoweap /* Cleaver can hit up to three targets at once so don't let it also hit from behind or shatter foes' weapons */ || (hmd->hand_to_hand && is_art(obj, ART_CLEAVER))) { ; /* no special bonuses */ - } else if (mon->mflee && Role_if(PM_ROGUE) && !Upolyd + } else if (Role_if(PM_ROGUE) && backstabbable(mon) && !Upolyd /* multi-shot throwing is too powerful here */ && hmd->hand_to_hand) { You("strike %s from behind!", mon_nam(mon)); @@ -1026,7 +1049,7 @@ hmon_hitmon_weapon_melee( && (is_ammo(obj) || is_missile(obj))) { if (ammo_and_launcher(obj, uwep)) { /* elves and samurai do extra damage using their own - bows with own arrows; they're highly trained */ + bows with their own arrows; they're highly trained */ if (Role_if(PM_SAMURAI) && obj->otyp == YA && uwep->otyp == YUMI) hmd->dmg++; @@ -1133,7 +1156,7 @@ hmon_hitmon_misc_obj( corpse_xname(obj, (const char *) 0, obj->dknown ? CXN_PFX_THE : CXN_ARTICLE)); - obj->dknown = 1; + observe_object(obj); if (!munstone(mon, TRUE)) minstapetrify(mon, TRUE); if (resists_ston(mon)) @@ -1316,6 +1339,16 @@ hmon_hitmon_misc_obj( hmd->get_dmg_bonus = FALSE; break; default: + if ((objects[obj->otyp].oc_material == VEGGY || + objects[obj->otyp].oc_material == PAPER) && + obj->oclass != SPBOOK_CLASS) { + /* vegetables (and similar) do no damage, because they + aren't rigid enough; paper objects also do no damage, + except for books */ + hmd->dmg = 0; + hmd->get_dmg_bonus = FALSE; + break; + } /* non-weapons can damage because of their weight */ /* (but not too much) */ hmd->dmg = (obj->owt + 99) / 100; @@ -1558,7 +1591,7 @@ hmon_hitmon_pet( struct obj *obj UNUSED) { if (mon->mtame && hmd->dmg > 0) { - /* do this even if the pet is being killed (affects revival) */ + /* do this even if the pet is being killed or migrating (affects revival) */ abuse_dog(mon); /* reduces tameness */ /* flee if still alive and still tame; if already suffering from untimed fleeing, no effect, otherwise increases timed fleeing */ @@ -1576,7 +1609,7 @@ hmon_hitmon_splitmon( if ((hmd->mdat == &mons[PM_BLACK_PUDDING] || hmd->mdat == &mons[PM_BROWN_PUDDING]) /* pudding is alive and healthy enough to split */ - && mon->mhp > 1 && !mon->mcan + && mon->mhp > 1 && !mon->mcan && !hmd->offmap /* iron weapon using melee or polearm hit [3.6.1: metal weapon too; also allow either or both weapons to cause split when twoweap] */ && obj && (obj == uwep || (u.twoweap && obj == uswapwep)) @@ -1752,6 +1785,7 @@ hmon_hitmon( hmd.needpoismsg = FALSE; hmd.poiskilled = FALSE; hmd.already_killed = FALSE; + hmd.offmap = FALSE; hmd.destroyed = FALSE; hmd.dryit = FALSE; hmd.doreturn = FALSE; @@ -1811,9 +1845,21 @@ hmon_hitmon( mon->mhp -= hmd.dmg; } /* adjustments might have made tmp become less than what - a level draining artifact has already done to max HP */ + a level-draining artifact has already done to max HP */ if (mon->mhp > mon->mhpmax) mon->mhp = mon->mhpmax; + if (mon->mx == 0) { + /* + * jousting can lead to: + * mhurtle_to_doom() + * mhurtle() + * mintrap() + * trapeffect_hole() + * trapeffect_level_telep() + * migrate_to_level() + * Set offmap in that situation so code to follow can test for it.*/ + hmd.offmap = TRUE; + } if (DEADMONSTER(mon)) hmd.destroyed = TRUE; @@ -1874,7 +1920,7 @@ hmon_hitmon( Your("%s %s no longer poisoned.", hmd.saved_oname, vtense(hmd.saved_oname, "are")); - if (!hmd.destroyed) { + if (!hmd.destroyed && !hmd.offmap) { int hitflags = M_ATTK_HIT; wakeup(mon, TRUE); @@ -2059,6 +2105,11 @@ joust(struct monst *mon, /* target */ /* sanity check; lance must be wielded in order to joust */ if (obj != uwep && (obj != uswapwep || !u.twoweap)) return 0; + /* can't joust while trapped--not enough room to maneuver; + * TODO? if the steed is trapped in a pit, perhaps the hero ought to be + * able to joust against a monster that's in a conjoined pit */ + if (u.utrap) + return 0; /* if using two weapons, use worse of lance and two-weapon skills */ skill_rating = P_SKILL(weapon_type(obj)); /* lance skill */ @@ -3990,7 +4041,8 @@ mhitm_ad_phys( if (mattk->aatyp == AT_WEAP && otmp) { struct obj *marmg; int tmp; - boolean was_poisoned = (otmp->opoisoned || permapoisoned(otmp)); + boolean was_poisoned = (otmp->opoisoned + || permapoisoned(otmp)); if (otmp->otyp == CORPSE && touch_petrifies(&mons[otmp->corpsenm])) { @@ -4033,6 +4085,9 @@ mhitm_ad_phys( tmp -= rnd(-u.uac); if (tmp < 1) tmp = 1; + if (Half_physical_damage) + tmp = (tmp + 1) / 2; + if (u.mh - tmp > 1 && (objects[otmp->otyp].oc_material == IRON /* relevant 'metal' objects are scalpel and tsurugi */ @@ -4053,13 +4108,13 @@ mhitm_ad_phys( char buf[BUFSZ]; /* similar to mhitm_really_poison, but we don't use the - * exact same values, nor do we want the same 1/8 chance of - * the poison taking (use 1/4, same as in the mhitm case). */ + * exact same values, nor do we want same 1/8 chance of + * poison taking (use 1/4, same as in the mhitm case). */ Sprintf(buf, "%s %s", s_suffix(Monnam(magr)), mpoisons_subj(magr, mattk)); /* arbitrary, but most poison sources in the game are * strength-based. With hpdamchance = 10, HP damage occurs - * 1/2 of the time and it will hit Str the rest of the time. + * 1/2 of the time and it will hit Str rest of the time. * (This is the same as poisoned ammo.) */ poisoned(buf, A_STR, pmname(magr->data, Mgender(magr)), 10, FALSE); @@ -6241,6 +6296,21 @@ stumble_onto_mimic(struct monst *mtmp) map_invisible(mtmp->mx, mtmp->my); } +boolean +disguised_as_non_mon(struct monst *mtmp) +{ + return (!sensemon(mtmp) + && M_AP_TYPE(mtmp) + && M_AP_TYPE(mtmp) != M_AP_MONSTER); +} + +boolean +disguised_as_mon(struct monst *mtmp) +{ + return (M_AP_TYPE(mtmp) + && M_AP_TYPE(mtmp) == M_AP_MONSTER); +} + staticfn void nohandglow(struct monst *mon) { diff --git a/src/utf8map.c b/src/utf8map.c index 87688b9ce..19f3b18e4 100644 --- a/src/utf8map.c +++ b/src/utf8map.c @@ -207,7 +207,8 @@ add_custom_urep_entry( } #endif /* ENHANCED_SYMBOLS */ -void reset_customsymbols(void) +void +reset_customsymbols(void) { #ifdef ENHANCED_SYMBOLS free_all_glyphmap_u(); diff --git a/src/version.c b/src/version.c index 99c36efdf..0b82963b8 100644 --- a/src/version.c +++ b/src/version.c @@ -157,6 +157,9 @@ doversion(void) { char buf[BUFSZ]; + if (iflags.menu_requested) + return doextversion(); + pline("%s", getversionstring(buf, sizeof buf)); return ECMD_OK; } diff --git a/src/vision.c b/src/vision.c index 3f33d726c..709f54d02 100644 --- a/src/vision.c +++ b/src/vision.c @@ -399,7 +399,7 @@ staticfn int new_angle(struct rm *, unsigned char *, int, int); * this is good enough. * * + When this function is called we don't have all of the seen - * information (we're doing a top down scan in vision_recalc). + * information (we're doing a top-down scan in vision_recalc). * We would need to scan once to set all IN_SIGHT and COULD_SEE * bits, then again to correctly set the seenv bits. * + I'm trying to make this as cheap as possible. The display @@ -561,7 +561,7 @@ vision_recalc(int control) /* * Our own version of the update loop below. We know we can't see - * anything, so we only need update positions we used to be able + * anything, so we only need to update positions we used to be able * to see. */ temp_array = gv.viz_array; /* set gv.viz_array so newsym() will work */ @@ -588,7 +588,7 @@ vision_recalc(int control) if (Underwater && !Is_waterlevel(&u.uz)) { /* - * The hero is under water. Only see surrounding locations if + * The hero is underwater. Only see surrounding locations if * they are also underwater. This overrides night vision but * does not override x-ray vision. */ @@ -714,7 +714,7 @@ vision_recalc(int control) * + Set the IN_SIGHT bit for places that we could see and are lit. * + Reset changed places. * - * There is one thing that make deciding what the hero can see + * There is one thing that makes deciding what the hero can see * difficult: * * 1. Directional lighting. Items that block light create problems. @@ -761,10 +761,9 @@ vision_recalc(int control) || IS_WALL(lev->typ)) && !viz_clear[row][col]) { /* * Make sure doors, walls, boulders or mimics don't show - * up - * at the end of dark hallways. We do this by checking + * up at the end of dark hallways. We do this by checking * the adjacent position. If it is lit, then we can see - * the door or wall, otherwise we can't. + * the door or wall; otherwise we can't. */ dx = u.ux - col; dx = sign(dx); @@ -1786,7 +1785,7 @@ right_side( * * Otherwise, we know we can see the right edge of the current row. * - * This must be a strict less than so that we can always see a + * This must be a strict "less than" so that we can always see a * horizontal wall, even if it is adjacent to us. */ if (right_mark < right_edge) { diff --git a/src/weapon.c b/src/weapon.c index 59fa7c30d..81da05613 100644 --- a/src/weapon.c +++ b/src/weapon.c @@ -16,6 +16,7 @@ staticfn boolean could_advance(int); staticfn boolean peaked_skill(int); staticfn int slots_required(int); staticfn void skill_advance(int); +staticfn void add_skills_to_menu(winid, boolean, boolean); /* Categories whose names don't come from OBJ_NAME(objects[type]) */ @@ -154,7 +155,7 @@ hitval(struct obj *otmp, struct monst *mon) if (Is_weapon) tmp += otmp->spe; - /* Put weapon specific "to hit" bonuses in below: */ + /* Put weapon-specific "to hit" bonuses in below: */ tmp += objects[otmp->otyp].oc_hitbon; /* Put weapon vs. monster type "to hit" bonuses in below: */ @@ -301,7 +302,7 @@ dmgval(struct obj *otmp, struct monst *mon) } if (objects[otyp].oc_material <= LEATHER && thick_skinned(ptr)) - /* thick skinned/scaled creatures don't feel it */ + /* thick-skinned or scaled creatures don't feel it */ tmp = 0; if (ptr == &mons[PM_SHADE] && !shade_glare(otmp)) tmp = 0; @@ -1215,6 +1216,102 @@ static const struct skill_range { { P_FIRST_SPELL, P_LAST_SPELL, "Spellcasting Skills" }, }; +/* write a list of skills onto the given menu + + if selectable is set, give selection letters for skills that can be + advanced and leave room for them on skills that can't be advanced */ +void +add_skills_to_menu(winid win, boolean selectable, boolean speedy) +{ + int pass, i, len, longest; + anything any; + char buf[BUFSZ], sklnambuf[BUFSZ]; + const char *prefix; + int clr = NO_COLOR; + + /* Find the longest skill name. */ + for (longest = 0, i = 0; i < P_NUM_SKILLS; i++) { + if (P_RESTRICTED(i)) + continue; + if ((len = Strlen(P_NAME(i))) > longest) + longest = len; + } + + /* List the skills, making ones that could be advanced selectable if + selectable is set. List the miscellaneous skills first. Possible + future enhancement: list spell skills before weapon skills for + spellcaster roles. */ + for (pass = 0; pass < SIZE(skill_ranges); pass++) + for (i = skill_ranges[pass].first; i <= skill_ranges[pass].last; + i++) { + /* Print headings for skill types */ + any = cg.zeroany; + if (i == skill_ranges[pass].first) + add_menu_heading(win, skill_ranges[pass].name); + + if (P_RESTRICTED(i)) + continue; + /* + * Sigh, this assumes a monospaced font unless + * iflags.menu_tab_sep is set in which case it puts + * tabs between columns. + * The 12 is the longest skill level name. + * The " " is room for a selection letter and dash, "a - ". + */ + if (!selectable) + prefix = ""; + else if (can_advance(i, speedy)) + prefix = ""; /* will be preceded by menu choice */ + else if (could_advance(i)) + prefix = " * "; + else if (peaked_skill(i)) + prefix = " # "; + else + prefix = " "; + (void) skill_level_name(i, sklnambuf); + if (wizard) { + if (!iflags.menu_tab_sep) + Snprintf(buf, sizeof buf, + " %s%-*s %-12s %5d(%4d)", prefix, + longest, P_NAME(i), sklnambuf, P_ADVANCE(i), + practice_needed_to_advance(P_SKILL(i))); + else + Snprintf(buf, sizeof buf, + " %s%s\t%s\t%5d(%4d)", prefix, P_NAME(i), + sklnambuf, P_ADVANCE(i), + practice_needed_to_advance(P_SKILL(i))); + } else { + if (!iflags.menu_tab_sep) + Snprintf(buf, sizeof buf, + " %s %-*s [%s]", prefix, longest, + P_NAME(i), sklnambuf); + else + Snprintf(buf, sizeof buf, + " %s%s\t[%s]", prefix, P_NAME(i), + sklnambuf); + } + any.a_int = selectable && can_advance(i, speedy) ? i + 1 : 0; + add_menu(win, &nul_glyphinfo, &any, 0, 0, + ATR_NONE, clr, buf, MENU_ITEMFLAGS_NONE); + } +} + +/* Displays a skill list for dumplog purposes. */ +void +show_skills(void) +{ + winid win; + menu_item *selected; + + pline("Skills:"); + win = create_nhwindow(NHW_MENU); + start_menu(win, MENU_BEHAVE_STANDARD); + add_skills_to_menu(win, FALSE, FALSE); + end_menu(win, ""); + nhUse(select_menu(win, PICK_NONE, &selected)); + destroy_nhwindow(win); +} + /* * The `#enhance' extended command. What we _really_ would like is * to keep being able to pick things to advance until we couldn't any @@ -1226,29 +1323,24 @@ static const struct skill_range { int enhance_weapon_skill(void) { - int pass, i, n, len, longest, to_advance, eventually_advance, maxxed_cnt; - char buf[BUFSZ], sklnambuf[BUFSZ]; - const char *prefix; + int i, n, to_advance, eventually_advance, maxxed_cnt; + char buf[BUFSZ]; menu_item *selected; - anything any; winid win; boolean speedy = FALSE; - int clr = NO_COLOR; /* player knows about #enhance, don't show tip anymore */ - svc.context.tips[TIP_ENHANCE] = TRUE; + svc.context.tips |= (1 << TIP_ENHANCE); if (wizard && y_n("Advance skills without practice?") == 'y') speedy = TRUE; do { - /* find longest available skill name, count those that can advance */ + /* count advanceable skills */ to_advance = eventually_advance = maxxed_cnt = 0; - for (longest = 0, i = 0; i < P_NUM_SKILLS; i++) { + for (i = 0; i < P_NUM_SKILLS; i++) { if (P_RESTRICTED(i)) continue; - if ((len = Strlen(P_NAME(i))) > longest) - longest = len; if (can_advance(i, speedy)) to_advance++; else if (could_advance(i)) @@ -1280,64 +1372,8 @@ enhance_weapon_skill(void) add_menu_str(win, ""); } - /* List the skills, making ones that could be advanced - selectable. List the miscellaneous skills first. - Possible future enhancement: list spell skills before - weapon skills for spellcaster roles. */ - for (pass = 0; pass < SIZE(skill_ranges); pass++) - for (i = skill_ranges[pass].first; i <= skill_ranges[pass].last; - i++) { - /* Print headings for skill types */ - any = cg.zeroany; - if (i == skill_ranges[pass].first) - add_menu_heading(win, skill_ranges[pass].name); - - if (P_RESTRICTED(i)) - continue; - /* - * Sigh, this assumes a monospaced font unless - * iflags.menu_tab_sep is set in which case it puts - * tabs between columns. - * The 12 is the longest skill level name. - * The " " is room for a selection letter and dash, "a - ". - */ - if (can_advance(i, speedy)) - prefix = ""; /* will be preceded by menu choice */ - else if (could_advance(i)) - prefix = " * "; - else if (peaked_skill(i)) - prefix = " # "; - else - prefix = - (to_advance + eventually_advance + maxxed_cnt > 0) - ? " " - : ""; - (void) skill_level_name(i, sklnambuf); - if (wizard) { - if (!iflags.menu_tab_sep) - Snprintf(buf, sizeof buf, - " %s%-*s %-12s %5d(%4d)", prefix, - longest, P_NAME(i), sklnambuf, P_ADVANCE(i), - practice_needed_to_advance(P_SKILL(i))); - else - Snprintf(buf, sizeof buf, - " %s%s\t%s\t%5d(%4d)", prefix, P_NAME(i), - sklnambuf, P_ADVANCE(i), - practice_needed_to_advance(P_SKILL(i))); - } else { - if (!iflags.menu_tab_sep) - Snprintf(buf, sizeof buf, - " %s %-*s [%s]", prefix, longest, - P_NAME(i), sklnambuf); - else - Snprintf(buf, sizeof buf, - " %s%s\t[%s]", prefix, P_NAME(i), - sklnambuf); - } - any.a_int = can_advance(i, speedy) ? i + 1 : 0; - add_menu(win, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, buf, MENU_ITEMFLAGS_NONE); - } + add_skills_to_menu( + win, to_advance + eventually_advance + maxxed_cnt > 0, speedy); Strcpy(buf, (to_advance > 0) ? "Pick a skill to advance:" : "Current skills:"); @@ -1351,7 +1387,7 @@ enhance_weapon_skill(void) n = selected[0].item.a_int - 1; /* get item selected */ free((genericptr_t) selected); skill_advance(n); - /* check for more skills able to advance, if so then .. */ + /* check for more skills able to advance; if so, then... */ for (n = i = 0; i < P_NUM_SKILLS; i++) { if (can_advance(i, speedy)) { if (!speedy) diff --git a/src/were.c b/src/were.c index b38bf0ed0..59197177f 100644 --- a/src/were.c +++ b/src/were.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 were.c $NHDT-Date: 1717570494 2024/06/05 06:54:54 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.36 $ */ +/* NetHack 3.7 were.c $NHDT-Date: 1766588485 2025/12/24 07:01:25 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.41 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -84,6 +84,7 @@ were_beastie(int pm) case PM_WOLF: case PM_WARG: case PM_WINTER_WOLF: + case PM_WINTER_WOLF_CUB: return PM_WEREWOLF; default: break; @@ -201,6 +202,8 @@ you_were(void) an(mons[u.ulycn].pmnames[NEUTRAL] + 4)); if (!paranoid_query(ParanoidWerechange, qbuf)) return; + } else if (monster_nearby()) { + return; } gw.were_changes++; (void) polymon(u.ulycn); @@ -216,6 +219,7 @@ you_unwere(boolean purify) set_ulycn(NON_PM); /* cure lycanthropy */ } if (!Unchanging && is_were(gy.youmonst.data) + && !monster_nearby() && (!controllable_poly || !paranoid_query(ParanoidWerechange, "Remain in beast form?"))) rehumanize(); diff --git a/src/wield.c b/src/wield.c index 53b0aa94f..7b24b3df7 100644 --- a/src/wield.c +++ b/src/wield.c @@ -86,7 +86,7 @@ static const char * 1. Initializing the slot during character generation or a * restore. * 2. Setting the slot due to a player's actions. - * 3. If one of the objects in the slot are split off, these + * 3. If one of the objects in the slot is split off, these * functions can be used to put the remainder back in the slot. * 4. Putting an item that was thrown and returned back into the slot. * 5. Emptying the slot, by passing a null object. NEVER pass @@ -958,7 +958,7 @@ chwepon(struct obj *otmp, int amount) if (otyp != STRANGE_OBJECT) makeknown(otyp); if (multiple) - (void) encumber_msg(); + encumber_msg(); return 1; } else if (uwep->otyp == CRYSKNIFE && amount < 0) { multiple = (uwep->quan > 1L); @@ -975,7 +975,7 @@ chwepon(struct obj *otmp, int amount) if (otyp != STRANGE_OBJECT && otmp->bknown) makeknown(otyp); if (multiple) - (void) encumber_msg(); + encumber_msg(); return 1; } @@ -1021,7 +1021,7 @@ chwepon(struct obj *otmp, int amount) /* * Enchantment, which normally improves a weapon, has an - * addition adverse reaction on Magicbane whose effects are + * additional adverse reaction on Magicbane whose effects are * spe dependent. Give an obscure clue here. */ if (u_wield_art(ART_MAGICBANE) && uwep->spe >= 0) { diff --git a/src/windows.c b/src/windows.c index b929cf62f..a36b0eaa4 100644 --- a/src/windows.c +++ b/src/windows.c @@ -333,7 +333,7 @@ choose_windows(const char *s) if (tmps) free((genericptr_t) tmps) /*, tmps = 0*/ ; - if (windowprocs.win_raw_print == def_raw_print || WINDOWPORT(safestartup)) + if (windowprocs.win_raw_print == def_raw_print) nh_terminate(EXIT_SUCCESS); } @@ -1852,7 +1852,8 @@ get_menu_coloring(const char *str, int *color, int *attr) return FALSE; } -int select_menu(winid window, int how, menu_item **menu_list) +int +select_menu(winid window, int how, menu_item **menu_list) { int reslt; boolean old_bot_disabled = gb.bot_disabled; @@ -1867,6 +1868,31 @@ void getlin(const char *query, char *bufp) { boolean old_bot_disabled = gb.bot_disabled; + char *obufp = bufp; + boolean got_cmdq = FALSE; + struct _cmd_queue *cmdq = NULL; + + while ((cmdq = cmdq_pop()) != 0) { + if (cmdq->typ == CMDQ_KEY) { + got_cmdq = TRUE; + *bufp = (cmdq->key != '\n') ? cmdq->key : '\0'; + bufp++; + if (cmdq->key == '\n') + break; + } else { + break; + } + free(cmdq); + cmdq = NULL; + } + if (cmdq) + free(cmdq); + + if (got_cmdq) { + *bufp = '\0'; + pline("%s %s", query, obufp); + return; + } program_state.in_getlin = 1; gb.bot_disabled = TRUE; diff --git a/src/wizard.c b/src/wizard.c index 2f4b0bdb9..c89e3296f 100644 --- a/src/wizard.c +++ b/src/wizard.c @@ -386,10 +386,12 @@ tactics(struct monst *mtmp) mtmp->mavenge = 1; /* covetous monsters attack while fleeing */ if (In_W_tower(mx, my, &u.uz) || (mtmp->iswiz && !sx && !mon_has_amulet(mtmp))) { - if (!rn2(3 + mtmp->mhp / 10)) + if (!noteleport_level(mtmp) && + !rn2(3 + mtmp->mhp / 10)) (void) rloc(mtmp, RLOC_MSG); } else if (sx && (mx != sx || my != sy)) { - if (!mnearto(mtmp, sx, sy, TRUE, RLOC_MSG)) { + if (!noteleport_level(mtmp) && + !mnearto(mtmp, sx, sy, TRUE, RLOC_MSG)) { /* couldn't move to the target spot for some reason, so stay where we are (don't actually need rloc_to() because mtmp is still on the map at ... */ @@ -408,7 +410,7 @@ tactics(struct monst *mtmp) /*FALLTHRU*/ case STRAT_NONE: /* harass */ - if (!rn2(!mtmp->mflee ? 5 : 33)) + if (!noteleport_level(mtmp) && !rn2(!mtmp->mflee ? 5 : 33)) mnexto(mtmp, RLOC_MSG); return 0; @@ -419,13 +421,16 @@ tactics(struct monst *mtmp) int targ = (int) (strat & STRAT_GOAL); struct obj *otmp; - if (!targ) { /* simply wants you to close */ + if (!targ || !isok(tx, ty)) { /* simply wants you to close */ return 0; } + if (noteleport_level(mtmp) && !monnear(mtmp, tx, ty)) + return 0; if (u_at(tx, ty) || where == STRAT_PLAYER) { /* player is standing on it (or has it) */ mx = mtmp->mx, my = mtmp->my; - if (!mnearto(mtmp, tx, ty, FALSE, RLOC_MSG)) + if (noteleport_level(mtmp) || + !mnearto(mtmp, tx, ty, FALSE, RLOC_MSG)) rloc_to(mtmp, mx, my); /* no room? stay put */ return 0; } @@ -445,13 +450,14 @@ tactics(struct monst *mtmp) return 0; } else { /* a monster is standing on it - cause some trouble */ - if (!rn2(5)) + if (!rn2(5) && !noteleport_level(mtmp)) mnexto(mtmp, RLOC_MSG); return 0; } } else { /* a monster has it - 'port beside it. */ mx = mtmp->mx, my = mtmp->my; - if (!mnearto(mtmp, tx, ty, FALSE, RLOC_MSG)) + if (!noteleport_level(mtmp) && + !mnearto(mtmp, tx, ty, FALSE, RLOC_MSG)) rloc_to(mtmp, mx, my); /* no room? stay put */ return 0; } @@ -631,7 +637,7 @@ nasty(struct monst *summoner) * randomized so it won't always do so. */ for (j = 0; j < 20; j++) { - /* Don't create more spellcasters of the monsters' level or + /* Don't create more spellcasters of the monster's level or * higher--avoids chain summoners filling up the level. */ trylimit = 10 + 1; /* 10 tries */ @@ -704,7 +710,7 @@ nasty(struct monst *summoner) return count; } -/* Let's resurrect the wizard, for some unexpected fun. */ +/* Let's resurrect the Wizard, for some unexpected fun. */ void resurrect(void) { @@ -744,7 +750,7 @@ resurrect(void) if (!mtmp->mx) mtmp = 0; /* note: there might be a second Wizard; if so, - he'll have to wait til the next resurrection */ + he'll have to wait until the next resurrection */ break; } } diff --git a/src/wizcmds.c b/src/wizcmds.c index 6dd756880..b045578b8 100644 --- a/src/wizcmds.c +++ b/src/wizcmds.c @@ -37,7 +37,7 @@ wiz_wish(void) /* Unlimited wishes for debug mode by Paul Polderman */ flags.verbose = FALSE; makewish(); flags.verbose = save_verbose; - (void) encumber_msg(); + encumber_msg(); } else pline(unavailcmd, ecname_from_fn(wiz_wish)); return ECMD_OK; @@ -1776,9 +1776,7 @@ wiz_display_macros(void) destroy_nhwindow(win); return ECMD_OK; } -#endif /* (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) */ -#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) /* the #wizmondiff command */ int wiz_mon_diff(void) @@ -1820,6 +1818,46 @@ wiz_mon_diff(void) destroy_nhwindow(win); return ECMD_OK; } + +/* the #wizobjprobs command */ +int +wiz_objprobs(void) +{ + int win; + char buf[BUFSZ]; + int probsum[MAXOCLASSES]; + int otyp; + int oclass = objects[FIRST_OBJECT].oc_class; + memset(probsum, 0, sizeof probsum); + + for (otyp = FIRST_OBJECT; otyp < NUM_OBJECTS; otyp++) { + probsum[(int) objects[otyp].oc_class] += objects[otyp].oc_prob; + } + + win = create_nhwindow(NHW_TEXT); + for (otyp = FIRST_OBJECT; otyp < NUM_OBJECTS; otyp++) { + /* placeholders for extra descriptions aren't generatable objects */ + if (!OBJ_NAME(objects[otyp])) + continue; + + if ((int) objects[otyp].oc_class != oclass) { + putstr(win, 0, ""); + } + oclass = objects[otyp].oc_class; + + Snprintf(buf, sizeof buf, "%4d / %4d (%6.2f%%): %s", + objects[otyp].oc_prob, + probsum[oclass], + (float) objects[otyp].oc_prob * 100.f / + (float) probsum[oclass], + OBJ_NAME(objects[otyp])); + putstr(win, 0, buf); + } + display_nhwindow(win, FALSE); + destroy_nhwindow(win); + + return ECMD_OK; +} #endif /* (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) */ /* #migratemons command */ @@ -1832,6 +1870,7 @@ wiz_migrate_mons(void) struct permonst *ptr; struct monst *mtmp; boolean use_random_mon = TRUE; + boolean mongen_saved = iflags.debug_mongen; #endif d_level tolevel; @@ -1864,6 +1903,7 @@ wiz_migrate_mons(void) else if (mcount > ((COLNO - 1) * ROWNO)) mcount = (COLNO - 1) * ROWNO; + iflags.debug_mongen = FALSE; while (mcount > 0) { if (use_random_mon) { ptr = rndmonst(); @@ -1876,6 +1916,7 @@ wiz_migrate_mons(void) (coord *) 0); mcount--; } + iflags.debug_mongen = mongen_saved; #endif /* DEBUG_MIGRATING_MONS */ return ECMD_OK; } diff --git a/src/worm.c b/src/worm.c index e2bb61c3b..a74b81ac8 100644 --- a/src/worm.c +++ b/src/worm.c @@ -31,7 +31,7 @@ staticfn struct wseg *create_worm_tail(int); /* may return NULL */ * If wormno == 0 this does not mean that the monster is not a worm, * it just means that the monster does not have a long worm tail. * - * The actual segments of a worm are not full blown monst structs. + * The actual segments of a worm are not full-blown monst structs. * They are small wseg structs, and their position in the levels.monsters[][] * array is held by the monst struct of the head of the worm. This makes * things like probing and hit point bookkeeping much easier. @@ -46,7 +46,7 @@ staticfn struct wseg *create_worm_tail(int); /* may return NULL */ * wheads: The last (end) of a linked list of segments. This points to * the segment that is at the same position as the real monster * (the head). Note that the segment that wheads[wormno] points - * to, is not displayed. It is simply there to keep track of + * to is not displayed. It is simply there to keep track of * where the head came from, so that worm movement and display * are simplified later. * Keeping the head segment of the worm at the end of the list @@ -113,8 +113,8 @@ get_wormno(void) * Initialize the worm entry. This will set up the worm grow time, and * create and initialize the dummy segment for wheads[] and wtails[]. * - * If the worm has no tail (ie get_wormno() fails) then this function need - * not be called. + * If the worm has no tail (ie get_wormno() fails) then this function + * need not be called. */ void initworm(struct monst *worm, int wseg_count) @@ -282,7 +282,7 @@ worm_move(struct monst *worm) * * Check for mon->wormno before calling this function! * - * The worm don't move so it should shrink. + * The worm doesn't move, so it should shrink. */ void worm_nomove(struct monst *worm) @@ -379,7 +379,7 @@ cutworm(struct monst *worm, coordxy x, coordxy y, int cut_chance, new_wnum; if (!wnum) - return; /* bullet proofing */ + return; /* bullet-proofing */ if (x == worm->mx && y == worm->my) return; /* hit on head */ @@ -420,7 +420,7 @@ cutworm(struct monst *worm, coordxy x, coordxy y, /* * At this point, the old worm is correct. Any new worm will have - * it's head at "curr" and its tail at "new_tail". The old worm + * its head at "curr" and its tail at "new_tail". The old worm * must be at least level 3 in order to produce a new worm. */ new_worm = 0; @@ -804,7 +804,7 @@ random_dir(int x, int y, int *nx, int *ny) { *nx = x + (x > 1 /* extreme left ? */ ? (x < COLNO - 1 /* extreme right ? */ - ? (rn2(3) - 1) /* neither so +1, 0, or -1 */ + ? (rn2(3) - 1) /* neither, so +1, 0, or -1 */ : -rn2(2)) /* right edge, use -1 or 0 */ : rn2(2)); /* left edge, use 0 or 1 */ if (*nx != x) /* if x has changed, do same thing with y */ diff --git a/src/worn.c b/src/worn.c index b7db2d94a..6b2c082c1 100644 --- a/src/worn.c +++ b/src/worn.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 worn.c $NHDT-Date: 1736530208 2025/01/10 09:30:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.116 $ */ +/* NetHack 3.7 worn.c $NHDT-Date: 1770949988 2026/02/12 18:33:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.119 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -205,6 +205,70 @@ wearmask_to_obj(long wornmask) return (struct obj *) 0; } +/* convert an armor wornmask to corresponding category */ +int +wornmask_to_armcat(long mask) +{ + int cat = 0; + + switch (mask & W_ARMOR) { + case W_ARM: + cat = ARM_SUIT; + break; + case W_ARMC: + cat = ARM_CLOAK; + break; + case W_ARMH: + cat = ARM_HELM; + break; + case W_ARMS: + cat = ARM_SHIELD; + break; + case W_ARMG: + cat = ARM_GLOVES; + break; + case W_ARMF: + cat = ARM_BOOTS; + break; + case W_ARMU: + cat = ARM_SHIRT; + break; + } + return cat; +} + +/* convert an armor category to corresponding wornmask */ +long +armcat_to_wornmask(int cat) +{ + long mask = 0L; + + switch (cat) { + case ARM_SUIT: + mask = W_ARM; + break; + case ARM_CLOAK: + mask = W_ARMC; + break; + case ARM_HELM: + mask = W_ARMH; + break; + case ARM_SHIELD: + mask = W_ARMS; + break; + case ARM_GLOVES: + mask = W_ARMG; + break; + case ARM_BOOTS: + mask = W_ARMF; + break; + case ARM_SHIRT: + mask = W_ARMU; + break; + } + return mask; +} + /* return a bitmask of the equipment slot(s) a given item might be worn in */ long wearslot(struct obj *obj) @@ -399,11 +463,13 @@ check_wornmask_slots(void) } /* check_wornmask_slots() */ void -mon_set_minvis(struct monst *mon) +mon_set_minvis( + struct monst *mon, + boolean cursed_potion) { - mon->perminvis = 1; + mon->perminvis = !cursed_potion ? 1 : 0; if (!mon->invis_blkd) { - mon->minvis = 1; + mon->minvis = mon->perminvis; newsym(mon->mx, mon->my); /* make it disappear */ if (mon->wormno) see_wsegs(mon); /* and any tail too */ @@ -423,7 +489,7 @@ mon_adjust_speed( switch (adjust) { case 2: mon->permspeed = MFAST; - give_msg = FALSE; /* special case monster creation */ + give_msg = FALSE; /* special-case monster creation */ break; case 1: if (mon->permspeed == MSLOW) @@ -916,7 +982,7 @@ m_dowear_type( } } update_mon_extrinsics(mon, best, TRUE, creation); - /* if couldn't see it but now can, or vice versa, */ + /* if couldn't see it but now can, or vice versa */ if (!creation && (sawmon ^ canseemon(mon))) { if (mon->minvis && !See_invisible) { pline("Suddenly you cannot see %s.", nambuf); diff --git a/src/write.c b/src/write.c index fd967685e..d7a1eddb4 100644 --- a/src/write.c +++ b/src/write.c @@ -4,12 +4,11 @@ #include "hack.h" staticfn int cost(struct obj *) NONNULLARG1; -staticfn boolean label_known(int, struct obj *) NO_NNARGS; staticfn int write_ok(struct obj *) NO_NNARGS; staticfn char *new_book_description(int, char *) NONNULL NONNULLPTRS; /* - * returns basecost of a scroll or a spellbook + * returns base cost of a scroll or a spellbook */ staticfn int cost(struct obj *otmp) @@ -57,34 +56,6 @@ cost(struct obj *otmp) return 1000; } -/* decide whether the hero knowns a particular scroll's label; - unfortunately, we can't track things that haven't been added to - the discoveries list and aren't present in current inventory, - so some scrolls with ought to yield True will end up False */ -staticfn boolean -label_known(int scrolltype, struct obj *objlist) -{ - struct obj *otmp; - - /* only scrolls */ - if (objects[scrolltype].oc_class != SCROLL_CLASS) - return FALSE; - /* type known implies full discovery; otherwise, - user-assigned name implies partial discovery */ - if (objects[scrolltype].oc_name_known || objects[scrolltype].oc_uname) - return TRUE; - /* check inventory, including carried containers with known contents */ - for (otmp = objlist; otmp; otmp = otmp->nobj) { - if (otmp->otyp == scrolltype && otmp->dknown) - return TRUE; - if (Has_contents(otmp) && otmp->cknown - && label_known(scrolltype, otmp->cobj)) - return TRUE; - } - /* not found */ - return FALSE; -} - /* getobj callback for object to write on */ staticfn int write_ok(struct obj *obj) @@ -134,7 +105,7 @@ dowrite(struct obj *pen) : "scroll"; if (Blind) { if (!paper->dknown) { - You("don't know if that %s is blank or not.", typeword); + You("don't know whether that %s is blank or not.", typeword); return ECMD_OK; } else if (paper->oclass == SPBOOK_CLASS) { /* can't write a magic book while blind */ @@ -143,12 +114,13 @@ dowrite(struct obj *pen) return ECMD_OK; } } - paper->dknown = 1; + observe_object(paper); if (paper->otyp != SCR_BLANK_PAPER && paper->otyp != SPE_BLANK_PAPER) { pline("That %s is not blank!", typeword); exercise(A_WIS, FALSE); return ECMD_TIME; } + makeknown(SCR_BLANK_PAPER); /* what to write */ Sprintf(qbuf, "What type of %s do you want to write?", typeword); @@ -326,13 +298,8 @@ dowrite(struct obj *pen) * * Writing by description requires that the hero knows the * description (a scroll's label, that is, since books by_descr - * are rejected above). BUG: We can only do this for known - * scrolls and for the case where the player has assigned a - * name to put it onto the discoveries list; we lack a way to - * track other scrolls which have been seen closely enough to - * read the label without then being ID'd or named. The only - * exception is for currently carried inventory, where we can - * check for one [with its dknown bit set] of the same type. + * are rejected above). This is done by checking to see if a + * scroll with the same description has been encountered. * * Normal requirements can be overridden if hero is Lucky. */ @@ -345,7 +312,7 @@ dowrite(struct obj *pen) /* if known, then either by-name or by-descr works */ if (!objects[new_obj->otyp].oc_name_known /* else if named, then only by-descr works */ - && !(by_descr && label_known(new_obj->otyp, gi.invent)) + && !(by_descr && objects[new_obj->otyp].oc_encountered) /* else fresh knowledge of the spell works */ && spell_knowledge != spe_Fresh /* and Luck might override after previous checks have failed */ @@ -384,7 +351,7 @@ dowrite(struct obj *pen) return ECMD_TIME; } - /* useup old scroll / spellbook */ + /* use up old scroll / spellbook */ useup(paper); /* success */ @@ -404,8 +371,11 @@ dowrite(struct obj *pen) /* unlike alchemy, for example, a successful result yields the specifically chosen item so hero recognizes it even if blind; the exception is for being lucky writing an undiscovered scroll, - where the label associated with the type-name isn't known yet */ - new_obj->dknown = label_known(new_obj->otyp, gi.invent) ? 1 : 0; + where the label associated with the type-name isn't known yet; + but if writing by description, the description is always known */ + new_obj->dknown = FALSE; + if (objects[new_obj->otyp].oc_name_known || by_descr) + observe_object(new_obj); new_obj = hold_another_object(new_obj, "Oops! %s out of your grasp!", The(aobjnam(new_obj, "slip")), diff --git a/src/zap.c b/src/zap.c index a1a5c3380..ac6d8e760 100644 --- a/src/zap.c +++ b/src/zap.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 zap.c $NHDT-Date: 1741793439 2025/03/12 07:30:39 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.564 $ */ +/* NetHack 3.7 zap.c $NHDT-Date: 1770949988 2026/02/12 18:33:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.584 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -26,6 +26,7 @@ staticfn void skiprange(int, int *, int *) NONNULLPTRS; staticfn void maybe_explode_trap(struct trap *, struct obj *, boolean *) NONNULLARG3; staticfn void zap_map(coordxy, coordxy, struct obj *) NONNULLARG3; +staticfn void bounce_dir(coordxy, coordxy, int *, int *, int) NO_NNARGS; staticfn int zap_hit(int, int); staticfn void disintegrate_mon(struct monst *, int, const char *) NONNULLARG1; staticfn int adtyp_to_prop(int); @@ -39,6 +40,7 @@ staticfn int maybe_destroy_item(struct monst *, struct obj *, int) NONNULLPTRS; staticfn boolean destroyable(struct obj *, int); staticfn void wishcmdassist(int); +staticfn void wish_history_menu(char *); #define ZT_MAGIC_MISSILE (AD_MAGM - 1) #define ZT_FIRE (AD_FIRE - 1) @@ -132,14 +134,14 @@ learnwand(struct obj *obj) /* if type already discovered, treat this item has having been seen even if hero is currently blinded (skips redundant makeknown) */ if (objects[obj->otyp].oc_name_known) { - obj->dknown = 1; /* will usually be set already */ + observe_object(obj); /* will usually be dknown already */ /* otherwise discover it if item itself has been or can be seen */ } else { /* in case it was picked up while blind and then zapped without examining inventory after regaining sight (bypassing xname) */ if (!Blind) - obj->dknown = 1; + observe_object(obj); /* make the discovery iff we know what we're manipulating */ if (obj->dknown) makeknown(obj->otyp); @@ -190,14 +192,16 @@ bhitm(struct monst *mtmp, struct obj *otmp) /*FALLTHRU*/ case SPE_FORCE_BOLT: reveal_invis = TRUE; - if (disguised_mimic) - seemimic(mtmp); learn_it = cansee(gb.bhitpos.x, gb.bhitpos.y); if (resists_magm(mtmp)) { /* match effect on player */ + if (disguised_mimic && !disguised_as_mon(mtmp)) + seemimic(mtmp); shieldeff(mtmp->mx, mtmp->my); pline("Boing!"); /* 3.7: used to 'break' to avoid setting learn_it here */ } else if (u.uswallow || rnd(20) < 10 + find_mac(mtmp)) { + if (disguised_mimic) + seemimic(mtmp); dmg = d(2, 12); if (dbldam) dmg *= 2; @@ -206,7 +210,8 @@ bhitm(struct monst *mtmp, struct obj *otmp) hit(zap_type_text, mtmp, exclam(dmg)); (void) resist(mtmp, otmp->oclass, dmg, TELL); } else { - miss(zap_type_text, mtmp); + if (!disguised_mimic) + miss(zap_type_text, mtmp); learn_it = FALSE; } break; @@ -349,7 +354,7 @@ bhitm(struct monst *mtmp, struct obj *otmp) seemimic(mtmp); /* format monster's name before altering its visibility */ Strcpy(nambuf, Monnam(mtmp)); - mon_set_minvis(mtmp); + mon_set_minvis(mtmp, FALSE); if (!oldinvis && knowninvisible(mtmp)) { pline("%s turns transparent!", nambuf); reveal_invis = TRUE; @@ -607,7 +612,7 @@ staticfn void probe_objchain(struct obj *otmp) { for (; otmp; otmp = otmp->nobj) { - otmp->dknown = 1; /* treat as "seen" */ + observe_object(otmp); /* treat as "seen" */ if (Is_container(otmp) || otmp->otyp == STATUE) { otmp->lknown = 1; if (!SchroedingersBox(otmp)) @@ -1211,7 +1216,7 @@ unturn_dead(struct monst *mon) } } if (is_u && res) - (void) encumber_msg(); + encumber_msg(); return res; } @@ -1640,7 +1645,7 @@ do_osshock(struct obj *obj) go.obj_zapped = TRUE; if (gp.poly_zapped < 0) { - /* some may metamorphosize */ + /* some may metamorphose */ for (i = obj->quan; i; i--) if (!rn2(Luck + 45)) { gp.poly_zapped = objects[obj->otyp].oc_material; @@ -1711,7 +1716,7 @@ poly_obj(struct obj *obj, int id) if (obj->otyp == UNICORN_HORN && obj->degraded_horn) magic_obj = 0; /* Try up to 3 times to make the magic-or-not status of - the new item be the same as it was for the old one. */ + the new item the same as the old item. */ otmp = (struct obj *) 0; do { if (otmp) @@ -1733,7 +1738,7 @@ poly_obj(struct obj *obj, int id) /* preserve quantity */ otmp->quan = obj->quan; - /* preserve the shopkeepers (lack of) interest */ + /* preserve the shopkeeper's (lack of) interest */ otmp->no_charge = obj->no_charge; /* preserve inventory letter if in inventory */ if (obj_location == OBJ_INVENT) @@ -2002,7 +2007,7 @@ stone_to_flesh_obj(struct obj *obj) /* nonnull */ return 0; (void) get_obj_location(obj, &oox, &ooy, 0); - /* add more if stone objects are added.. */ + /* add more if stone objects are added... */ switch (objects[obj->otyp].oc_class) { case ROCK_CLASS: /* boulders and statues */ case TOOL_CLASS: /* figurines */ @@ -2217,7 +2222,7 @@ bhito(struct obj *obj, struct obj *otmp) case WAN_PROBING: res = !obj->dknown; /* target object has now been "seen (up close)" */ - obj->dknown = 1; + observe_object(obj); if (Is_container(obj) || obj->otyp == STATUE) { obj->cknown = obj->lknown = 1; if (Is_box(obj) && !obj->tknown) { @@ -2246,7 +2251,7 @@ bhito(struct obj *obj, struct obj *otmp) /* view contents (not recursively) */ for (o = obj->cobj; o; o = o->nobj) - o->dknown = 1; /* "seen", even if blind */ + observe_object(o); /* "seen", even if blind */ (void) display_cinventory(obj); } res = 1; @@ -2551,6 +2556,16 @@ zapnodir(struct obj *obj) known = !!obj->dknown; (void) findit(); break; + case WAN_STASIS: { + long tmp_until = svm.moves + (long) rn1(21, 10); + + /* no immediately obvious effect, and no message so that it isn't + distinguishable from other NODIR wands that produce no message; + for multiple zaps, keep the longest duration rather than latest */ + if (tmp_until > svl.level.flags.stasis_until) + svl.level.flags.stasis_until = tmp_until; + break; + } case WAN_CREATE_MONSTER: /* create_critters() returns True iff hero sees a new monster appear */ if (create_critters(rn2(23) ? 1 : rn1(7, 2), @@ -3487,7 +3502,7 @@ spell_damage_bonus( } /* - * Generate the to hit bonus for a spell. Based on the hero's skill in + * Generate the to-hit bonus for a spell. Based on the hero's skill in * spell class and dexterity. */ staticfn int @@ -3798,8 +3813,8 @@ zap_map( * function) several objects and monsters on its path. The return value * is the monster hit (weapon != ZAPPED_WAND), or a null monster pointer. * - * Thrown and kicked objects (THROWN_WEAPON or KICKED_WEAPON) may be - * destroyed and *pobj set to NULL to indicate this. + * Thrown and kicked objects (THROWN_WEAPON or KICKED_WEAPON) may be + * destroyed and *pobj set to NULL to indicate this. * * Check !u.uswallow before calling bhit(). * This function reveals the absence of a remembered invisible monster in @@ -3810,7 +3825,7 @@ zap_map( */ struct monst * bhit( - coordxy ddx, coordxy ddy, int range, /* direction and range */ + int ddx, int ddy, int range, /* direction and range */ enum bhit_call_types weapon, /* defined in hack.h */ int (*fhitm)(MONST_P, OBJ_P), /* fns called when mon/obj hit */ int (*fhito)(OBJ_P, OBJ_P), @@ -3825,8 +3840,8 @@ bhit( boolean in_skip = FALSE, allow_skip = FALSE; boolean tethered_weapon = FALSE; int skiprange_start = 0, skiprange_end = 0, skipcount = 0; - struct obj *was_returning = - (iflags.returning_missile == obj) ? obj : (struct obj *) 0; + struct obj *was_returning = (iflags.returning_missile == obj) ? obj + : (struct obj *) 0; if (weapon == KICKED_WEAPON) { /* object starts one square in front of player */ @@ -3983,7 +3998,7 @@ bhit( through instead of stop so we call flash_hits_mon() directly rather than returning mtmp back to caller. That allows the flash to keep on going. Note that we - use mtmp->minvis not canspotmon() because it makes no + use mtmp->minvis, not canspotmon(), because it makes no difference whether hero can see the monster or not. */ if (mtmp->minvis) { obj->ox = u.ux, obj->oy = u.uy; @@ -4064,8 +4079,7 @@ bhit( break; } if (weapon != ZAPPED_WAND && weapon != INVIS_BEAM) { - /* 'I' present but no monster: erase */ - /* do this before the tmp_at() */ + /* 'I' present but no monster: erase; do this before tmp_at() */ if (glyph_is_invisible(levl[x][y].glyph) && cansee(x, y)) { unmap_object(x, y); newsym(x, y); @@ -4131,12 +4145,13 @@ bhit( * is too obviously silly. */ struct monst * -boomhit(struct obj *obj, coordxy dx, coordxy dy) +boomhit(struct obj *obj, int dx, int dy) { int i, ct; int boom; /* showsym[] index */ struct monst *mtmp; boolean counterclockwise = URIGHTY; /* ULEFTY => clockwise */ + int nhits = max(1, obj->spe + 1); /* counterclockwise traversal patterns, from @ to 1 then on through to 9 * ..........................54................................. @@ -4154,7 +4169,7 @@ boomhit(struct obj *obj, coordxy dx, coordxy dy) gb.bhitpos.x = u.ux; gb.bhitpos.y = u.uy; boom = counterclockwise ? S_boomleft : S_boomright; - i = xytod(dx, dy); + i = xytodir(dx, dy); tmp_at(DISP_FLASH, cmap_to_glyph(boom)); for (ct = 0; ct < 10; ct++) { i = DIR_CLAMP(i); @@ -4171,8 +4186,12 @@ boomhit(struct obj *obj, coordxy dx, coordxy dy) } if ((mtmp = m_at(gb.bhitpos.x, gb.bhitpos.y)) != 0) { m_respond(mtmp); - tmp_at(DISP_END, 0); - return mtmp; + if (nhits-- < 0) { + tmp_at(DISP_END, 0); + return mtmp; + } else if (throwit_mon_hit(obj, mtmp) || !gt.thrownobj) { + break; + } } if (!ZAP_POS(levl[gb.bhitpos.x][gb.bhitpos.y].typ) || closed_door(gb.bhitpos.x, gb.bhitpos.y)) { @@ -4454,21 +4473,21 @@ zhitu( monstunseesu(M_SEEN_DISINT); if (uarms) { /* destroy shield; other possessions are safe */ - (void) destroy_arm(uarms); + (void) disintegrate_arm(uarms); break; } else if (uarm) { /* destroy suit; if present, cloak goes too */ if (uarmc) - (void) destroy_arm(uarmc); - (void) destroy_arm(uarm); + (void) disintegrate_arm(uarmc); + (void) disintegrate_arm(uarm); break; } /* no shield or suit, you're dead; wipe out cloak and/or shirt in case of life-saving or bones */ if (uarmc) - (void) destroy_arm(uarmc); + (void) disintegrate_arm(uarmc); if (uarmu) - (void) destroy_arm(uarmu); + (void) disintegrate_arm(uarmu); } else if (nonliving(gy.youmonst.data) || is_demon(gy.youmonst.data)) { shieldeff(sx, sy); You("seem unaffected."); @@ -4634,6 +4653,51 @@ burn_floor_objects( return cnt; } +/* which direction a ray bounces. + current location is sx,sy, direction is ddx, ddy. + bounceback is 1/n chance of bouncing back. + caller must ensure sx,sy is a bouncing location: !ZAP_POS or closed_door + */ +staticfn void +bounce_dir(coordxy sx, coordxy sy, + int *ddx, int *ddy, + int bounceback) +{ + if (!*ddx || !*ddy || (bounceback > 0 && !rn2(bounceback))) { + *ddx = -(*ddx); + *ddy = -(*ddy); + } else { + uchar rmn; + int bounce = 0; + coordxy lsy = sy - *ddy; + coordxy lsx = sx - *ddx; + + if (isok(sx, lsy) && ZAP_POS(rmn = levl[sx][lsy].typ) + && !closed_door(sx, lsy) + && (IS_ROOM(rmn) || (isok(sx + *ddx, lsy) + && ZAP_POS(levl[sx + *ddx][lsy].typ)))) + bounce = 1; + if (isok(lsx, sy) && ZAP_POS(rmn = levl[lsx][sy].typ) + && !closed_door(lsx, sy) + && (IS_ROOM(rmn) || (isok(lsx, sy + *ddy) + && ZAP_POS(levl[lsx][sy + *ddy].typ)))) + if (!bounce || rn2(2)) + bounce = 2; + switch (bounce) { + case 0: + *ddx = -(*ddx); + FALLTHROUGH; + /*FALLTHRU*/ + case 1: + *ddy = -(*ddy); + break; + case 2: + *ddx = -(*ddx); + break; + } + } +} + /* will zap/spell/breath attack score a hit against armor class `ac'? */ staticfn int zap_hit( @@ -4692,13 +4756,13 @@ disintegrate_mon( void ubuzz(int type, int nd) { - dobuzz(type, nd, u.ux, u.uy, u.dx, u.dy, TRUE); + dobuzz(type, nd, u.ux, u.uy, u.dx, u.dy, TRUE, FALSE, FALSE); } void buzz(int type, int nd, coordxy sx, coordxy sy, int dx, int dy) { - dobuzz(type, nd, sx, sy, dx, dy, TRUE); + dobuzz(type, nd, sx, sy, dx, dy, TRUE, FALSE, FALSE); } /* @@ -4712,11 +4776,12 @@ buzz(int type, int nd, coordxy sx, coordxy sy, int dx, int dy) */ void dobuzz( - int type, /* 0..29 (by hero) or -39..-10 (by monster) */ + int type, /* 0..29 (by hero) or -39..-10 (by monster) */ int nd, /* damage strength ('number of dice') */ coordxy sx, coordxy sy, /* starting point */ int dx, int dy, /* direction delta */ - boolean say) /* announce out of sight hit/miss events if true */ + boolean sayhit, boolean saymiss, /* report out of sight hit/miss events */ + boolean forcemiss) { int range, fltyp = zaptype(type), damgtype = fltyp % 10; coordxy lsx, lsy; @@ -4729,7 +4794,7 @@ dobuzz( int spell_type; int hdmgtype = Hallucination ? rn2(6) : damgtype; - /* if it's a Hero Spell then get its SPE_TYPE */ + /* if it's a hero spell then get its SPE_TYPE */ spell_type = is_hero_spell(type) ? SPE_MAGIC_MISSILE + damgtype : 0; if (u.uswallow) { @@ -4802,7 +4867,7 @@ dobuzz( buzzmonst: gn.notonhead = (mon->mx != gb.bhitpos.x || mon->my != gb.bhitpos.y); - if (zap_hit(find_mac(mon), spell_type)) { + if (!forcemiss && zap_hit(find_mac(mon), spell_type)) { if (mon_reflects(mon, (char *) 0)) { if (cansee(mon->mx, mon->my)) { hit(flash_str(fltyp, FALSE), mon, exclam(0)); @@ -4865,7 +4930,7 @@ dobuzz( } else { if (!otmp) { /* normal non-fatal hit */ - if (say || canseemon(mon)) + if (sayhit || canseemon(mon)) hit(flash_str(fltyp, FALSE), mon, exclam(tmp)); } else { /* some armor was destroyed; no damage done */ @@ -4883,7 +4948,8 @@ dobuzz( } range -= 2; } else { - if (say || canseemon(mon)) + if (saymiss + || (canseemon(mon) && !disguised_as_non_mon(mon))) miss(flash_str(fltyp, FALSE), mon); } } else if (u_at(sx, sy) && range >= 0) { @@ -4891,9 +4957,9 @@ dobuzz( if (u.usteed && !rn2(3) && !mon_reflects(u.usteed, (char *) 0)) { mon = u.usteed; goto buzzmonst; - } else if (zap_hit((int) u.uac, 0)) { + } else if (!forcemiss && zap_hit((int) u.uac, 0)) { range -= 2; - pline_dir(xytod(-dx, -dy), "%s hits you!", + pline_dir(xytodir(-dx, -dy), "%s hits you!", The(flash_str(fltyp, FALSE))); if (Reflecting) { if (!Blind) { @@ -4929,14 +4995,12 @@ dobuzz( if (!ZAP_POS(levl[sx][sy].typ) || (closed_door(sx, sy) && range >= 0)) { - int bounce, bchance; - uchar rmn; + int bchance; make_bounce: bchance = (!isok(sx, sy) || levl[sx][sy].typ == STONE) ? 10 : (In_mines(&u.uz) && IS_WALL(levl[sx][sy].typ)) ? 20 : 75; - bounce = 0; if ((--range > 0 && isok(lsx, lsy) && cansee(lsx, lsy)) || fireball) { if (Is_airlevel(&u.uz)) { /* nothing to bounce off of */ @@ -4952,36 +5016,8 @@ dobuzz( } else pline_The("%s bounces!", flash_str(fltyp, FALSE)); } - if (!dx || !dy || !rn2(bchance)) { - dx = -dx; - dy = -dy; - } else { - if (isok(sx, lsy) && ZAP_POS(rmn = levl[sx][lsy].typ) - && !closed_door(sx, lsy) - && (IS_ROOM(rmn) || (isok(sx + dx, lsy) - && ZAP_POS(levl[sx + dx][lsy].typ)))) - bounce = 1; - if (isok(lsx, sy) && ZAP_POS(rmn = levl[lsx][sy].typ) - && !closed_door(lsx, sy) - && (IS_ROOM(rmn) || (isok(lsx, sy + dy) - && ZAP_POS(levl[lsx][sy + dy].typ)))) - if (!bounce || rn2(2)) - bounce = 2; - - switch (bounce) { - case 0: - dx = -dx; - FALLTHROUGH; - /*FALLTHRU*/ - case 1: - dy = -dy; - break; - case 2: - dx = -dx; - break; - } - tmp_at(DISP_CHANGE, zapdir_to_glyph(dx, dy, hdmgtype)); - } + bounce_dir(sx, sy, &dx, &dy, bchance); + tmp_at(DISP_CHANGE, zapdir_to_glyph(dx, dy, hdmgtype)); } } tmp_at(DISP_END, 0); @@ -6180,6 +6216,96 @@ wishcmdassist(int triesleft) destroy_nhwindow(win); } +#define MAX_WISH_HISTORY 20 +static char *wish_history[MAX_WISH_HISTORY] = { NULL }; +static int wish_history_idx = 0; + +/* add string to wish history list */ +void +wish_history_add(char *buf) +{ +#ifdef DEBUG + int i; + + if (!wizard) + return; + + for (i = 0; i < MAX_WISH_HISTORY; i++) { + int idx = (wish_history_idx + i) % MAX_WISH_HISTORY; + + if (!wish_history[idx]) + continue; + if (!strncmpi(wish_history[idx], buf, strlen(wish_history[idx]))) + break; + + } + + if (i == MAX_WISH_HISTORY) { + int idx = (wish_history_idx + i) % MAX_WISH_HISTORY; + + if (wish_history[idx]) + free(wish_history[idx]); + wish_history[idx] = (char *) alloc(strlen(buf) + 1); + strcpy(wish_history[idx], buf); + wish_history_idx = (wish_history_idx + 1) % MAX_WISH_HISTORY; + } +#endif /* DEBUG */ +} + +/* release any old wish text; called from freedynamicdata(save.c) */ +void +wish_history_flush(void) +{ +#ifdef DEBUG + int idx; + + for (idx = 0; idx < MAX_WISH_HISTORY; ++idx) { + if (wish_history[idx]) + free((genericptr_t) wish_history[idx]), wish_history[idx] = NULL; + } + wish_history_idx = 0; +#endif +} + +/* shows menu of previous wishes, copies selected into buf, max BUFSZ len. + buf is not modified, if nothing was selected. */ +staticfn void +wish_history_menu(char *buf) +{ +#ifdef DEBUG + winid win; + anything any; + int i = 0, npick; + menu_item *picks = (menu_item *) 0; + int idx; + + win = create_nhwindow(NHW_MENU); + start_menu(win, MENU_BEHAVE_STANDARD); + any = cg.zeroany; + + for (i = MAX_WISH_HISTORY-1; i >= 0; i--) { + idx = (wish_history_idx + i) % MAX_WISH_HISTORY; + if (wish_history[idx]) { + any.a_int = (i + 1); + add_menu(win, &nul_glyphinfo, &any, '\0', 0, ATR_NONE, NO_COLOR, + wish_history[idx], MENU_ITEMFLAGS_NONE); + } + } + + end_menu(win, "Wish what?"); + npick = select_menu(win, PICK_ONE, &picks); + destroy_nhwindow(win); + if (npick > 0) { + i = picks->item.a_int; + i--; + idx = (wish_history_idx + i) % MAX_WISH_HISTORY; + + if (wish_history[idx]) + strcpy(buf, wish_history[idx]); + } +#endif /* DEBUG */ +} + RESTORE_WARNING_FORMAT_NONLITERAL void @@ -6202,10 +6328,15 @@ makewish(void) if (iflags.cmdassist && tries > 0) Strcat(promptbuf, " (enter 'help' for assistance)"); Strcat(promptbuf, "?"); - getlin(promptbuf, buf); + + if (iflags.menu_requested && wish_history[0] && (tries == 0)) + wish_history_menu(buf); + else + getlin(promptbuf, buf); if (iflags.term_gone) { - svc.context.resume_wish = 1; + if (!iflags.debug_fuzzer) + svc.context.resume_wish = 1; return; } @@ -6239,9 +6370,11 @@ makewish(void) livelog_printf(LL_WISH, "declined to make a wish"); return; } else if (otmp == &hands_obj) { + wish_history_add(bufcpy); /* wizard mode terrain wish: skip livelogging, etc */ return; } + wish_history_add(bufcpy); if (otmp->oartifact) { /* update artifact bookkeeping; doesn't produce a livelog event */ diff --git a/submodules/CHKSUMS b/submodules/CHKSUMS index bea046547..c40043d77 100644 --- a/submodules/CHKSUMS +++ b/submodules/CHKSUMS @@ -6,3 +6,5 @@ # shasum -a 256 FILETOCHECK >>THISFILE 7d5ea1b9cb6aa0b59ca3dde1c6adcb57ef83a1ba8e5432c0ecd06bf439b3ad88 lua-5.4.6.tar.gz 9fbf5e28ef86c69858f6d3d34eccc32e911c1a28b4120ff3e84aaa70cfbf1e30 lua-5.4.7.tar.gz +4f18ddae154e793e46eeab727c59ef1c0c0c2b744e7b94219710d76f530629ae lua-5.4.8.tar.gz +57ccc32bbbd005cab75bcc52444052535af691789dba2b9016d5c50640d68b3d lua-5.5.0.tar.gz diff --git a/sys/amiga/.gitattributes b/sys/amiga/.gitattributes new file mode 100644 index 000000000..45572b7d6 --- /dev/null +++ b/sys/amiga/.gitattributes @@ -0,0 +1,2 @@ +*.p NHSUBST +* NH_filestag=(file%s_for_Amiga_versions) diff --git a/sys/amiga/README.amiga b/sys/amiga/README.amiga new file mode 100644 index 000000000..6aa2a2a95 --- /dev/null +++ b/sys/amiga/README.amiga @@ -0,0 +1,107 @@ + NetHack 3.7 for Amiga + ==================== + +Requirements +------------ + - AmigaOS 3.0 or later (Kickstart 39+) + - 6 MB free RAM minimum (8 MB recommended) + - Hard drive with ~5 MB free space + +Recommended: RTG graphics card (Picasso96/CyberGraphX) for best +tile rendering. Native AGA/OCS/ECS chipsets are supported. + + +Installation +------------ +Extract NH370AMI.ZIP to a directory and assign it: + + assign NetHack: + +The directory should contain: + nethack - game binary + nhdat - data library + symbols - symbol set definitions + sysconf - system configuration + nethack.cnf - game options + hack.font - font descriptor + hack/8 - font bitmap (8pt) + tiles/tiles16.iff - 16-color tiles (OCS/ECS) + tiles/tiles32.iff - 32-color tiles (AGA/RTG) + tomb.iff - tombstone image + record - high score file + +To play: + cd NetHack: + nethack + + +Display Modes +------------- +Two display modes are available, selected in nethack.cnf: + + Text mode (AMII): + OPTIONS=symset:AmigaFont + + Tile mode (AMIV): + OPTIONS=windowtype:amiv + +Tile mode auto-selects tiles32.iff (32 colors, 5 bitplanes) when the +screen supports 32+ colors, otherwise tiles16.iff (16 colors, 4 planes). + + +Configuration +------------- +Edit nethack.cnf for game options. Key settings: + + OPTIONS=windowtype:amiv - tile graphics (default) + OPTIONS=symset:AmigaFont - text mode with line-drawing chars + OPTIONS=menucolors - colored inventory items + OPTIONS=time,lit_corridor - show turn count, lit corridors + OPTIONS=boulder:0 - display boulders as '0' + +Menu color examples (add after OPTIONS=menucolors): + MENUCOLOR=" blessed "=green + MENUCOLOR=" cursed "=red + MENUCOLOR=" uncursed "=cyan + + +Building from Source +-------------------- +Cross-compilation from Linux using bebbo's m68k-amigaos-gcc toolchain +(https://franke.ms/git/bebbo/amiga-gcc): + + # Install toolchain to /opt/amiga + # Clone NetHack 3.7 source + cd NetHack + sys/unix/setup.sh sys/unix/hints/linux.370 + make fetch-lua + make CROSS_TO_AMIGA=1 fetch-regex + make CROSS_TO_AMIGA=1 all + make CROSS_TO_AMIGA=1 package + +The distribution ZIP is created at targets/amiga/NH370AMI.ZIP. + +Toolchain requirements: + - m68k-amigaos-gcc (bebbo's amigaos-cross-toolchain in /opt/amiga) + - -noixemul (libnix) for linking + - -m68000 for maximum compatibility (or -m68020/040/060) + + +Known Issues +------------ + - Native Amiga compilation (SAS/C, DICE) is not supported; + cross-compilation with GCC is required + + +Credits +------- +Olaf Seibert first ported NetHack 2.3 and 3.0 to the Amiga. +Richard Addison, Andrew Church, Jochen Erwied, Mark Gooderum, +Ken Lorber, Greg Olson, Mike Passaretti, and Gregg Wonderly +polished and extended the 3.0 and 3.1 ports. Andrew Church, +Ken Lorber, and Gregg Wonderly are responsible for the 3.2 port. +Janne Salmijärvi resurrected the Amiga port for 3.3 and +Teemu Suikki joined before 3.4.0. + +Updated for NetHack 3.7, cross compile fixes and 32 color tile support by Ingo +Paschke in 2026. diff --git a/outdated/sys/amiga/amidos.c b/sys/amiga/amidos.c similarity index 91% rename from outdated/sys/amiga/amidos.c rename to sys/amiga/amidos.c index 6ec41b5de..01d33343f 100644 --- a/outdated/sys/amiga/amidos.c +++ b/sys/amiga/amidos.c @@ -9,6 +9,7 @@ #include "hack.h" #include "winami.h" +#include "windefs.h" /* Defined in config.h, let's undefine it here (static function below) */ #undef strcmpi @@ -18,11 +19,17 @@ #include #undef COUNT + #if defined(__SASC_60) || defined(__GNUC__) #include #include #endif +/* POSIX stubs needed by libnix (-noixemul) */ +#ifdef __noixemul__ +int getpid(void) { return (int)FindTask(NULL); } +#endif + #ifdef AZTEC_50 #include #undef strcmpi @@ -34,11 +41,12 @@ #include "NH:sys/amiga/winami.p" #include "NH:sys/amiga/amidos.p" #else -#include "winami.p" +#include "amiwind.p" #include "winami.p" #include "amidos.p" #endif + extern char Initialized; extern struct window_procs amii_procs; struct ami_sysflags sysflags = {0}; @@ -54,13 +62,14 @@ char PATH[PATHLEN] = "NetHack:"; static boolean record_exists(void); void -flushout() +flushout(void) { (void) fflush(stdout); } #ifndef getuid -getuid() +int +getuid(void) { return 1; } @@ -68,26 +77,19 @@ getuid() #ifndef getlogin char * -getlogin() +getlogin(void) { return ((char *) NULL); } #endif -#ifndef AZTEC_50 -int -abs(x) -int x; -{ - return x < 0 ? -x : x; -} -#endif +/* abs() provided by libnix/stdlib */ #ifdef SHELL int -dosh() +dosh(void) { - int i; + int i = 0; char buf[BUFSZ]; extern struct ExecBase *SysBase; @@ -121,8 +123,7 @@ dosh() */ /* TODO: update this for FFS */ long -freediskspace(path) -char *path; +freediskspace(char *path) { #ifdef UNTESTED /* these changes from Patric Mueller for AROS to @@ -131,9 +132,9 @@ char *path; */ unsigned long long freeBytes = 0; #else - register long freeBytes = 0; + long freeBytes = 0; #endif - register struct InfoData *infoData; /* Remember... longword aligned */ + struct InfoData *infoData; /* Remember... longword aligned */ char fileName[32]; /* @@ -145,7 +146,7 @@ char *path; * so must be on the current device, so "" is enough... */ { - register char *colon; + char *colon; strncpy(fileName, path, sizeof(fileName) - 1); fileName[31] = 0; @@ -191,12 +192,11 @@ char *path; } long -filesize(file) -char *file; +filesize(char *file) { - register BPTR fileLock; - register struct FileInfoBlock *fileInfoBlock; - register long size = 0; + BPTR fileLock; + struct FileInfoBlock *fileInfoBlock; + long size = 0; fileInfoBlock = (struct FileInfoBlock *) alloc(sizeof(struct FileInfoBlock)); @@ -212,8 +212,8 @@ char *file; #if 0 void -eraseall(path, files) -const char *path, *files; +void +eraseall(const char *path, const char *files) { BPTR dirLock, dirLock2; struct FileInfoBlock *fibp; @@ -248,12 +248,11 @@ const char *path, *files; #if 0 /* Unused */ #define COPYSIZE 4096 -char *CopyFile(from, to) -const char *from, *to; +char *CopyFile(const char *from, const char *to) { - register BPTR fromFile, toFile; - register char *buffer; - register long size; + BPTR fromFile, toFile; + char *buffer; + long size; char *error = NULL; buffer = (char *) alloc(COPYSIZE); @@ -282,7 +281,8 @@ const char *from, *to; #ifdef MFLOPPY /* this should be replaced */ -saveDiskPrompt(start) +int +saveDiskPrompt(int start) { char buf[BUFSIZ], *bp; BPTR fileLock; @@ -338,7 +338,7 @@ saveDiskPrompt(start) /* Return 1 if the record file was found */ static boolean -record_exists() +record_exists(void) { FILE *file; @@ -355,7 +355,7 @@ record_exists() * For Amiga: do nothing, but called from restore.c */ void -gameDiskPrompt() +gameDiskPrompt(void) { } #endif @@ -365,8 +365,7 @@ gameDiskPrompt() * be room for the /. */ void -append_slash(name) -char *name; +append_slash(char *name) { char *ptr; @@ -381,8 +380,7 @@ char *name; } void -getreturn(str) -const char *str; +getreturn(const char *str) { int ch; @@ -396,12 +394,11 @@ const char *str; #define PATHSEP ';' FILE * -fopenp(name, mode) -register const char *name, *mode; +fopenp(const char *name, const char *mode) { - register char *bp, *pp, lastch; - register FILE *fp; - register BPTR theLock; + char *bp, *pp, lastch = 0; + FILE *fp; + BPTR theLock; char buf[BUFSIZ]; /* Try the default directory first. Then look along PATH. @@ -452,7 +449,12 @@ register const char *name, *mode; static BPTR OrgDirLock = NO_LOCK; -chdir(dir) char *dir; +int +chdir( +#ifdef CROSS_TO_AMIGA + const +#endif + char *dir) { extern char orgdir[]; @@ -489,7 +491,7 @@ chdir(dir) char *dir; */ #undef exit void -nethack_exit(code) +nethack_exit(int code) { #ifdef CHDIR extern char orgdir[]; @@ -506,10 +508,10 @@ nethack_exit(code) exit(code); } -void regularize(s) /* normalize file name - we don't like :'s or /'s */ -register char *s; +void +regularize(char *s) /* normalize file name - we don't like :'s or /'s */ { - register char *lp; + char *lp; while ((lp = strchr(s, ':')) || (lp = strchr(s, '/'))) *lp = '_'; diff --git a/outdated/sys/amiga/amidos.p b/sys/amiga/amidos.p similarity index 91% rename from outdated/sys/amiga/amidos.p rename to sys/amiga/amidos.p index fc479f9d1..ed669f563 100644 --- a/outdated/sys/amiga/amidos.p +++ b/sys/amiga/amidos.p @@ -31,10 +31,10 @@ void getreturn(const char *); #ifndef msmsg void msmsg( const char *, ... ); #endif -#if !defined(__SASC_60) && !defined(_DCC) +#if !defined(__SASC_60) && !defined(_DCC) && !defined(CROSS_TO_AMIGA) int chdir(char *); #endif -#ifndef strcmpi +#ifndef strcmpi int strcmpi(char * , char *); #endif #if !defined(memcmp) && !defined(AZTEC_C) && !defined(_DCC) && !defined(__GNUC__) diff --git a/outdated/sys/amiga/amifont.uu b/sys/amiga/amifont.uu similarity index 100% rename from outdated/sys/amiga/amifont.uu rename to sys/amiga/amifont.uu diff --git a/outdated/sys/amiga/amifont8.uu b/sys/amiga/amifont8.uu similarity index 100% rename from outdated/sys/amiga/amifont8.uu rename to sys/amiga/amifont8.uu diff --git a/outdated/sys/amiga/amigst.c b/sys/amiga/amigst.c similarity index 100% rename from outdated/sys/amiga/amigst.c rename to sys/amiga/amigst.c diff --git a/outdated/sys/amiga/amii.hlp b/sys/amiga/amii.hlp similarity index 100% rename from outdated/sys/amiga/amii.hlp rename to sys/amiga/amii.hlp diff --git a/outdated/sys/amiga/amimenu.c b/sys/amiga/amimenu.c similarity index 100% rename from outdated/sys/amiga/amimenu.c rename to sys/amiga/amimenu.c diff --git a/outdated/sys/amiga/amirip.c b/sys/amiga/amirip.c similarity index 93% rename from outdated/sys/amiga/amirip.c rename to sys/amiga/amirip.c index b14689f80..5f4aa52af 100644 --- a/outdated/sys/amiga/amirip.c +++ b/sys/amiga/amirip.c @@ -43,15 +43,15 @@ static struct RastPort *rp; #include #endif -static char *load_list[] = { "tomb.iff", 0 }; static BitMapHeader tomb_bmhd; -static struct BitMap *tbmp[1] = { 0 }; +static struct BitMap *tombimg = NULL; -static int cols[2] = { 154, 319 }; /* X location of center of columns */ +static const int cols_base[2] = { 154, 319 }; /* X location of center of columns */ +static int cols[2]; /* cols_base[] + xoff, computed per call */ static int cno = 0; /* current column */ #define TEXT_TOP (65 + yoff) -static xoff, yoff; /* image centering */ +static int xoff, yoff; /* image centering */ /* terrible kludge */ /* this is why prototypes should have ONLY types in them! */ @@ -100,19 +100,16 @@ int wh; /* was local in outrip, but needed for SCALE macro */ int cmap_white, cmap_black; void -amii_outrip(tmpwin, how, when) -winid tmpwin; -int how; -time_t when; +amii_outrip(winid tmpwin, int how, time_t when) { int just_return = 0; int done, rtxth; + struct IntuiMessage *imsg; int i; - register char *dpx; + char *dpx; char buf[200]; int line, tw, ww; - char *errstr = NULL; long year; if (!WINVERS_AMIV || HackScreen->RastPort.BitMap->Depth < 4) @@ -141,9 +138,7 @@ time_t when; SetFont(rp, HackFont); #endif - tomb_bmhd = ReadImageFiles(load_list, tbmp, &errstr); - if (errstr) - goto cleanup; + tomb_bmhd = ReadImageFile("tomb.iff", &tombimg); if (tomb_bmhd.w > ww || tomb_bmhd.h > wh) goto cleanup; @@ -151,12 +146,12 @@ time_t when; xoff = GENOFF(ww, tomb_bmhd.w); yoff = GENOFF(wh, tomb_bmhd.h); for (i = 0; i < SIZE(cols); i++) - cols[i] += xoff; + cols[i] = cols_base[i] + xoff; cmap_white = search_cmap(0, 0, 0); cmap_black = search_cmap(15, 15, 15); - BltBitMap(*tbmp, 0, 0, rp->BitMap, xoff, yoff, tomb_bmhd.w, tomb_bmhd.h, + BltBitMap(tombimg, 0, 0, rp->BitMap, xoff, yoff, tomb_bmhd.w, tomb_bmhd.h, 0xc0, 0xff, NULL); /* Put together death description */ @@ -199,7 +194,7 @@ time_t when; /* Put death type on stone */ for (line = DEATH_LINE, dpx = buf; line < YEAR_LINE; line++) { - register int i, i0; + int i, i0; char tmpchar; if ((i0 = strlen(dpx)) > STONE_LINE_LEN) { @@ -280,8 +275,7 @@ cleanup: } LoadRGB4(&HackScreen->ViewPort, sysflags.amii_curmap, amii_numcolors); - if (tbmp[0]) - FreeImageFiles(load_list, tbmp); + FreeImageFile(&tombimg); if (just_return) return; /* fall back to the straight-ASCII version */ @@ -289,8 +283,7 @@ cleanup: } static void -tomb_text(p) -char *p; +tomb_text(char *p) { char buf[STONE_LINE_LEN * 2]; int l; diff --git a/outdated/sys/amiga/amistack.c b/sys/amiga/amistack.c similarity index 55% rename from outdated/sys/amiga/amistack.c rename to sys/amiga/amistack.c index 33769697f..e0ea10a7b 100644 --- a/outdated/sys/amiga/amistack.c +++ b/sys/amiga/amistack.c @@ -1,5 +1,5 @@ /* NetHack 3.6 amistack.c $NHDT-Date: 1432512795 2015/05/25 00:13:15 $ $NHDT-Branch: master $:$NHDT-Revision: 1.8 $ */ -/* Copyright (c) Janne Salmijärvi, Tampere, Finland, 2000 */ +/* Copyright (c) Janne Salmij�rvi, Tampere, Finland, 2000 */ /* NetHack may be freely redistributed. See license for details. */ /* @@ -12,10 +12,16 @@ #ifdef __SASC_60 #include +#endif /* - * At the moment 90*1024 would suffice, but just to be on the safe side ... + * Increase stack size to allow deep recursions. + * NetHack 3.7 with Lua needs significantly more stack than 3.6. */ -long __stack = 128 * 1024; +#ifdef __SASC_60 +long __stack = 256 * 1024; +#else +/* For GCC with -noixemul (libnix), __stack is also recognized */ +unsigned long __stack = 256 * 1024; #endif diff --git a/outdated/sys/amiga/amitty.c b/sys/amiga/amitty.c similarity index 97% rename from outdated/sys/amiga/amitty.c rename to sys/amiga/amitty.c index e3ff882ef..22036c8f5 100644 --- a/outdated/sys/amiga/amitty.c +++ b/sys/amiga/amitty.c @@ -23,12 +23,12 @@ void tty_change_color(void); char *tty_get_color_string(void); -#ifdef TTY_GRAPHICS - int amibbs = 0; /* BBS mode */ char bbs_id[80] = ""; /* BBS uid equivalent */ long afh_in, afh_out; /* BBS mode Amiga filehandles */ +#ifdef TTY_GRAPHICS + void settty(const char *s) { @@ -58,9 +58,10 @@ setftty() } char kill_char = 'X' - '@'; char erase_char = '\b'; -tgetch() +int +tgetch(void) { - char x; + unsigned char x; Read(afh_in, &x, 1); return (x == '\r') ? '\n' : x; } diff --git a/outdated/sys/amiga/amiwind.c b/sys/amiga/amiwind.c similarity index 90% rename from outdated/sys/amiga/amiwind.c rename to sys/amiga/amiwind.c index 6a64667eb..d931697b7 100644 --- a/outdated/sys/amiga/amiwind.c +++ b/sys/amiga/amiwind.c @@ -23,18 +23,20 @@ static struct Message *GetFMsg(struct MsgPort *); #endif static int BufferGetchar(void); -static void ProcessMessage(register struct IntuiMessage *message); +static void ProcessMessage(struct IntuiMessage *message); #define BufferQueueChar(ch) (KbdBuffer[KbdBuffered++] = (ch)) -#ifndef CROSS_TO_AMIGA -struct Library *ConsoleDevice; -#else -struct Device * -# ifdef __CONSTLIBBASEDECL__ - __CONSTLIBBASEDECL__ -# endif /* __CONSTLIBBASEDECL__ */ - ConsoleDevice; -#endif + +struct Device *ConsoleDevice = NULL; + +/* Library bases - opened by amii_init_nhwindows, closed by amii_cleanup. + DOSBase is provided by newlib's startup code. + The rest must be defined here. */ +struct IntuitionBase *IntuitionBase = NULL; +struct GfxBase *GfxBase = NULL; +struct Library *GadToolsBase = NULL; +struct Library *LayersBase = NULL; +struct Library *AslBase = NULL; #ifndef CROSS_TO_AMIGA #include "NH:sys/amiga/amimenu.c" @@ -44,7 +46,6 @@ struct Device * /* Now our own variables */ -struct IntuitionBase *IntuitionBase; struct Screen *HackScreen; struct Window *pr_WindowPtr; struct MsgPort *HackPort; @@ -55,7 +56,6 @@ char Initialized = 0; WEVENT lastevent; #ifdef HACKFONT -struct GfxBase *GfxBase; struct Library *DiskfontBase; #endif @@ -100,11 +100,10 @@ static enum { NoAction, CloseOver } delayed_key_action = NoAction; */ struct Window * -OpenShWindow(nw) -struct NewWindow *nw; +OpenShWindow(struct NewWindow *nw) { - register struct Window *win; - register ULONG idcmpflags; + struct Window *win; + ULONG idcmpflags; if (!HackPort) /* Sanity check */ return (struct Window *) 0; @@ -126,12 +125,10 @@ struct NewWindow *nw; * Close a window that shared the HackPort IDCMP port. */ -void CloseShWindow(struct Window *); void -CloseShWindow(win) -struct Window *win; +CloseShWindow(struct Window *win) { - register struct IntuiMessage *msg; + struct IntuiMessage *msg; if (!HackPort) panic("HackPort NULL in CloseShWindow"); @@ -152,9 +149,9 @@ struct Window *win; } static int -BufferGetchar() +BufferGetchar(void) { - register int c; + int c; if (KbdBuffered > 0) { c = KbdBuffer[0]; @@ -180,8 +177,7 @@ BufferGetchar() */ int -ConvertKey(message) -register struct IntuiMessage *message; +ConvertKey(struct IntuiMessage *message) { static struct InputEvent theEvent; static char numpad[] = "bjnh.lyku"; @@ -190,8 +186,8 @@ register struct IntuiMessage *message; unsigned char buffer[10]; struct Window *w = message->IDCMPWindow; - register int length; - register ULONG qualifier; + int length; + ULONG qualifier; char numeric_pad, shift, control, alt; if (amii_wins[WIN_MAP]) @@ -355,10 +351,7 @@ register struct IntuiMessage *message; return (-1); } } - printf("Unrecognized key: %d ", (int) buffer[0]); - for (i = 1; i < length; ++i) - printf("%d ", (int) buffer[i]); - printf("\n"); + /* unrecognized key — silently ignore */ } return (-1); } @@ -373,8 +366,7 @@ register struct IntuiMessage *message; */ static void -ProcessMessage(message) -register struct IntuiMessage *message; +ProcessMessage(struct IntuiMessage *message) { int c; int cnt; @@ -385,15 +377,7 @@ register struct IntuiMessage *message; switch (message->Class) { case ACTIVEWINDOW: - if (alwaysinvent && WIN_INVEN != WIN_ERR - && w == amii_wins[WIN_INVEN]->win) { - cnt = DoMenuScroll(WIN_INVEN, 0, PICK_NONE, &mip); - } else if (scrollmsg && WIN_MESSAGE != WIN_ERR - && w == amii_wins[WIN_MESSAGE]->win) { - cnt = DoMenuScroll(WIN_MESSAGE, 0, PICK_NONE, &mip); - } else { - skip_mouse = 1; - } + skip_mouse = 1; break; case MOUSEBUTTONS: { @@ -429,12 +413,8 @@ register struct IntuiMessage *message; } } break; - case REFRESHWINDOW: { - if (scrollmsg && amii_wins[WIN_MESSAGE] - && w == amii_wins[WIN_MESSAGE]->win) { - cnt = DoMenuScroll(WIN_MESSAGE, 0, PICK_NONE, &mip); - } - } break; + case REFRESHWINDOW: + break; case CLOSEWINDOW: if (WIN_INVEN != WIN_ERR && w == amii_wins[WIN_INVEN]->win) { @@ -458,11 +438,6 @@ register struct IntuiMessage *message; break; case GADGETDOWN: - if (WIN_MESSAGE != WIN_ERR && w == amii_wins[WIN_MESSAGE]->win) { - cnt = DoMenuScroll(WIN_MESSAGE, 0, PICK_NONE, &mip); - } else if (WIN_INVEN != WIN_ERR && w == amii_wins[WIN_INVEN]->win) { - cnt = DoMenuScroll(WIN_INVEN, 0, PICK_NONE, &mip); - } break; case NEWSIZE: @@ -481,7 +456,17 @@ register struct IntuiMessage *message; ReDisplayData(WIN_INVEN); } else if (WINVERS_AMIV && (WIN_OVER != WIN_ERR && w == amii_wins[WIN_OVER]->win)) { - BufferQueueChar('R' - 64); + { + int i, have_redraw = 0; + for (i = 0; i < KbdBuffered; i++) { + if (KbdBuffer[i] == 'R' - 64) { + have_redraw = 1; + break; + } + } + if (!have_redraw) + BufferQueueChar('R' - 64); + } } else if (WIN_MAP != WIN_ERR && w == amii_wins[WIN_MAP]->win) { #ifdef CLIPPING CO = (w->Width - w->BorderLeft - w->BorderRight) / mxsize; @@ -494,7 +479,17 @@ register struct IntuiMessage *message; clipping = FALSE; clipx = clipy = 0; } - BufferQueueChar('R' - 64); + { + int i, have_redraw = 0; + for (i = 0; i < KbdBuffered; i++) { + if (KbdBuffer[i] == 'R' - 64) { + have_redraw = 1; + break; + } + } + if (!have_redraw) + BufferQueueChar('R' - 64); + } #endif } break; @@ -506,8 +501,9 @@ register struct IntuiMessage *message; amii_destroy_nhwindow(WIN_OVER); WIN_OVER = WIN_ERR; delayed_key_action = NoAction; + break; case NoAction: - ; /* null */ + break; } } @@ -522,13 +518,13 @@ register struct IntuiMessage *message; #if defined(TTY_GRAPHICS) && !defined(AMII_GRAPHICS) int -kbhit() +kbhit(void) { return 0; } #else int -kbhit() +kbhit(void) { int c; #ifdef TTY_GRAPHICS @@ -547,9 +543,9 @@ kbhit() #ifdef AMII_GRAPHICS int -amikbhit() +amikbhit(void) { - register struct IntuiMessage *message; + struct IntuiMessage *message; while (KbdBuffered < KBDBUFFER / 2) { #ifdef AMIFLUSH message = (struct IntuiMessage *) GetFMsg(HackPort); @@ -572,7 +568,7 @@ amikbhit() */ int -WindowGetchar() +WindowGetchar(void) { while ((lastevent.type = WEUNK), amikbhit() <= 0) { WaitPort(HackPort); @@ -581,7 +577,7 @@ WindowGetchar() } WETYPE -WindowGetevent() +WindowGetevent(void) { lastevent.type = WEUNK; while (amikbhit() == 0) { @@ -601,9 +597,9 @@ WindowGetevent() */ void -amii_cleanup() +amii_cleanup(void) { - register struct IntuiMessage *msg; + struct IntuiMessage *msg; /* Close things up */ if (HackPort) { @@ -713,8 +709,7 @@ amii_cleanup() #ifndef SHAREDLIB void -Abort(rc) -long rc; +Abort(long rc) { int fault = 1; #ifdef CHDIR @@ -765,7 +760,7 @@ long rc; } void -CleanUp() +CleanUp(void) { amii_cleanup(); } @@ -776,8 +771,7 @@ CleanUp() #ifdef AMIFLUSH /* This routine adapted from AmigaMail IV-37 by Michael Sinz */ static struct Message * -GetFMsg(port) -struct MsgPort *port; +GetFMsg(struct MsgPort *port) { struct IntuiMessage *msg, *succ, *succ1; @@ -803,8 +797,7 @@ struct MsgPort *port; #endif struct NewWindow * -DupNewWindow(win) -struct NewWindow *win; +DupNewWindow(struct NewWindow *win) { struct NewWindow *nwin; struct Gadget *ngd, *gd, *pgd = NULL; @@ -845,11 +838,10 @@ struct NewWindow *win; } void -FreeNewWindow(win) -struct NewWindow *win; +FreeNewWindow(struct NewWindow *win) { - register struct Gadget *gd, *pgd; - register struct StringInfo *sip; + struct Gadget *gd, *pgd; + struct StringInfo *sip; for (gd = win->FirstGadget; gd; gd = pgd) { pgd = gd->NextGadget; @@ -868,7 +860,7 @@ struct NewWindow *win; } void -bell() +bell(void) { if (flags.silent) return; @@ -876,15 +868,14 @@ bell() } void -amii_delay_output() +amii_delay_output(void) { /* delay 50 ms */ Delay(2L); } void -amii_number_pad(state) -int state; +amii_number_pad(int state) { } #endif /* AMII_GRAPHICS */ @@ -900,6 +891,10 @@ amii_loadlib(void) { } +#ifdef CROSS_TO_AMIGA +extern void Abort(long) NORETURN; +#endif + /* fatal error */ /*VARARGS1*/ void error diff --git a/outdated/sys/amiga/amiwind.p b/sys/amiga/amiwind.p similarity index 100% rename from outdated/sys/amiga/amiwind.p rename to sys/amiga/amiwind.p diff --git a/sys/amiga/bmp2iff_host.c b/sys/amiga/bmp2iff_host.c new file mode 100644 index 000000000..154335297 --- /dev/null +++ b/sys/amiga/bmp2iff_host.c @@ -0,0 +1,480 @@ +/* + * bmp2iff_host.c - Convert a BMP to Amiga BMAP IFF. + * Copyright (c) 2026 by Ingo Paschke. + * NetHack may be freely redistributed. See license for details. + * + * IFF BMAP format matches sys/amiga/xpm2iff.c by Gregg Wonderly. + * + * Usage: bmp2iff_host -planes N input.bmp output.iff + * + * This is a HOST tool -- runs on the build machine. + */ + +#include +#include +#include +#include + +#define TILE_X 16 +#define TILE_Y 16 + +#pragma pack(push,1) +typedef struct { + uint16_t bfType; + uint32_t bfSize; + uint16_t bfReserved1, bfReserved2; + uint32_t bfOffBits; +} BMPFILEHEADER; + +typedef struct { + uint32_t biSize; + int32_t biWidth, biHeight; + uint16_t biPlanes, biBitCount; + uint32_t biCompression; + uint32_t biSizeImage; + int32_t biXPelsPerMeter, biYPelsPerMeter; + uint32_t biClrUsed, biClrImportant; +} BMPINFOHEADER; +#pragma pack(pop) + +typedef struct { + uint8_t r, g, b; +} RGB; + +/* --------------------------------------------------------- */ +/* Fixed 16-color AMIV UI palette */ +/* Must match amiv_init_map[] in winami.c */ +/* --------------------------------------------------------- */ + +static const RGB amiv_pal[16] = { + {0x00,0x00,0x00}, /* 0 black */ + {0xFF,0xFF,0xFF}, /* 1 white */ + {0x00,0xBB,0xFF}, /* 2 cyan */ + {0xFF,0x66,0x00}, /* 3 orange */ + {0x00,0x00,0xFF}, /* 4 blue */ + {0x00,0x99,0x00}, /* 5 green */ + {0x66,0x99,0xBB}, /* 6 grey */ + {0xFF,0x00,0x00}, /* 7 red */ + {0x66,0xFF,0x00}, /* 8 ltgreen */ + {0xFF,0xFF,0x00}, /* 9 yellow */ + {0xFF,0x00,0xFF}, /* 10 magenta */ + {0x99,0x44,0x00}, /* 11 brown */ + {0x44,0x66,0x66}, /* 12 greyblue */ + {0xCC,0x44,0x00}, /* 13 ltbrown */ + {0xDD,0xDD,0xBB}, /* 14 ltgrey */ + {0xFF,0xBB,0x99}, /* 15 peach */ +}; + +/* --------------------------------------------------------- */ +/* Colour helpers */ +/* --------------------------------------------------------- */ + +static int +coldist(const RGB *a, const RGB *b) +{ + int dr = a->r - b->r; + int dg = a->g - b->g; + int db = a->b - b->b; + return dr*dr + dg*dg + db*db; +} + +static int +nearest(const RGB *c, const RGB *pal, int n) +{ + int best = 0, bestd = 0x7fffffff, i; + for (i = 0; i < n; i++) { + int d = coldist(c, &pal[i]); + if (d < bestd) { bestd = d; best = i; } + } + return best; +} + +/* --------------------------------------------------------- */ +/* IFF output */ +/* --------------------------------------------------------- */ + +static FILE *iff_fp; + +static void +wr32(uint32_t v) +{ + uint8_t b[4] = { v>>24, v>>16, v>>8, v }; + fwrite(b, 1, 4, iff_fp); +} + +static void +wr_chunk(const char *id, const void *d, uint32_t len) +{ + fwrite(id, 1, 4, iff_fp); + wr32(len); + fwrite(d, 1, len, iff_fp); + if (len & 1) fputc(0, iff_fp); +} + +static void +iff_write(int nplanes, int ncolors, uint8_t *cmap, + int w, int h, uint8_t **planes, + int ntiles, int across, int down) +{ + long spos; + uint32_t plsz = (uint32_t)(w / 8) * h; + int i; + + fwrite("FORM", 1, 4, iff_fp); + spos = ftell(iff_fp); + wr32(0); + fwrite("BMAP", 1, 4, iff_fp); + + /* BMHD */ + { + uint8_t bm[20]; + memset(bm, 0, 20); + bm[0] = w >> 8; bm[1] = w; + bm[2] = h >> 8; bm[3] = h; + bm[8] = (uint8_t)nplanes; + bm[14] = 100; bm[15] = 100; + wr_chunk("BMHD", bm, 20); + } + + /* CAMG */ + { + uint8_t c[4] = {0,0,0x80,0x04}; + wr_chunk("CAMG", c, 4); + } + + /* CMAP */ + wr_chunk("CMAP", cmap, (uint32_t)ncolors * 3); + + /* PDAT */ + { + uint8_t pd[28], *p = pd; + uint32_t v[7]; + v[0] = nplanes; + v[1] = plsz; + v[2] = across; + v[3] = down; + v[4] = ntiles; + v[5] = TILE_X; + v[6] = TILE_Y; + for (i = 0; i < 7; i++) { + p[0] = (v[i]>>24); p[1] = (v[i]>>16); + p[2] = (v[i]>> 8); p[3] = v[i]; + p += 4; + } + wr_chunk("PDAT", pd, 28); + } + + /* PLNE */ + fwrite("PLNE", 1, 4, iff_fp); + wr32(plsz * nplanes); + for (i = 0; i < nplanes; i++) + fwrite(planes[i], 1, plsz, iff_fp); + + /* fix FORM size */ + { + long end = ftell(iff_fp); + uint32_t sz = (uint32_t)(end - spos - 4); + fseek(iff_fp, spos, SEEK_SET); + wr32(sz); + fseek(iff_fp, 0, SEEK_END); + } +} + +/* --------------------------------------------------------- */ +/* Pixel-to-bitplane conversion */ +/* --------------------------------------------------------- */ + +static void +to_planes(uint8_t *pix, int w, int h, + int np, uint8_t **pl) +{ + int rb = w / 8; + int x, y, p; + + for (p = 0; p < np; p++) + memset(pl[p], 0, rb * h); + + for (y = 0; y < h; y++) + for (x = 0; x < w; x++) { + uint8_t v = pix[y * w + x]; + int off = y * rb + x / 8; + int bit = 7 - (x & 7); + for (p = 0; p < np; p++) + if (v & (1 << p)) + pl[p][off] |= (1 << bit); + } +} + +/* --------------------------------------------------------- */ +/* Palette building */ +/* --------------------------------------------------------- */ + +/* + * Build an output palette of 'maxcol' entries: + * - slots 0-15: fixed AMIV UI colors + * - slots 16+: tile colors from the BMP + * + * Returns remap[0..nsrc-1] mapping BMP palette index + * to output palette index. + */ +static void +build_palette(const RGB *src, int nsrc, + const uint8_t *pix, int npix, + int maxcol, + RGB *out, int *remap) +{ + int freq[256] = {0}; + int order[256]; + int i, j, nfree, nuniq; + + /* count pixel frequency per BMP palette entry */ + for (i = 0; i < npix; i++) + freq[pix[i]]++; + + /* sort BMP colors by frequency (descending) */ + for (i = 0; i < nsrc; i++) order[i] = i; + for (i = 1; i < nsrc; i++) { + int k = order[i], kf = freq[k]; + j = i - 1; + while (j >= 0 && freq[order[j]] < kf) { + order[j+1] = order[j]; + j--; + } + order[j+1] = k; + } + + /* count unique colors actually used */ + nuniq = 0; + for (i = 0; i < nsrc; i++) + if (freq[i] > 0) nuniq++; + + /* first 16 slots are AMIV UI */ + for (i = 0; i < 16 && i < maxcol; i++) + out[i] = amiv_pal[i]; + for (i = 16; i < maxcol; i++) + memset(&out[i], 0, sizeof(RGB)); + + nfree = maxcol - 16; + if (nfree < 0) nfree = 0; + + /* + * Case 1: enough free slots for all unique colors. + * Assign each unique BMP color its own pen, exact + * AMIV matches share the UI pen. + */ + if (nuniq <= nfree) { + int next = 16; + for (i = 0; i < nsrc; i++) { + if (freq[i] == 0) { + remap[i] = 0; + continue; + } + /* exact match to an AMIV pen? */ + int best = nearest(&src[i], amiv_pal, 16); + if (coldist(&src[i], &amiv_pal[best]) == 0) { + remap[i] = best; + } else { + remap[i] = next; + out[next] = src[i]; + next++; + } + } + return; + } + + /* + * Case 2: more unique colors than free slots. + * - Direct/near AMIV matches use UI pens. + * - Remaining slots filled by most-frequent colors. + * - Leftovers mapped to nearest in final palette. + */ + { + int next = 16; + int assigned[256]; + memset(assigned, 0, sizeof(assigned)); + + /* pass 1: exact/near AMIV matches */ + for (i = 0; i < nsrc; i++) { + if (freq[i] == 0) { + remap[i] = 0; + assigned[i] = 1; + continue; + } + int best = nearest(&src[i], amiv_pal, 16); + int d = coldist(&src[i], &amiv_pal[best]); + if (d < 200) { /* near match threshold */ + remap[i] = best; + assigned[i] = 1; + } + } + + /* pass 2: fill free slots with most-frequent + * unassigned colors (order[] is freq-sorted) */ + for (i = 0; i < nsrc && next < maxcol; i++) { + int idx = order[i]; + if (assigned[idx] || freq[idx] == 0) + continue; + remap[idx] = next; + out[next] = src[idx]; + assigned[idx] = 1; + next++; + } + + /* pass 3: map remaining to nearest in palette */ + for (i = 0; i < nsrc; i++) { + if (!assigned[i]) + remap[i] = nearest(&src[i], out, next); + } + } +} + +/* --------------------------------------------------------- */ +/* Main */ +/* --------------------------------------------------------- */ + +int +main(int argc, char **argv) +{ + FILE *bmpfp; + BMPFILEHEADER fhdr; + BMPINFOHEADER ihdr; + RGB palette[256]; + int ncolors, img_w, img_h, rowstride; + uint8_t *bmpdata, *pixels; + int ntiles, across, down; + int nplanes, maxcol; + int i, y; + RGB outpal[256]; + int remap[256]; + uint8_t *remapped; + uint8_t *plane_data[8]; + uint8_t cmap_rgb[256 * 3]; + int planesize; + + /* parse args */ + if (argc != 5 + || strcmp(argv[1], "-planes") != 0) { + fprintf(stderr, + "Usage: %s -planes N input.bmp output.iff\n", + argv[0]); + return 1; + } + nplanes = atoi(argv[2]); + if (nplanes < 1 || nplanes > 8) { + fprintf(stderr, "planes must be 1-8\n"); + return 1; + } + maxcol = 1 << nplanes; + + /* read BMP */ + bmpfp = fopen(argv[3], "rb"); + if (!bmpfp) { perror(argv[3]); return 1; } + + if (fread(&fhdr, sizeof(fhdr), 1, bmpfp) != 1 + || fread(&ihdr, sizeof(ihdr), 1, bmpfp) != 1) { + fprintf(stderr, "Failed to read BMP header\n"); + return 1; + } + if (fhdr.bfType != 0x4D42) { + fprintf(stderr, "Not a BMP file\n"); + return 1; + } + if (ihdr.biBitCount != 8) { + fprintf(stderr, + "Expected 8-bit BMP, got %d-bit\n", + ihdr.biBitCount); + return 1; + } + + img_w = ihdr.biWidth; + img_h = abs(ihdr.biHeight); + ncolors = ihdr.biClrUsed ? ihdr.biClrUsed : 256; + if (ncolors > 256) ncolors = 256; + + /* read palette (BMP stores BGRx) */ + { + uint8_t raw[256][4]; + if (fread(raw, 4, ncolors, bmpfp) + != (size_t)ncolors) { + fprintf(stderr, "Failed to read palette\n"); + return 1; + } + for (i = 0; i < ncolors; i++) { + palette[i].r = raw[i][2]; + palette[i].g = raw[i][1]; + palette[i].b = raw[i][0]; + } + } + + /* read pixel data */ + rowstride = (img_w + 3) & ~3; + bmpdata = malloc(rowstride * img_h); + fseek(bmpfp, fhdr.bfOffBits, SEEK_SET); + if (fread(bmpdata, 1, rowstride * img_h, bmpfp) + != (size_t)(rowstride * img_h)) { + fprintf(stderr, "Failed to read pixel data\n"); + return 1; + } + fclose(bmpfp); + + /* flip bottom-up to top-down */ + pixels = malloc(img_w * img_h); + if (ihdr.biHeight > 0) { + for (y = 0; y < img_h; y++) + memcpy(pixels + y * img_w, + bmpdata + (img_h-1-y) * rowstride, + img_w); + } else { + for (y = 0; y < img_h; y++) + memcpy(pixels + y * img_w, + bmpdata + y * rowstride, img_w); + } + free(bmpdata); + + across = img_w / TILE_X; + down = img_h / TILE_Y; + ntiles = across * down; + + /* build palette and remap pixels */ + build_palette(palette, ncolors, + pixels, img_w * img_h, + maxcol, outpal, remap); + + remapped = malloc(img_w * img_h); + for (i = 0; i < img_w * img_h; i++) + remapped[i] = (uint8_t)remap[pixels[i]]; + + /* convert to bitplanes */ + planesize = (img_w / 8) * img_h; + for (i = 0; i < nplanes; i++) + plane_data[i] = calloc(1, planesize); + + to_planes(remapped, img_w, img_h, + nplanes, plane_data); + + /* build CMAP */ + for (i = 0; i < maxcol; i++) { + cmap_rgb[i*3+0] = outpal[i].r; + cmap_rgb[i*3+1] = outpal[i].g; + cmap_rgb[i*3+2] = outpal[i].b; + } + + /* write IFF */ + iff_fp = fopen(argv[4], "wb"); + if (!iff_fp) { perror(argv[4]); return 1; } + + iff_write(nplanes, maxcol, cmap_rgb, + img_w, img_h, plane_data, + ntiles, across, down); + fclose(iff_fp); + + printf("%s: %dx%d, %d colors (%d planes), " + "%d tiles\n", + argv[4], img_w, img_h, + maxcol, nplanes, ntiles); + + for (i = 0; i < nplanes; i++) free(plane_data[i]); + free(remapped); + free(pixels); + return 0; +} diff --git a/outdated/sys/amiga/clipwin.c b/sys/amiga/clipwin.c similarity index 100% rename from outdated/sys/amiga/clipwin.c rename to sys/amiga/clipwin.c diff --git a/outdated/sys/amiga/colorwin.c b/sys/amiga/colorwin.c similarity index 100% rename from outdated/sys/amiga/colorwin.c rename to sys/amiga/colorwin.c diff --git a/outdated/sys/amiga/grave16.xpm b/sys/amiga/grave16.xpm similarity index 100% rename from outdated/sys/amiga/grave16.xpm rename to sys/amiga/grave16.xpm diff --git a/sys/amiga/nethack.cnf b/sys/amiga/nethack.cnf new file mode 100644 index 000000000..dad37d4d5 --- /dev/null +++ b/sys/amiga/nethack.cnf @@ -0,0 +1,44 @@ +# NetHack 3.7 Amiga Configuration +# +# For a full list of options, see the opthelp file or press '?' +# then 'o' during gameplay. + +# *** DISPLAY MODE *** +# +# Tile mode (graphical tiles, recommended): +OPTIONS=windowtype:amiv +# +# Text mode (uses hack.font line-drawing characters): +#OPTIONS=windowtype:amii +#OPTIONS=symset:AmigaFont + +# *** GENERAL OPTIONS *** +OPTIONS=time,showexp,lit_corridor +OPTIONS=boulder:0 +OPTIONS=autopickup,pickup_types:$"=/!?+ +OPTIONS=catname:Kaori + +# *** MENU COLORS *** +# Colour-code inventory items by BUC status and value. +OPTIONS=menucolors +MENUCOLOR=" blessed "=green +MENUCOLOR=" holy "=green +MENUCOLOR=" uncursed "=cyan +MENUCOLOR=" cursed "=red +MENUCOLOR=" unholy "=red +MENUCOLOR=" cursed .* (being worn)"=red&underline +MENUCOLOR=" cursed .* (wielded)"=red&underline +MENUCOLOR="loadstone"=red&underline +MENUCOLOR="gold piece"=brown +MENUCOLOR="worthless"=brown +MENUCOLOR="wand of wishing"=magenta +MENUCOLOR="magic lamp"=magenta +MENUCOLOR="magic marker"=magenta +MENUCOLOR="bag of holding"=magenta +MENUCOLOR="amulet of life saving"=magenta +MENUCOLOR="cloak of magic resistance"=magenta +MENUCOLOR="silver dragon scale"=cyan +MENUCOLOR="gray dragon scale"=cyan +MENUCOLOR="speed boots"=magenta +MENUCOLOR="luckstone"=green +MENUCOLOR="unicorn horn"=green diff --git a/outdated/sys/amiga/winamenu.c b/sys/amiga/winamenu.c similarity index 94% rename from outdated/sys/amiga/winamenu.c rename to sys/amiga/winamenu.c index 013e3c792..d391bdec4 100644 --- a/outdated/sys/amiga/winamenu.c +++ b/sys/amiga/winamenu.c @@ -15,14 +15,12 @@ /* Start building the text for a menu */ void -amii_start_menu(window, mbehavior) -register winid window; -unsigned long mbehavior UNUSED; - +amii_start_menu(winid window, unsigned long mbehavior UNUSED) { - register int i; - register struct amii_WinDesc *cw; - register amii_menu_item *mip; + int i; + struct amii_WinDesc *cw; + amii_menu_item *mip; + if (window == WIN_ERR || (cw = amii_wins[window]) == NULL || cw->type != NHW_MENU) @@ -64,17 +62,12 @@ unsigned long mbehavior UNUSED; /* Add a string to a menu */ void -amii_add_menu(window, glyph, id, ch, gch, attr, str, itemflags) -register winid window; -register int glyph; -register const anything *id; -register char ch; -register char gch; -register int attr; -register const char *str; -register unsigned int itemflags; +amii_add_menu(winid window, const glyph_info *glyphinfo, const anything *id, + char ch, char gch, int attr, int clr, + const char *str, unsigned int itemflags) { - register struct amii_WinDesc *cw; + struct amii_WinDesc *cw; + boolean preselected = ((itemflags & MENU_ITEMFLAGS_SELECTED) != 0); amii_menu_item *mip; char buf[4 + BUFSZ]; @@ -90,7 +83,8 @@ register unsigned int itemflags; mip->identifier = *id; mip->selected = preselected; mip->attr = attr; - mip->glyph = Is_rogue_level(&u.uz) ? NO_GLYPH : glyph; + mip->color = clr; + mip->glyph = Is_rogue_level(&u.uz) ? NO_GLYPH : (glyphinfo ? glyphinfo->glyph : NO_GLYPH); mip->selector = 0; mip->gselector = gch; mip->count = -1; @@ -131,11 +125,10 @@ register unsigned int itemflags; /* Done building a menu. */ void -amii_end_menu(window, morestr) -register winid window; -register const char *morestr; +amii_end_menu(winid window, const char *morestr) { - register struct amii_WinDesc *cw; + struct amii_WinDesc *cw; + if (window == WIN_ERR || (cw = amii_wins[window]) == NULL || cw->type != NHW_MENU) @@ -151,8 +144,8 @@ register const char *morestr; mip = cw->menu.last; #endif any.a_void = 0; - amii_add_menu(window, NO_GLYPH, &any, 0, 0, ATR_NONE, morestr, - MENU_ITEMFLAGS_NONE); + amii_add_menu(window, (const glyph_info *) 0, &any, 0, 0, ATR_NONE, + NO_COLOR, morestr, MENU_ITEMFLAGS_NONE); #ifdef PROMPTFIRST /* Do some shuffling. Last first, push others one forward \ */ mip->next = NULL; @@ -178,13 +171,11 @@ register const char *morestr; /* Select something from the menu. */ int -amii_select_menu(window, how, mip) -register winid window; -register int how; -register menu_item **mip; +amii_select_menu(winid window, int how, menu_item **mip) { int cnt; - register struct amii_WinDesc *cw; + struct amii_WinDesc *cw; + if (window == WIN_ERR || (cw = amii_wins[window]) == NULL || cw->type != NHW_MENU) @@ -199,7 +190,7 @@ register menu_item **mip; } amii_menu_item * -find_menu_item(register struct amii_WinDesc *cw, int idx) +find_menu_item(struct amii_WinDesc *cw, int idx) { amii_menu_item *mip; for (mip = cw->menu.items; idx > 0 && mip; mip = mip->next) @@ -209,11 +200,11 @@ find_menu_item(register struct amii_WinDesc *cw, int idx) } int -make_menu_items(register struct amii_WinDesc *cw, register menu_item **rmip) +make_menu_items(struct amii_WinDesc *cw, menu_item **rmip) { - register int idx = 0; - register amii_menu_item *mip; - register menu_item *mmip; + int idx = 0; + amii_menu_item *mip; + menu_item *mmip; for (mip = cw->menu.items; mip; mip = mip->next) { if (mip->selected) @@ -236,19 +227,17 @@ make_menu_items(register struct amii_WinDesc *cw, register menu_item **rmip) } int -DoMenuScroll(win, blocking, how, retmip) -int win, blocking, how; -menu_item **retmip; +DoMenuScroll(int win, int blocking, int how, menu_item **retmip) { amii_menu_item *amip; - register struct Window *w; - register struct NewWindow *nw; + struct Window *w; + struct NewWindow *nw; struct PropInfo *pip; - register struct amii_WinDesc *cw; + struct amii_WinDesc *cw; struct IntuiMessage *imsg; struct Gadget *gd; - register int wheight, xsize, ysize, aredone = 0; - register int txwd, txh; + int wheight, xsize, ysize, aredone = 0; + int txwd, txh; long mics, secs, class, code; long oldmics = 0, oldsecs = 0; int aidx, oidx, topidx, hidden; @@ -819,7 +808,7 @@ menu_item **retmip; ++topidx; else break; - } else if (code = CTRL('U') + } else if (code == CTRL('U') || code == MENU_PREVIOUS_PAGE) { if (topidx > 0) --topidx; @@ -1007,11 +996,13 @@ menu_item **retmip; oldmics = mics; } else { amip = find_menu_item(cw, oidx); - amip->selected = 0; - amip->count = -1; - reset_counting = TRUE; - if (amip->canselect && amip->selector) - amip->str[SOFF + 2] = '-'; + if (amip) { + amip->selected = 0; + amip->count = -1; + reset_counting = TRUE; + if (amip->canselect && amip->selector) + amip->str[SOFF + 2] = '-'; + } } if (counting && amip->selected && amip->canselect && amip->selector) { @@ -1092,13 +1083,12 @@ menu_item **retmip; } void -ReDisplayData(win) -winid win; +ReDisplayData(winid win) { int totalvis; - register struct amii_WinDesc *cw; - register struct Window *w; - register struct Gadget *gd; + struct amii_WinDesc *cw; + struct Window *w; + struct Gadget *gd; unsigned long hidden, aidx, wheight; struct PropInfo *pip; @@ -1123,15 +1113,13 @@ winid win; } long -FindLine(win, line) -winid win; -int line; +FindLine(winid win, int line) { int txwd; - register char *t; - register struct amii_WinDesc *cw; - register struct Window *w; - register int i, disprow, len; + char *t; + struct amii_WinDesc *cw; + struct Window *w; + int i, disprow, len; int col = -1; if (win == WIN_ERR || !(cw = amii_wins[win]) || !(w = cw->win)) { @@ -1186,15 +1174,14 @@ int line; } long -CountLines(win) -winid win; +CountLines(winid win) { int txwd; amii_menu_item *mip; - register char *t; - register struct amii_WinDesc *cw; - register struct Window *w; - register int i, disprow, len; + char *t; + struct amii_WinDesc *cw; + struct Window *w; + int i, disprow, len; int col = -1; if (win == WIN_ERR || !(cw = amii_wins[win]) || !(w = cw->win)) { @@ -1250,17 +1237,15 @@ winid win; } void -DisplayData(win, start) -winid win; -int start; +DisplayData(winid win, int start) { int txwd; amii_menu_item *mip; - register char *t; - register struct amii_WinDesc *cw; - register struct Window *w; - register struct RastPort *rp; - register int i, disprow, len, wheight; + char *t; + struct amii_WinDesc *cw; + struct Window *w; + struct RastPort *rp; + int i, disprow, len, wheight; int whichcolor = -1; int col; @@ -1363,6 +1348,13 @@ int start; whichcolor = 2; } + /* Apply menucolor if set for this item */ + if (mip && mip->color != NO_COLOR && !(mip->selected)) { + extern const int foreg[]; + SetAPen(rp, foreg[mip->color]); + whichcolor = 0; /* force re-evaluation next item */ + } + /* Next line out, wrap if too long */ t = cw->data[i] + SOFF; @@ -1427,14 +1419,11 @@ int start; } void -SetPropInfo(win, gad, vis, total, top) -register struct Window *win; -register struct Gadget *gad; -register long vis, total, top; +SetPropInfo(struct Window *win, struct Gadget *gad, long vis, long total, long top) { long mflags; - register long hidden; - register int body, pot; + long hidden; + int body, pot; hidden = max(total - vis, 0); diff --git a/outdated/sys/amiga/winami.c b/sys/amiga/winami.c similarity index 94% rename from outdated/sys/amiga/winami.c rename to sys/amiga/winami.c index 4b7866346..7770a3547 100644 --- a/outdated/sys/amiga/winami.c +++ b/sys/amiga/winami.c @@ -39,7 +39,8 @@ long amii_scrnmode; * the intuition interface for the amiga... */ struct window_procs amii_procs = { - "amii", WC_COLOR | WC_HILITE_PET | WC_INVERSE, + WPID(amii), + WC_COLOR | WC_HILITE_PET | WC_INVERSE, 0L, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* color availability */ amii_init_nhwindows, @@ -48,7 +49,7 @@ struct window_procs amii_procs = { amii_create_nhwindow, amii_clear_nhwindow, amii_display_nhwindow, amii_destroy_nhwindow, amii_curs, amii_putstr, genl_putmixed, amii_display_file, amii_start_menu, amii_add_menu, amii_end_menu, - amii_select_menu, genl_message_menu, amii_update_inventory, + amii_select_menu, genl_message_menu, amii_mark_synch, amii_wait_synch, #ifdef CLIPPING amii_cliparound, @@ -67,13 +68,16 @@ struct window_procs amii_procs = { genl_status_init, genl_status_finish, genl_status_enablefield, genl_status_update, genl_can_suspend_yes, + amii_update_inventory, + amii_ctrl_nhwindow, }; /* The view window layout uses the same function names so we can use * a shared library to allow the executable to be smaller. */ struct window_procs amiv_procs = { - "amitile", WC_COLOR | WC_HILITE_PET | WC_INVERSE, + WPID(amiv), + WC_COLOR | WC_HILITE_PET | WC_INVERSE, 0L, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* color availability */ amii_init_nhwindows, @@ -82,7 +86,7 @@ struct window_procs amiv_procs = { amii_create_nhwindow, amii_clear_nhwindow, amii_display_nhwindow, amii_destroy_nhwindow, amii_curs, amii_putstr, genl_putmixed, amii_display_file, amii_start_menu, amii_add_menu, amii_end_menu, - amii_select_menu, genl_message_menu, amii_update_inventory, + amii_select_menu, genl_message_menu, amii_mark_synch, amii_wait_synch, #ifdef CLIPPING amii_cliparound, @@ -101,6 +105,8 @@ struct window_procs amiv_procs = { genl_status_init, genl_status_finish, genl_status_enablefield, genl_status_update, genl_can_suspend_yes, + amii_update_inventory, + amii_ctrl_nhwindow, }; unsigned short amii_initmap[AMII_MAXCOLORS]; @@ -428,7 +434,7 @@ struct NewScreen NewHackScreen = { 0, 0, WIDTH, SCREENHEIGHT, 3, 0, * init_sound_disp_gamewindows(). */ void -amii_askname() +amii_askname(void) { char plnametmp[300]; /* From winreq.c: sizeof(StrStringSIBuff) */ *plnametmp = 0; @@ -454,12 +460,12 @@ amii_askname() #if 0 /* New function at the bottom */ void -amii_player_selection() +amii_player_selection(void) { - register struct Window *cwin; - register struct IntuiMessage *imsg; - register int aredone = 0; - register struct Gadget *gd; + struct Window *cwin; + struct IntuiMessage *imsg; + int aredone = 0; + struct Gadget *gd; static int once = 0; long class, code; @@ -499,7 +505,7 @@ amii_player_selection() #ifdef INTUI_NEW_LOOK Type_NewWindowStructure1.Extension = wintags; Type_NewWindowStructure1.Flags |= WFLG_NW_EXTENDED; - fillhook.h_Entry = (ULONG(*)())LayerFillHook; + fillhook.h_Entry = (void *) &LayerFillHook; fillhook.h_Data = (void *)-2; fillhook.h_SubEntry = 0; #endif @@ -595,8 +601,7 @@ amii_player_selection() #include "NH:sys/amiga/randwin.c" void -RandomWindow( name ) - char *name; +RandomWindow(char *name) { struct MsgPort *tport; struct timerequest *trq; @@ -672,7 +677,7 @@ allocerr: #ifdef INTUI_NEW_LOOK Rnd_NewWindowStructure1.Extension = wintags; Rnd_NewWindowStructure1.Flags |= WFLG_NW_EXTENDED; - fillhook.h_Entry = (ULONG(*)())LayerFillHook; + fillhook.h_Entry = (void *) &LayerFillHook; fillhook.h_Data = (void *)-2; fillhook.h_SubEntry = 0; #endif @@ -762,10 +767,11 @@ amii_get_ext_cmd(void) #endif int colx; int bottom = 0; + struct Window *w; char obufp[100]; - register char *bufp = obufp; - register int c; + char *bufp = obufp; + int c; int com_index, oindex; int did_comp = 0; /* did successful completion? */ int sel = -1; @@ -787,7 +793,8 @@ amii_get_ext_cmd(void) id.a_char = *extcmdlist[i].ef_txt; sprintf(buf, "%-10s - %s ", extcmdlist[i].ef_txt, extcmdlist[i].ef_desc); - amii_add_menu(win, NO_GLYPH, &id, extcmdlist[i].ef_txt[0], 0, 0, + amii_add_menu(win, (const glyph_info *) 0, &id, + extcmdlist[i].ef_txt[0], 0, 0, NO_COLOR, buf, MENU_ITEMFLAGS_NONE); } @@ -848,8 +855,9 @@ amii_get_ext_cmd(void) id.a_char = extcmdlist[i].ef_txt[0]; sprintf(buf, "%-10s - %s ", extcmdlist[i].ef_txt, extcmdlist[i].ef_desc); - amii_add_menu(win, NO_GLYPH, &id, extcmdlist[i].ef_txt[0], 0, - 0, buf, MENU_ITEMFLAGS_NONE); + amii_add_menu(win, (const glyph_info *) 0, &id, + extcmdlist[i].ef_txt[0], 0, 0, NO_COLOR, + buf, MENU_ITEMFLAGS_NONE); } amii_end_menu(win, (char *) 0); @@ -941,10 +949,7 @@ amii_get_ext_cmd(void) } static int -put_ext_cmd(obufp, colx, cw, bottom) -char * obufp; -int colx, bottom; -struct amii_WinDesc *cw; +put_ext_cmd(char *obufp, int colx, struct amii_WinDesc *cw, int bottom) { struct Window *w = cw->win; char *t; @@ -989,10 +994,9 @@ struct amii_WinDesc *cw; /* Ask a question and get a response */ char -amii_yn_function(query, resp, def) -const char * query, *resp; -char def; +amii_yn_function(const char *query, const char *resp, char def) { + /* * Generic yes/no function. 'def' is the default (returned by space or * return; 'esc' returns 'q', or 'n', or the default, depending on @@ -1004,11 +1008,11 @@ char def; * are allowed); if it includes an , anything beyond that won't * be shown in the prompt to the user but will be acceptable as input. */ - register char q; + char q; char rtmp[40]; boolean digit_ok, allow_num; char prompt[BUFSZ]; - register struct amii_WinDesc *cw; + struct amii_WinDesc *cw; if (cw = amii_wins[WIN_MESSAGE]) cw->disprows = 0; @@ -1138,16 +1142,15 @@ char def; } void -amii_display_file(fn, complain) -const char * fn; -boolean complain; +amii_display_file(const char *fn, boolean complain) { - register struct amii_WinDesc *cw; - register int win; - register dlb *fp; - register char *t; + struct amii_WinDesc *cw; + int win; + dlb *fp; + char *t; char buf[200]; + if (fn == NULL) panic("NULL file name in display_file()"); @@ -1194,12 +1197,11 @@ boolean complain; * are rendered in the up position by default. */ void -SetBorder(gd) -register struct Gadget * gd; +SetBorder(struct Gadget *gd) { - register struct Border *bp; - register short *sp; - register int i, inc = -1, dec = -1; + struct Border *bp; + short *sp; + int i, inc = -1, dec = -1; int borders = 6; int hipen = sysflags.amii_dripens[SHINEPEN], shadowpen = sysflags.amii_dripens[SHADOWPEN]; @@ -1337,7 +1339,7 @@ register struct Gadget * gd; /* Following function copied from wintty.c; Modified slightly to fit amiga needs */ void -amii_player_selection() +amii_player_selection(void) { int i, k, n; char pick4u = 'n', thisch, lastch = 0; @@ -1346,6 +1348,7 @@ amii_player_selection() anything any; menu_item *selected = 0; + rigid_role_checks(); /* Should we randomly pick for the player? */ @@ -1424,8 +1427,8 @@ amii_player_selection() } else Strcpy(rolenamebuf, roles[i].name.m); } - add_menu(win, NO_GLYPH, &any, thisch, 0, ATR_NONE, - an(rolenamebuf), MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, thisch, 0, ATR_NONE, + NO_COLOR, an(rolenamebuf), MENU_ITEMFLAGS_NONE); lastch = thisch; } } @@ -1433,11 +1436,11 @@ amii_player_selection() flags.initalign, PICK_RANDOM) + 1; if (any.a_int == 0) /* must be non-zero */ any.a_int = randrole(FALSE) + 1; - add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random", - MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, '*', 0, ATR_NONE, + NO_COLOR, "Random", MENU_ITEMFLAGS_NONE); any.a_int = i + 1; /* must be non-zero */ - add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit", - MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, 'q', 0, ATR_NONE, + NO_COLOR, "Quit", MENU_ITEMFLAGS_NONE); Sprintf(pbuf, "Pick a role for your %s", plbuf); end_menu(win, pbuf); n = select_menu(win, PICK_ONE, &selected); @@ -1498,19 +1501,19 @@ amii_player_selection() if (ok_race(flags.initrole, i, flags.initgend, flags.initalign)) { any.a_int = i + 1; /* must be non-zero */ - add_menu(win, NO_GLYPH, &any, races[i].noun[0], 0, - ATR_NONE, races[i].noun, + add_menu(win, &nul_glyphinfo, &any, races[i].noun[0], 0, + ATR_NONE, NO_COLOR, races[i].noun, MENU_ITEMFLAGS_NONE); } any.a_int = pick_race(flags.initrole, flags.initgend, flags.initalign, PICK_RANDOM) + 1; if (any.a_int == 0) /* must be non-zero */ any.a_int = randrace(flags.initrole) + 1; - add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random", - MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, '*', 0, ATR_NONE, + NO_COLOR, "Random", MENU_ITEMFLAGS_NONE); any.a_int = i + 1; /* must be non-zero */ - add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit", - MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, 'q', 0, ATR_NONE, + NO_COLOR, "Quit", MENU_ITEMFLAGS_NONE); Sprintf(pbuf, "Pick the race of your %s", plbuf); end_menu(win, pbuf); n = select_menu(win, PICK_ONE, &selected); @@ -1571,18 +1574,19 @@ amii_player_selection() if (ok_gend(flags.initrole, flags.initrace, i, flags.initalign)) { any.a_int = i + 1; - add_menu(win, NO_GLYPH, &any, genders[i].adj[0], 0, - ATR_NONE, genders[i].adj, MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, genders[i].adj[0], 0, + ATR_NONE, NO_COLOR, genders[i].adj, + MENU_ITEMFLAGS_NONE); } any.a_int = pick_gend(flags.initrole, flags.initrace, flags.initalign, PICK_RANDOM) + 1; if (any.a_int == 0) /* must be non-zero */ any.a_int = randgend(flags.initrole, flags.initrace) + 1; - add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random", - MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, '*', 0, ATR_NONE, + NO_COLOR, "Random", MENU_ITEMFLAGS_NONE); any.a_int = i + 1; /* must be non-zero */ - add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit", - MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, 'q', 0, ATR_NONE, + NO_COLOR, "Quit", MENU_ITEMFLAGS_NONE); Sprintf(pbuf, "Pick the gender of your %s", plbuf); end_menu(win, pbuf); n = select_menu(win, PICK_ONE, &selected); @@ -1642,18 +1646,19 @@ amii_player_selection() if (ok_align(flags.initrole, flags.initrace, flags.initgend, i)) { any.a_int = i + 1; - add_menu(win, NO_GLYPH, &any, aligns[i].adj[0], 0, - ATR_NONE, aligns[i].adj, MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, aligns[i].adj[0], 0, + ATR_NONE, NO_COLOR, aligns[i].adj, + MENU_ITEMFLAGS_NONE); } any.a_int = pick_align(flags.initrole, flags.initrace, flags.initgend, PICK_RANDOM) + 1; if (any.a_int == 0) /* must be non-zero */ any.a_int = randalign(flags.initrole, flags.initrace) + 1; - add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random", - MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, '*', 0, ATR_NONE, + NO_COLOR, "Random", MENU_ITEMFLAGS_NONE); any.a_int = i + 1; /* must be non-zero */ - add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit", - MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, 'q', 0, ATR_NONE, + NO_COLOR, "Quit", MENU_ITEMFLAGS_NONE); Sprintf(pbuf, "Pick the alignment of your %s", plbuf); end_menu(win, pbuf); n = select_menu(win, PICK_ONE, &selected); diff --git a/outdated/sys/amiga/winami.p b/sys/amiga/winami.p similarity index 87% rename from outdated/sys/amiga/winami.p rename to sys/amiga/winami.p index 2e26cfb10..dfb588177 100644 --- a/outdated/sys/amiga/winami.p +++ b/sys/amiga/winami.p @@ -5,10 +5,10 @@ void amii_raw_print(const char *); void amii_raw_print_bold(const char *); void amii_start_menu(winid , unsigned long ); -void amii_add_menu(winid , char , int , const char *, unsigned int); +void amii_add_menu(winid, const glyph_info *, const anything *, char, char, int, int, const char *, unsigned int); void amii_end_menu(winid , char , const char * , const char *); char amii_select_menu(winid ); -void amii_update_inventory (void); +void amii_update_inventory(int); void amii_mark_synch (void); void amii_wait_synch (void); void amii_setclipped (void); @@ -27,7 +27,7 @@ void amii_putstr(winid , int , const char *); void amii_putsym(winid , int , int , CHAR_P ); void amii_clear_nhwindow(winid ); void amii_exit_nhwindows(const char *); -int amii_nh_poskey(int * , int * , int *); +int amii_nh_poskey(coordxy *, coordxy *, int *); int amii_nhgetch (void); void amii_get_nh_event (void); void amii_remember_topl (void); @@ -35,7 +35,7 @@ int amii_doprev_message (void); void amii_display_nhwindow(winid , boolean ); void amii_display_file(const char * , boolean ); void amii_curs(winid , int , int ); -void amii_print_glyph(winid , coordxy , coordxy , int, int ); +void amii_print_glyph(winid, coordxy, coordxy, const glyph_info *, const glyph_info *); void DoMenuScroll(int , int ); void DisplayData(int , int , int ); void SetPropInfo(struct Window * , struct Gadget * , long , long , long ); diff --git a/outdated/sys/amiga/winchar.c b/sys/amiga/winchar.c similarity index 78% rename from outdated/sys/amiga/winchar.c rename to sys/amiga/winchar.c index e4e56b8a2..75c35c87b 100644 --- a/outdated/sys/amiga/winchar.c +++ b/sys/amiga/winchar.c @@ -41,7 +41,8 @@ int main(int, char **); struct BitMap *MyAllocBitMap(int, int, int, long); void MyFreeBitMap(struct BitMap *); -void FreeImageFiles(char **, struct BitMap **); +BitMapHeader ReadImageFile(const char *, struct BitMap **); +void FreeImageFile(struct BitMap **); void amiv_flush_glyph_buffer(struct Window *); void amiv_lprint_glyph(winid, int, int); void amii_lprint_glyph(winid, int, int); @@ -91,17 +92,9 @@ extern int maxmontile, maxobjtile, maxothtile; /* from tile.c */ struct PDAT pictdata; -#define NUMTILEIMAGES 3 -char *tileimages[] = { -#define TBLMONTILE 0 - "NetHack:tiles/monsters.iff", -#define TBLOBJTILE 1 - "NetHack:tiles/objects.iff", -#define TBLOTHTILE 2 - "NetHack:tiles/other.iff", 0, -}; - -struct BitMap *ifftimg[NUMTILEIMAGES], *tile; +/* Single tile image file, set at runtime by amii_init_nhwindows */ +char *tilefile; +struct BitMap *tileimg, *tile; #ifdef TESTING short pens[NUMDRIPENS] = { 8, 3, 15, 0, 15, 7, 7, 8, 0 }; @@ -146,7 +139,7 @@ main(int argc, char **argv) x = x % IMGCOLUMNS; dx = i % (IMGCOLUMNS * 2); dy = i / (IMGCOLUMNS * 2); - BltBitMapRastPort(ifftimg[tbl], x * pictdata.xsize, + BltBitMapRastPort(tileimg, x * pictdata.xsize, y * pictdata.ysize, w->RPort, w->BorderLeft + 1 + dx * pictdata.xsize, w->BorderTop + 1 + dy * pictdata.ysize, @@ -176,175 +169,112 @@ main(int argc, char **argv) CloseScreen(scr); } - FreeImageFiles(tileimages, ifftimg); + FreeTileImageFiles(); return (0); } #endif +/* + * Read a single BMAP IFF file into a BitMap. + * Returns the BitMapHeader; *bmp receives the bitmap. + * Caller frees via FreeImageFile(). + */ BitMapHeader -ReadTileImageFiles() +ReadImageFile(const char *filename, struct BitMap **bmp) { - char *errstr = NULL; - BitMapHeader ret = ReadImageFiles(tileimages, ifftimg, &errstr); - if (errstr) { - panic(errstr); - } - return ret; -} - -BitMapHeader -ReadImageFiles(char **filenames, struct BitMap **iffimg, char **errstrp) -{ - BitMapHeader *bmhd = NULL, bmhds; - unsigned char *cmap; - extern int errno; - register int i, j; + BitMapHeader *bmhd, bmhds; + int j, np; struct IFFHandle *iff; struct StoredProperty *prop; IFFParseBase = OpenLibrary("iffparse.library", 0L); - if (!IFFParseBase) { - *errstrp = "No iffparse.library"; - return bmhds; + if (!IFFParseBase) + panic("No iffparse.library"); + + iff = AllocIFF(); + if (!iff) + panic("can't start IFF processing"); + + iff->iff_Stream = Open(filename, MODE_OLDFILE); + if (iff->iff_Stream == 0) + panic("Can't open %s", filename); + + InitIFFasDOS(iff); + OpenIFF(iff, IFFF_READ); + PropChunk(iff, ID_BMAP, ID_BMHD); + PropChunk(iff, ID_BMAP, ID_CMAP); + PropChunk(iff, ID_BMAP, ID_PDAT); + StopChunk(iff, ID_BMAP, ID_PLNE); + if ((j = ParseIFF(iff, IFFPARSE_SCAN)) != 0) + panic("ParseIFF failed on %s, code %d", + filename, j); + + prop = FindProp(iff, ID_BMAP, ID_BMHD); + if (!prop) + panic("No BMHD chunk in %s", filename); + bmhd = (BitMapHeader *) prop->sp_Data; + np = bmhd->nPlanes; + + /* Load CMAP into palette arrays if present */ + prop = FindProp(iff, ID_BMAP, ID_CMAP); + if (prop) { + unsigned char *cmap = prop->sp_Data; + for (j = 0; j < (1L << np) * 3; j += 3) { + amii_initmap[j / 3] = + amiv_init_map[j / 3] = + ((cmap[j+0] >> 4) << 8) + | ((cmap[j+1] >> 4) << 4) + | (cmap[j+2] >> 4); + } } - /* - for( i = 0; filenames[i]; ++i ) - memset( iffimg[i], 0, sizeof( struct BitMap ) ); - */ - for (i = 0; filenames[i]; ++i) { - iff = AllocIFF(); - if (!iff) { - FreeImageFiles(filenames, iffimg); - *errstrp = "can't start IFF processing"; - return bmhds; - } - iff->iff_Stream = Open(filenames[i], MODE_OLDFILE); - if (iff->iff_Stream == 0) { - char *buf = malloc(100 + strlen(filenames[i])); - FreeImageFiles(filenames, iffimg); - sprintf(buf, "Can't open %s: %s", filenames[i], strerror(errno)); - *errstrp = buf; - return bmhds; - } - InitIFFasDOS(iff); - OpenIFF(iff, IFFF_READ); - PropChunk(iff, ID_BMAP, ID_BMHD); - PropChunk(iff, ID_BMAP, ID_CMAP); - PropChunk(iff, ID_BMAP, ID_CAMG); - PropChunk(iff, ID_BMAP, ID_PDAT); - StopChunk(iff, ID_BMAP, ID_PLNE); - if ((j = ParseIFF(iff, IFFPARSE_SCAN)) != 0) { - char *buf = malloc(100); - FreeImageFiles(filenames, iffimg); - sprintf(buf, "ParseIFF failed for image %d, failure code: %d", i, - j); - *errstrp = buf; - return bmhds; - } + /* Load PDAT if present */ + prop = FindProp(iff, ID_BMAP, ID_PDAT); + if (prop) + pictdata = *(struct PDAT *) prop->sp_Data; - if (prop = FindProp(iff, ID_BMAP, ID_BMHD)) { - bmhd = (BitMapHeader *) prop->sp_Data; - } else { - FreeImageFiles(filenames, iffimg); - CloseIFF(iff); - Close(iff->iff_Stream); - FreeIFF(iff); - *errstrp = "No BMHD CHUNK in file"; - return bmhds; - } + *bmp = MyAllocBitMap(bmhd->w, bmhd->h, + np, MEMF_CHIP | MEMF_CLEAR); + if (!*bmp) + panic("Can't allocate bitmap for %s", filename); - if (prop = FindProp(iff, ID_BMAP, ID_CMAP)) { - cmap = prop->sp_Data; - for (j = 0; j < (1L << bmhd->nPlanes) * 3; j += 3) { -#if 0 - /* Some day we will want to use the larger palette - * resolution available under v39 and later. i.e. - * 32 instead of 12 bits of color. Ususally this - * just means shifting the color left by 16-20 bits - * depending on what intensity looks best. Experience - * says that the higher values are better intensities. - * - * For now though we won't do this. The color table - * structure is incompatible with earlier versions of - * intuition. We would have to do some funny things - * to make 3*AMII_MAXCOLORS longs work like 3*AMII_MAXCOLORS - * UWORD's at run time... A union would help, but... - */ - if( IntuitionBase->LibNode.lib_Version >= 39 ) - { - /* 8 bits of color, so shift to left end. */ - amiv_init_map[ j+0 ] = cmap[j+0]<<24; - amiv_init_map[ j+1 ] = cmap[j+1]<<24; - amiv_init_map[ j+2 ] = cmap[j+2]<<24; - } - else -#endif - { -/* We can only use 4 bits of the 8 that are stored in the - * cmap, so mask them and then shift them into position - * for the UWORD value to store. - */ -#ifndef TESTING - amii_initmap[j / 3] = amiv_init_map[j / 3] = - ((cmap[j + 0] >> 4) << 8) | ((cmap[j + 1] >> 4) << 4) - | (cmap[j + 2] >> 4); -#endif - } - } - } else { - FreeImageFiles(filenames, iffimg); - CloseIFF(iff); - Close(iff->iff_Stream); - FreeIFF(iff); - *errstrp = "No CMAP CHUNK in file"; - return bmhds; - } + for (j = 0; j < np; j++) + ReadChunkBytes(iff, (*bmp)->Planes[j], + RASSIZE(bmhd->w, bmhd->h)); - if (prop = FindProp(iff, ID_BMAP, ID_PDAT)) { - struct PDAT *pp; - - pp = (struct PDAT *) prop->sp_Data; - pictdata = *pp; - } else { - FreeImageFiles(filenames, iffimg); - CloseIFF(iff); - Close(iff->iff_Stream); - FreeIFF(iff); - *errstrp = "No PDAT CHUNK in file"; - return bmhds; - } - - iffimg[i] = MyAllocBitMap(bmhd->w, bmhd->h, - pictdata.nplanes + amii_extraplanes, - MEMF_CHIP | MEMF_CLEAR); - if (iffimg[i] == NULL) { - char *buf = malloc(80); - FreeImageFiles(filenames, iffimg); - sprintf(buf, "Can't allocate bitmap for image %d\n", i); - *errstrp = buf; - return bmhds; - } - for (j = 0; j < pictdata.nplanes + amii_extraplanes; ++j) { - ReadChunkBytes(iff, iffimg[i]->Planes[j], - RASSIZE(bmhd->w, bmhd->h)); - } - bmhds = *bmhd; - CloseIFF(iff); - Close(iff->iff_Stream); - FreeIFF(iff); - } + bmhds = *bmhd; + CloseIFF(iff); + Close(iff->iff_Stream); + FreeIFF(iff); CloseLibrary(IFFParseBase); - tile = MyAllocBitMap(pictdata.xsize, pictdata.ysize, - pictdata.nplanes + amii_extraplanes, - MEMF_CHIP | MEMF_CLEAR); - if (tile == NULL) { - FreeImageFiles(filenames, iffimg); - *errstrp = "Can't allocate tile bitmap for scaling"; + return bmhds; +} + +void +FreeImageFile(struct BitMap **bmp) +{ + if (*bmp) { + MyFreeBitMap(*bmp); + *bmp = NULL; } - return (bmhds); +} + +BitMapHeader +ReadTileImageFiles(void) +{ + BitMapHeader bmhds; + + bmhds = ReadImageFile(tilefile, &tileimg); + + tile = MyAllocBitMap(pictdata.xsize, pictdata.ysize, + pictdata.nplanes + amii_extraplanes, + MEMF_CHIP | MEMF_CLEAR); + if (!tile) + panic("Can't allocate tile temp bitmap"); + + return bmhds; } struct MyBitMap { @@ -401,8 +331,7 @@ MyFreeBitMap(struct BitMap *bmp) #ifdef TESTING void -panic(s, a1, a2, a3, a4) -char *s; +panic(char *s, long a1, long a2, long a3, long a4) { printf(s, a1, a2, a3, a4); putchar('\n'); @@ -420,24 +349,10 @@ alloc(unsigned int x) #endif void -FreeTileImageFiles() +FreeTileImageFiles(void) { - FreeImageFiles(tileimages, ifftimg); -} - -void -FreeImageFiles(char **filenames, struct BitMap **img) -{ - register int i; - - for (i = 0; filenames[i]; ++i) { - if (img[i]) - MyFreeBitMap(img[i]); - } - - /* REALLY ugly hack alert! */ - if (tile && img == ifftimg) - MyFreeBitMap(tile); + FreeImageFile(&tileimg); + FreeImageFile(&tile); } #ifndef TESTING @@ -456,8 +371,7 @@ struct amiv_glyph_node amiv_g_nodes[NUMBER_GLYPH_NODES]; static char amiv_glyph_buffer[GLYPH_BUFFER_SIZE]; void -flush_glyph_buffer(vw) -struct Window *vw; +flush_glyph_buffer(struct Window *vw) { if (WINVERS_AMIV) amiv_flush_glyph_buffer(vw); @@ -469,8 +383,7 @@ struct Window *vw; * Routine to flush whatever is buffered */ void -amiv_flush_glyph_buffer(vw) -struct Window *vw; +amiv_flush_glyph_buffer(struct Window *vw) { #if !defined(DISPMAP) || defined(OPT_DISPMAP) int xsize, ysize, x, y; @@ -481,7 +394,7 @@ struct Window *vw; struct BitMap *imgbm = 0, *bm = 0; int i, k; int scaling_needed; - register struct RastPort *rp = vw->RPort; + struct RastPort *rp = vw->RPort; #endif /* If nothing is buffered, return before we do anything */ @@ -577,7 +490,7 @@ struct Window *vw; /* Go ahead and start dumping the stuff */ for (i = 0; i < glyph_node_index; ++i) { /* Do it */ - register int offx, offy, j; + int offx, offy, j; struct BitMap *nodebm = amiv_g_nodes[i].bitmap; /* Get the unclipped coordinates */ @@ -596,10 +509,10 @@ struct Window *vw; * this code is generalized to handle any size tile * image... */ - memcpy(tile->Planes[j] + ((k * pictdata.ysize) / 8), + memcpy(tile->Planes[j] + k * tile->BytesPerRow, nodebm->Planes[j] + offx + offy + (nodebm->BytesPerRow * k), - pictdata.ysize / 8); + pictdata.xsize / 8); } } @@ -648,41 +561,30 @@ struct Window *vw; * Glyph buffering routine. Called instead of WindowPuts(). */ void -amiv_lprint_glyph(window, color_index, glyph) -winid window; -int color_index, glyph; +amiv_lprint_glyph(winid window, int color_index, int glyph) { int base; struct amii_WinDesc *cw; struct Window *w; int curx; int cury; - int tbl, icon; - register int xoff, yoff; + int icon; + int xoff, yoff; - /* Get the real icon index */ - if (glyph != NO_GLYPH) - icon = GlyphToIcon(glyph); + /* Skip NO_GLYPH — nothing to draw */ + if (glyph == NO_GLYPH) + return; + + icon = GlyphToIcon(glyph); if ((cw = amii_wins[window]) == (struct amii_WinDesc *) NULL) panic("bad winid in amiv_lprint_glyph: %d", window); w = cw->win; - if (glyph != NO_GLYPH && glyph < 10000) { - /* decide on which image has the needed picture */ - if (icon <= MAXMONTILE) { - tbl = TBLMONTILE; - base = 0; - } else if (icon <= MAXOBJTILE) { - tbl = TBLOBJTILE; - base = MAXMONTILE + 1; - } else if (icon <= MAXOTHTILE) { - tbl = TBLOTHTILE; - base = MAXOBJTILE + 1; - } else - panic("Bad icon #%d, glyph #%d, only %d icons known\n", icon, - glyph, MAXOTHTILE); + if (glyph < 10000) { + if (icon >= pictdata.npics) + icon = 0; /* fallback for out-of-range */ /* Get the relative offset in the page */ @@ -748,11 +650,11 @@ int color_index, glyph; amiv_g_nodes[glyph_node_index].odstx = cw->curx; amiv_g_nodes[glyph_node_index].srcx = xoff; amiv_g_nodes[glyph_node_index].srcy = yoff; - amiv_g_nodes[glyph_node_index].bitmap = ifftimg[tbl]; + amiv_g_nodes[glyph_node_index].bitmap = tileimg; ++glyph_node_index; } else { /* Do it */ - register int j, k, x, y, apen; + int j, k, x, y, apen; struct RastPort *rp = w->RPort; x = rp->cp_x - pictdata.xsize - 3; #ifdef OPT_DISPMAP @@ -770,17 +672,17 @@ int color_index, glyph; y = rp->cp_y - pictdata.ysize + 1; if (glyph != NO_GLYPH) { - struct BitMap *bm = ifftimg[tbl]; + struct BitMap *bm = tileimg; /* 8 bits per byte */ xoff /= 8; yoff *= bm->BytesPerRow; for (j = 0; j < pictdata.nplanes; ++j) { for (k = 0; k < pictdata.ysize; ++k) { - memcpy(tile->Planes[j] + ((k * pictdata.ysize) / 8), + memcpy(tile->Planes[j] + k * tile->BytesPerRow, bm->Planes[j] + xoff + yoff + (bm->BytesPerRow * k), - pictdata.ysize / 8); + pictdata.xsize / 8); } } @@ -824,8 +726,7 @@ static int usecolor; */ void -amiv_start_glyphout(window) -winid window; +amiv_start_glyphout(winid window) { struct amii_WinDesc *cw; struct Window *w; @@ -860,8 +761,7 @@ winid window; * General cleanup routine -- flushes and restores cursor */ void -amii_end_glyphout(window) -winid window; +amii_end_glyphout(winid window) { struct amii_WinDesc *cw; struct Window *w; @@ -891,7 +791,7 @@ winid window; Move(w->RPort, xsave, ysave); } -static maze_type = COL_MAZE_BRICK; +static int maze_type = COL_MAZE_BRICK; void SetMazeType(MazeType t) @@ -987,11 +887,10 @@ int backg[AMII_MAXCOLORS] = { * Routine to simply flush whatever is buffered */ void -amii_flush_glyph_buffer(w) -struct Window *w; +amii_flush_glyph_buffer(struct Window *w) { short i, x, y; - register struct RastPort *rp = w->RPort; + struct RastPort *rp = w->RPort; /* If nothing is buffered, return before we do anything */ if (glyph_node_index == 0) @@ -1013,6 +912,10 @@ struct Window *w; + rp->TxBaseline + 1; x = amii_g_nodes[i].x * rp->TxWidth + w->BorderLeft; + /* Skip if pixel coordinates are outside window */ + if (x < 0 || y < 0 || y >= w->Height || x >= w->Width) + continue; + /* Move pens to correct location */ Move(rp, (long) x, (long) y); @@ -1029,9 +932,7 @@ struct Window *w; glyph_node_index = glyph_buffer_index = 0; } void -amiga_print_glyph(window, color_index, glyph) -winid window; -int color_index, glyph; +amiga_print_glyph(winid window, int color_index, int glyph) { if (WINVERS_AMIV) amiv_lprint_glyph(window, color_index, glyph); @@ -1043,9 +944,7 @@ int color_index, glyph; * Glyph buffering routine. Called instead of WindowPuts(). */ void -amii_lprint_glyph(window, color_index, glyph) -winid window; -int color_index, glyph; +amii_lprint_glyph(winid window, int color_index, int glyph) { int fg_color, bg_color; struct amii_WinDesc *cw; @@ -1120,8 +1019,7 @@ static int usecolor; */ void -amii_start_glyphout(window) -winid window; +amii_start_glyphout(winid window) { struct amii_WinDesc *cw; struct Window *w; @@ -1195,7 +1093,7 @@ amii_end_glyphout(window) #ifdef OPT_DISPMAP /* don't use dispmap unless x & y are 8,16,24,32,48 and equal */ void -dispmap_sanity() +dispmap_sanity(void) { if (mxsize != mysize || dispmap_sanity1(mxsize) || dispmap_sanity1(mysize)) { @@ -1203,11 +1101,10 @@ dispmap_sanity() } } int -dispmap_sanity1(x) -int x; +dispmap_sanity1(int x) { static unsigned char valid[] = { 8, 16, 24, 32, 48, 0 }; - return !!strchr(valid, x); + return !strchr((char *)valid, x); } #endif /* OPT_DISPMAP */ #endif /* TESTING */ diff --git a/outdated/sys/amiga/windefs.h b/sys/amiga/windefs.h similarity index 98% rename from outdated/sys/amiga/windefs.h rename to sys/amiga/windefs.h index 9e59da82f..cb6cbd3db 100644 --- a/outdated/sys/amiga/windefs.h +++ b/sys/amiga/windefs.h @@ -112,7 +112,7 @@ CLIPPING must be defined for the AMIGA version #endif #define WINVERS_AMII (strcmp("amii", windowprocs.name) == 0) -#define WINVERS_AMIV (strcmp("amitile", windowprocs.name) == 0) +#define WINVERS_AMIV (strcmp("amiv", windowprocs.name) == 0) #define WINVERS_AMIT (strcmp("amitty", windowprocs.name) == 0) /* cw->data[x] contains 2 characters worth of special information. These @@ -196,3 +196,4 @@ struct PDAT #undef MAXCOLORS #define MAXCOLORS 256 + diff --git a/outdated/sys/amiga/winext.h b/sys/amiga/winext.h similarity index 100% rename from outdated/sys/amiga/winext.h rename to sys/amiga/winext.h diff --git a/outdated/sys/amiga/winfuncs.c b/sys/amiga/winfuncs.c similarity index 86% rename from outdated/sys/amiga/winfuncs.c rename to sys/amiga/winfuncs.c index 28a811ba7..7d451a665 100644 --- a/outdated/sys/amiga/winfuncs.c +++ b/sys/amiga/winfuncs.c @@ -14,7 +14,6 @@ #endif #include "patchlevel.h" -#include "date.h" extern struct TagItem scrntags[]; #ifndef CROSS_TO_AMIGA @@ -40,6 +39,11 @@ int xclipbord = 4, yclipbord = 2; #endif int mxsize, mysize; + +/* Track the last level we centered the clipping viewport on, so that + both amii_clear_nhwindow (pre-docrt centering) and amii_cliparound + (scroll/pan during play) can detect level changes. */ +static d_level clip_saved_level = { 127, 127 }; /* XXX */ struct Rectangle amii_oldover; struct Rectangle amii_oldmsg; @@ -58,8 +62,7 @@ int amii_otherBPen; long amii_libvers = LIBRARY_FONT_VERSION; void -ami_wininit_data(dir) -int dir; +ami_wininit_data(int dir) { extern unsigned short amii_init_map[AMII_MAXCOLORS]; extern unsigned short amiv_init_map[AMII_MAXCOLORS]; @@ -150,12 +153,13 @@ struct TagItem wintags[] = { }; #endif -void amii_destroy_nhwindow(win) /* just hide */ -register winid win; +void +amii_destroy_nhwindow(winid win) /* just hide */ { int i; int type; - register struct amii_WinDesc *cw; + struct amii_WinDesc *cw; + if (win == WIN_ERR || (cw = amii_wins[win]) == NULL) { panic(winpanicstr, win, "destroy_nhwindow"); @@ -260,26 +264,37 @@ PPC_LayerFillHook(void) struct RastPort *rp = (struct RastPort *) REG_A2; struct FillParams *fp = (struct FillParams *) REG_A1; #else +/* Assembly trampoline: Intuition calls LayerFillHook with arguments + in registers a0 (hook), a1 (fillparams), a2 (rastport). + Push them onto the stack and call the C implementation. */ +__asm( +" .globl _LayerFillHook\n" +"_LayerFillHook:\n" +" move.l a1,-(sp)\n" +" move.l a2,-(sp)\n" +" move.l a0,-(sp)\n" +" jsr _LayerFillHook_impl\n" +" lea 12(sp),sp\n" +" rts\n" +); void -LayerFillHook(void) +LayerFillHook_impl(struct Hook *hk, struct RastPort *rp, + struct FillParams *fp) { - register struct Hook *hk asm("a0"); - register struct RastPort *rp asm("a2"); - register struct FillParams *fp asm("a1"); #endif #else void #ifndef _DCC __interrupt #endif - __saveds __asm LayerFillHook(register __a0 struct Hook *hk, - register __a2 struct RastPort *rp, - register __a1 struct FillParams *fp) + __saveds __asm LayerFillHook(__a0 struct Hook *hk, + __a2 struct RastPort *rp, + __a1 struct FillParams *fp) { #endif - register long x, y, xmax, ymax; - register int apen; + long x, y, xmax, ymax; + int apen; struct RastPort rptmp; memcpy(&rptmp, rp, sizeof(struct RastPort)); @@ -321,15 +336,17 @@ void } #endif -amii_create_nhwindow(type) register int type; +winid +amii_create_nhwindow(int type) { - register struct Window *w = NULL; - register struct NewWindow *nw = NULL; - register struct amii_WinDesc *wd = NULL; + struct Window *w = NULL; + struct NewWindow *nw = NULL; + struct amii_WinDesc *wd = NULL; struct Window *mapwin = NULL, *stwin = NULL, *msgwin = NULL; - register int newid; + int newid; int maph, stath, scrfontysize; + scrfontysize = HackScreen->Font->ta_YSize; /* @@ -563,11 +580,7 @@ amii_create_nhwindow(type) register int type; case NHW_OVER: case NHW_MAP: if (wd) { -#ifdef __GNUC__ fillhook.h_Entry = (void *) &LayerFillHook; -#else - fillhook.h_Entry = (ULONG (*) ()) LayerFillHook; -#endif fillhook.h_Data = (void *) type; fillhook.h_SubEntry = 0; wd->hook = alloc(sizeof(fillhook)); @@ -665,6 +678,10 @@ amii_create_nhwindow(type) register int type; / w->RPort->TxHeight; wd->cols = (w->Width - w->BorderLeft - w->BorderRight - 2) / w->RPort->TxWidth; + /* Map window uses y+2 offset in amii_print_glyph, so cury + values go up to ROWNO+2. Ensure rows accommodates this. */ + if (type == NHW_MAP && wd->rows < ROWNO + 3) + wd->rows = ROWNO + 3; } /* Okay, now do the individual type initialization */ @@ -736,7 +753,9 @@ amii_create_nhwindow(type) register int type; */ wd->data = (char **) alloc(3 * sizeof(char *)); wd->data[0] = (char *) alloc(wd->cols + 10); + wd->data[0][0] = '\0'; wd->data[1] = (char *) alloc(wd->cols + 10); + wd->data[1][0] = '\0'; wd->data[2] = NULL; break; @@ -771,7 +790,7 @@ amii_create_nhwindow(type) register int type; SetMenuStrip(w, MenuStrip); /* Make our requesters come to our screen */ { - register struct Process *myProcess = + struct Process *myProcess = (struct Process *) FindTask(NULL); pr_WindowPtr = (struct Window *) (myProcess->pr_WindowPtr); myProcess->pr_WindowPtr = (APTR) w; @@ -787,17 +806,7 @@ amii_create_nhwindow(type) register int type; Abort(AG_OpenDev | AO_ConsoleDev); } - ConsoleDevice = -#ifndef CROSS_TO_AMIGA - (struct Library *) -#else - (struct Device * -# ifdef __CONSTLIBBASEDECL__ - __CONSTLIBBASEDECL__ -# endif /* __CONSTLIBBASEDECL__ */ - ) -#endif - ConsoleIO.io_Device; + ConsoleDevice = (struct Device *) ConsoleIO.io_Device; KbdBuffered = 0; #ifdef HACKFONT @@ -831,12 +840,23 @@ PPC_SM_Filter(void) ULONG modeID = (ULONG) REG_A1; struct ScreenModeRequester *smr = (struct ScreenModeRequester *) REG_A2; #else +/* Assembly trampoline for SM_Filter hook callback. + SM_Filter is the asm entry point; SM_Filter_impl is the C body. */ +extern void SM_Filter(void); +__asm( +" .globl _SM_Filter\n" +"_SM_Filter:\n" +" move.l a2,-(sp)\n" +" move.l a1,-(sp)\n" +" move.l a0,-(sp)\n" +" jsr _SM_Filter_impl\n" +" lea 12(sp),sp\n" +" rts\n" +); int -SM_Filter(void) +SM_Filter_impl(struct Hook *hk, ULONG modeID, + struct ScreenModeRequester *smr) { - register struct Hook *hk asm("a0"); - register ULONG modeID asm("a1"); - register struct ScreenModeRequester *smr asm("a2"); #endif #else int @@ -844,8 +864,8 @@ int __interrupt #endif __saveds __asm SM_Filter( - register __a0 struct Hook *hk, register __a1 ULONG modeID, - register __a2 struct ScreenModeRequester *smr) + __a0 struct Hook *hk, __a1 ULONG modeID, + __a2 struct ScreenModeRequester *smr) { #endif struct DimensionInfo dims; @@ -869,14 +889,13 @@ int /* Initialize the windowing environment */ void -amii_init_nhwindows(argcp, argv) -int *argcp; -char **argv; +amii_init_nhwindows(int *argcp, char **argv) { int i; struct Screen *wbscr; int forcenobig = 0; + if (HackScreen) panic("init_nhwindows() called twice", 0); @@ -1192,9 +1211,16 @@ char **argv; } #endif - if (WINVERS_AMIV) + if (WINVERS_AMIV) { + extern char *tilefile; + if (amii_numcolors >= 32) { + tilefile = "NetHack:tiles/tiles32.iff"; + amii_numcolors = 32; + } else { + tilefile = "NetHack:tiles/tiles16.iff"; + } amii_bmhd = ReadTileImageFiles(); - else + } else memcpy(amii_initmap, amii_init_map, sizeof(amii_initmap)); memcpy(sysflags.amii_curmap, amii_initmap, sizeof(sysflags.amii_curmap)); @@ -1361,11 +1387,11 @@ amii_setdrawpens(struct Window *w, int type) /* Clear the indicated window */ void -amii_clear_nhwindow(win) -register winid win; +amii_clear_nhwindow(winid win) { - register struct amii_WinDesc *cw; - register struct Window *w; + struct amii_WinDesc *cw; + struct Window *w; + if (reclip == 2) return; @@ -1394,6 +1420,40 @@ register winid win; amii_setfillpens(w, cw->type); SetDrMd(w->RPort, JAM2); +#ifdef CLIPPING + /* When clearing the map for a full redraw (docrt/cls), center the + clipping viewport on the player BEFORE the map is redrawn. + Without this, the first docrt() after a level change or new game + draws with stale clip coordinates (typically 0,0), leaving the + player off-screen until their first move triggers amii_cliparound. */ + if (cw->type == NHW_MAP && clipping && u.ux + && !on_level(&u.uz, &clip_saved_level)) { + int COx, LIx; + struct RastPort *rp = w->RPort; + + if (Is_rogue_level(&u.uz)) { + COx = (w->Width - w->BorderLeft - w->BorderRight) / rp->TxWidth; + LIx = (w->Height - w->BorderTop - w->BorderBottom) / rp->TxHeight; + } else { + COx = CO; + LIx = LI; + } + clipx = max(0, (int) u.ux - COx / 2); + clipxmax = clipx + COx; + if (clipxmax > COLNO) { + clipxmax = COLNO; + clipx = clipxmax - COx; + } + clipy = max(0, (int) u.uy - LIx / 2); + clipymax = clipy + LIx; + if (clipymax > ROWNO) { + clipymax = ROWNO; + clipy = clipymax - LIx; + } + clip_saved_level = u.uz; + } +#endif + if (cw->type == NHW_MENU || cw->type == NHW_TEXT) { RectFill(w->RPort, w->BorderLeft, w->BorderTop, w->Width - w->BorderRight - 1, @@ -1418,11 +1478,10 @@ register winid win; /* Dismiss the window from the screen */ void -dismiss_nhwindow(win) -register winid win; +dismiss_nhwindow(winid win) { - register struct Window *w; - register struct amii_WinDesc *cw; + struct Window *w; + struct amii_WinDesc *cw; if (win == WIN_ERR || (cw = amii_wins[win]) == NULL) { panic(winpanicstr, win, "dismiss_nhwindow"); @@ -1458,9 +1517,9 @@ register winid win; } void -amii_exit_nhwindows(str) -const char *str; +amii_exit_nhwindows(const char *str) { + /* Seems strange to have to do this... but we need the BASE window * left behind... */ @@ -1475,15 +1534,14 @@ const char *str; } void -amii_display_nhwindow(win, blocking) -winid win; -boolean blocking; +amii_display_nhwindow(winid win, boolean blocking) { menu_item *mip; int cnt; static int lastwin = -1; struct amii_WinDesc *cw; + if (!Initialized) return; lastwin = win; @@ -1513,13 +1571,12 @@ boolean blocking; } void -amii_curs(window, x, y) -winid window; -register int x, y; +amii_curs(winid window, int x, int y) { - register struct amii_WinDesc *cw; - register struct Window *w; - register struct RastPort *rp; + struct amii_WinDesc *cw; + struct Window *w; + struct RastPort *rp; + if (window == WIN_ERR || (cw = amii_wins[window]) == NULL) panic(winpanicstr, window, "curs"); @@ -1540,8 +1597,14 @@ register int x, y; cw->curx = x; cw->cury = y; + /* Silently skip rendering for out-of-bounds coordinates */ + if (cw->rows > 0 && y >= cw->rows) + return; + if (cw->cols > 0 && x >= cw->cols) + return; + #ifdef DEBUG - if (x < 0 || y < 0 || y >= cw->rows || x >= cw->cols) { + if (0 && (x < 0 || y < 0 || y >= cw->rows || x >= cw->cols)) { char *s = "[unknown type]"; switch (cw->type) { case NHW_MESSAGE: @@ -1663,12 +1726,10 @@ printf("pos: (%d,%d)->(%d,%d)\n",x,y,qqx,qqy); } void -amii_set_text_font(name, size) -char *name; -int size; +amii_set_text_font(char *name, int size) { - register int i; - register struct amii_WinDesc *cw; + int i; + struct amii_WinDesc *cw; int osize = TextsFont13.ta_YSize; static char nname[100]; @@ -1709,11 +1770,10 @@ int size; } void -kill_nhwindows(all) -register int all; +kill_nhwindows(int all) { - register int i; - register struct amii_WinDesc *cw; + int i; + struct amii_WinDesc *cw; /* Foreach open window in all of amii_wins[], CloseShWindow, free memory */ @@ -1726,12 +1786,10 @@ register int all; } void -amii_cl_end(cw, curs_pos) -register struct amii_WinDesc *cw; -register int curs_pos; +amii_cl_end(struct amii_WinDesc *cw, int curs_pos) { - register struct Window *w = cw->win; - register int oy, ox; + struct Window *w = cw->win; + int oy, ox; if (!w) panic("NULL window pointer in amii_cl_end()"); @@ -1745,12 +1803,11 @@ register int curs_pos; } void -cursor_off(window) -winid window; +cursor_off(winid window) { - register struct amii_WinDesc *cw; - register struct Window *w; - register struct RastPort *rp; + struct amii_WinDesc *cw; + struct Window *w; + struct RastPort *rp; int curx, cury; int x, y; long dmode; @@ -1805,13 +1862,12 @@ winid window; } void -cursor_on(window) -winid window; +cursor_on(winid window) { int x, y; - register struct amii_WinDesc *cw; - register struct Window *w; - register struct RastPort *rp; + struct amii_WinDesc *cw; + struct Window *w; + struct RastPort *rp; unsigned char ch; long dmode; short apen, bpen; @@ -1858,10 +1914,13 @@ winid window; if (WINVERS_AMIV && cw->type == NHW_MAP) { cursor_common(rp, x, y); } else { - Move(rp, x, y); - ch = CURSOR_CHAR; - Text(rp, &ch, 1); - Move(rp, x, y); + if (w && x >= 0 && y >= 0 + && x < w->Width && y < w->Height) { + Move(rp, x, y); + ch = CURSOR_CHAR; + Text(rp, &ch, 1); + Move(rp, x, y); + } } SetDrMd(rp, dmode); @@ -1870,9 +1929,7 @@ winid window; } static void -cursor_common(rp, x, y) -struct RastPort *rp; -int x, y; +cursor_common(struct RastPort *rp, int x, int y) { int x1, x2, y1, y2; @@ -1895,29 +1952,27 @@ int x, y; } void -amii_suspend_nhwindows(str) -const char *str; +amii_suspend_nhwindows(const char *str) { if (HackScreen) ScreenToBack(HackScreen); } void -amii_resume_nhwindows() +amii_resume_nhwindows(void) { if (HackScreen) ScreenToFront(HackScreen); } void -amii_bell() +amii_bell(void) { DisplayBeep(NULL); } void -removetopl(cnt) -int cnt; +removetopl(int cnt) { struct amii_WinDesc *cw = amii_wins[WIN_MESSAGE]; /* NB - this is sufficient for @@ -1935,7 +1990,7 @@ int cnt; #ifdef PORT_HELP void -port_help() +port_help(void) { display_file(PORT_HELP, 1); } @@ -1951,17 +2006,20 @@ port_help() */ void -amii_print_glyph(win, x, y, glyph, bkglyph) -winid win; -coordxy x, y; -int glyph, bkglyph; +amii_print_glyph(winid win, coordxy x, coordxy y, + const glyph_info *glyphinfo, + const glyph_info *bkglyphinfo UNUSED) { struct amii_WinDesc *cw; uchar ch; + int glyph; int color, och; extern const int zapcolors[]; unsigned special; + + glyph = glyphinfo->glyph; + /* In order for the overview window to work, we can not clip here */ if (!WINVERS_AMIV) { #ifdef CLIPPING @@ -1993,14 +2051,8 @@ if(u.uz.dlevel != x){ } else /* AMII, or Rogue level in either version */ { /* map glyph to character and color */ -#if 0 - (void) mapglyph(glyph, &och, &color, &special, x, y, 0); - ch = (uchar) och; -#else - glyph_info gi; - map_glyphinfo(0, 0, glyph, 0, &gi); - ch = gi.ttychar; -#endif + ch = glyphinfo->ttychar; + color = glyphinfo->gm.sym.color; if (WINVERS_AMIV) { /* implies Rogue level here */ amii_curs(win, x, y); amiga_print_glyph(win, NO_COLOR, ch + 10000); @@ -2021,11 +2073,11 @@ if(u.uz.dlevel != x){ /* Make sure the user sees a text string when no windowing is available */ void -amii_raw_print(s) -register const char *s; +amii_raw_print(const char *s) { int argc = 0; + if (!s) return; if (amiIDisplay) @@ -2057,11 +2109,11 @@ register const char *s; */ void -amii_raw_print_bold(s) -register const char *s; +amii_raw_print_bold(const char *s) { int argc = 0; + if (!s) return; @@ -2092,20 +2144,28 @@ register const char *s; /* Rebuild/update the inventory if the window is up. */ void -amii_update_inventory() +amii_update_inventory(int arg UNUSED) { - register struct amii_WinDesc *cw; + struct amii_WinDesc *cw; + if (WIN_INVEN != WIN_ERR && (cw = amii_wins[WIN_INVEN]) && cw->type == NHW_MENU && cw->win) { - display_inventory(NULL, FALSE); + repopulate_perminvent(); } } +/* Stub for ctrl_nhwindow - no special handling needed for Amiga */ +win_request_info * +amii_ctrl_nhwindow(winid window UNUSED, int request UNUSED, win_request_info *wri UNUSED) +{ + return (win_request_info *) 0; +} + /* Humm, doesn't really do anything useful */ void -amii_mark_synch() +amii_mark_synch(void) { if (!amiIDisplay) fflush(stderr); @@ -2116,7 +2176,7 @@ amii_mark_synch() * ask for a key to be pressed. */ void -amii_wait_synch() +amii_wait_synch(void) { if (!amiIDisplay || amiIDisplay->rawprint) { if (amiIDisplay) @@ -2130,7 +2190,7 @@ amii_wait_synch() } void -amii_setclipped() +amii_setclipped(void) { #ifdef CLIPPING clipping = TRUE; @@ -2141,12 +2201,35 @@ amii_setclipped() #endif } +/* Redraw a rectangular region of the map via newsym(). Used after + ScrollRaster() shifts existing pixels — only the exposed strip needs + redrawing. Does NOT flush; the caller flushes after all regions. */ +static void +redraw_map_region(int x1, int y1, int x2, int y2) +{ + int x, y; + + if (!u.ux) + return; + if (x1 < 1) x1 = 1; + if (y1 < 0) y1 = 0; + if (x2 >= COLNO) x2 = COLNO - 1; + if (y2 >= ROWNO) y2 = ROWNO - 1; + for (y = y1; y <= y2; y++) + for (x = x1; x <= x2; x++) { + /* Invalidate the glyph buffer so newsym() redraws even if + * the glyph hasn't changed — the pixels were shifted by + * ScrollRaster and no longer match the buffer. */ + gg.gbuf[y][x].glyphinfo.glyph = NO_GLYPH; + newsym(x, y); + } +} + /* XXX still to do: suppress scrolling if we violate the boundary but the * edge of the map is already displayed */ void -amii_cliparound(x, y) -register int x, y; +amii_cliparound(int x, int y) { #ifdef CLIPPING int oldx = clipx, oldy = clipy; @@ -2155,8 +2238,9 @@ register int x, y; #define SCROLLCNT 1 /* Get there in 3 moves... */ int scrollcnt = SCROLLCNT; /* ...or 1 if we changed level */ - if (!clipping) /* And 1 in anycase, cleaner, simpler, quicker */ + if (!clipping) { /* And 1 in anycase, cleaner, simpler, quicker */ return; + } if (Is_rogue_level(&u.uz)) { struct Window *w = amii_wins[WIN_MAP]->win; @@ -2173,16 +2257,22 @@ register int x, y; * reasonablely large window extra motion is avoided; for * the rogue level hopefully this means no motion at all. */ - { - static d_level saved_level = { 127, 127 }; /* XXX */ - - if (!on_level(&u.uz, &saved_level)) { - scrollcnt = 1; /* jump with blanking */ - clipx = clipy = 0; - clipxmax = COx; - clipymax = LIx; - saved_level = u.uz; /* save as new current level */ + if (!on_level(&u.uz, &clip_saved_level)) { + scrollcnt = 1; /* jump with blanking */ + /* Center viewport on the player for the new level. */ + clipx = max(0, x - COx / 2); + clipxmax = clipx + COx; + if (clipxmax > COLNO) { + clipxmax = COLNO; + clipx = clipxmax - COx; } + clipy = max(0, y - LIx / 2); + clipymax = clipy + LIx; + if (clipymax > ROWNO) { + clipymax = ROWNO; + clipy = clipymax - LIx; + } + clip_saved_level = u.uz; /* save as new current level */ } if (x <= clipx + xclipbord) { @@ -2204,13 +2294,12 @@ register int x, y; reclip = 1; if (clipx != oldx || clipy != oldy || clipxmax != oldxmax || clipymax != oldymax) { -#ifndef NOSCROLLRASTER struct Window *w = amii_wins[WIN_MAP]->win; struct RastPort *rp = w->RPort; - int xdelta, ydelta, xmod, ymod, i; - int incx, incy, mincx, mincy; - int savex, savey, savexmax, saveymax; - int scrx, scry; + int dx = clipx - oldx; + int dy = clipy - oldy; + int scrx, scry; /* tile pixel dimensions */ + int halfW, halfH; /* half viewport in tiles */ if (Is_rogue_level(&u.uz)) { scrx = rp->TxWidth; @@ -2220,129 +2309,63 @@ register int x, y; scry = mysize; } - /* Ask that the glyph routines not draw the overview window */ - reclip = 2; - cursor_off(WIN_MAP); + halfW = COx / 2; + halfH = LIx / 2; - /* Compute how far we are moving in terms of tiles */ - mincx = clipx - oldx; - mincy = clipy - oldy; + /* Erase the map cursor before shifting pixels, otherwise the + old cursor position leaves a COMPLEMENT artifact on screen. */ + if (WIN_MAP != WIN_ERR) + cursor_off(WIN_MAP); - /* How many tiles to get there in SCROLLCNT moves */ - incx = (clipx - oldx) / scrollcnt; - incy = (clipy - oldy) / scrollcnt; - - /* If less than SCROLLCNT tiles, then move by 1 tile if moving at all - */ - if (incx == 0) - incx = (mincx != 0); - if (incy == 0) - incy = (mincy != 0); - - /* Get count of pixels to move each iteration and final pixel count */ - xdelta = ((clipx - oldx) * scrx) / scrollcnt; - xmod = ((clipx - oldx) * scrx) % scrollcnt; - ydelta = ((clipy - oldy) * scry) / scrollcnt; - ymod = ((clipy - oldy) * scry) % scrollcnt; - - /* Preserve the final move location */ - savex = clipx; - savey = clipy; - saveymax = clipymax; - savexmax = clipxmax; - -/* - * Set clipping rectangle to be just the region that will be exposed so - * that drawing will be faster - */ -#if 0 /* Doesn't seem to work quite the way it should */ - /* In some cases hero is 'centered' offscreen */ - if( xdelta < 0 ) - { - clipx = oldx; - clipxmax = clipx + incx; - } - else if( xdelta > 0 ) - { - clipxmax = oldxmax; - clipx = clipxmax - incx; - } - else - { - clipx = oldx; - clipxmax = oldxmax; - } - - if( ydelta < 0 ) - { - clipy = oldy; - clipymax = clipy + incy; - } - else if( ydelta > 0 ) - { - clipymax = oldymax; - clipy = clipymax - incy; - } - else - { - clipy = oldy; - clipymax = oldymax; - } -#endif - /* Now, in scrollcnt moves, move the picture toward the final view */ - for (i = 0; i < scrollcnt; ++i) { -#ifdef DISPMAP - if (i == scrollcnt - 1 && (xmod != 0 || ymod != 0) - && (xdelta != 0 || ydelta != 0)) { - incx += (clipx - oldx) % scrollcnt; - incy += (clipy - oldy) % scrollcnt; - xdelta += xmod; - ydelta += ymod; + /* Large jumps (teleport, level change): just redraw everything. */ + if (abs(dx) > halfW || abs(dy) > halfH + || scrollcnt != SCROLLCNT) { + { + int savedAPen = rp->FgPen; + int savedDrMd = rp->DrawMode; + SetAPen(rp, amii_otherBPen); + SetDrMd(rp, JAM1); + RectFill(rp, w->BorderLeft, w->BorderTop, + w->Width - w->BorderRight - 1, + w->Height - w->BorderBottom - 1); + SetAPen(rp, savedAPen); + SetDrMd(rp, savedDrMd); } -#endif - /* Scroll the raster if we are scrolling */ - if (xdelta != 0 || ydelta != 0) { - ScrollRaster(rp, xdelta, ydelta, w->BorderLeft, w->BorderTop, - w->Width - w->BorderRight - 1, - w->Height - w->BorderBottom - 1); + redraw_map(FALSE); + } else { + /* Flush pending glyphs — they reference old clip coords */ + flush_glyph_buffer(w); - if (mincx == 0) - incx = 0; - else - mincx -= incx; + /* Hardware-blit the viewport by the scroll delta */ + ScrollRaster(rp, dx * scrx, dy * scry, + w->BorderLeft, w->BorderTop, + w->Width - w->BorderRight - 1, + w->Height - w->BorderBottom - 1); - clipx += incx; - clipxmax += incx; + /* Redraw only the exposed strip(s) */ + if (dx > 0) + redraw_map_region(clipxmax - dx, clipy, + clipxmax, clipymax - 1); + else if (dx < 0) + redraw_map_region(clipx, clipy, + clipx - dx, clipymax - 1); - if (mincy == 0) - incy = 0; - else - mincy -= incy; + if (dy > 0) + redraw_map_region(clipx, clipymax - dy, + clipxmax, clipymax - 1); + else if (dy < 0) + redraw_map_region(clipx, clipy, + clipxmax, clipy - dy - 1); - clipy += incy; - clipymax += incy; - - /* Draw the exposed portion */ - redraw_map(); - flush_glyph_buffer(amii_wins[WIN_MAP]->win); - } + flush_glyph_buffer(w); } - - clipx = savex; - clipy = savey; - clipymax = saveymax; - clipxmax = savexmax; -#endif - redraw_map(); - flush_glyph_buffer(amii_wins[WIN_MAP]->win); } reclip = 0; #endif } void -flushIDCMP(port) -struct MsgPort *port; +flushIDCMP(struct MsgPort *port) { struct Message *msg; while (msg = GetMsg(port)) diff --git a/outdated/sys/amiga/winkey.c b/sys/amiga/winkey.c similarity index 89% rename from outdated/sys/amiga/winkey.c rename to sys/amiga/winkey.c index 9534c0c6d..f2686bfde 100644 --- a/outdated/sys/amiga/winkey.c +++ b/sys/amiga/winkey.c @@ -12,12 +12,14 @@ #include "winproto.h" #endif -amii_nh_poskey(x, y, mod) int *x, *y, *mod; +int +amii_nh_poskey(coordxy *x, coordxy *y, int *mod) { struct amii_WinDesc *cw; WETYPE type; struct RastPort *rp; struct Window *w; + /* No entry log for nh_poskey -- too noisy (called constantly) */ if (cw = amii_wins[WIN_MESSAGE]) { cw->wflags &= ~FLMAP_SKIP; @@ -59,10 +61,11 @@ amii_nh_poskey(x, y, mod) int *x, *y, *mod; } int -amii_nhgetch() +amii_nhgetch(void) { int ch; struct amii_WinDesc *cw = amii_wins[WIN_MESSAGE]; + /* No entry log for nhgetch -- too noisy (called constantly) */ if (WIN_MAP != WIN_ERR && amii_wins[WIN_MAP]) { cursor_on(WIN_MAP); @@ -75,15 +78,15 @@ amii_nhgetch() } void -amii_get_nh_event() +amii_get_nh_event(void) { /* nothing now - later I have no idea. Is this just a Mac hook? */ } void -amii_getret() +amii_getret(void) { - register int c; + int c; raw_print(""); raw_print("Press Return..."); diff --git a/outdated/sys/amiga/winproto.h b/sys/amiga/winproto.h similarity index 88% rename from outdated/sys/amiga/winproto.h rename to sys/amiga/winproto.h index efa1e9b58..d855e50fe 100644 --- a/outdated/sys/amiga/winproto.h +++ b/sys/amiga/winproto.h @@ -8,10 +8,9 @@ void EditClipping(void); void DrawCol(struct Window *w, int idx, UWORD *colors); void DispCol(struct Window *w, int idx, UWORD *colors); void amii_change_color(int, long, int); -char *amii_get_color_string(); +char *amii_get_color_string(void); void amii_getlin(const char *prompt, char *bufp); void getlind(const char *prompt, char *bufp, const char *dflt); -char *amii_get_color_string(void); int filecopy(char *from, char *to); char *basename(char *str); char *dirname(char *str); @@ -33,15 +32,15 @@ void amii_scrollmsg(register struct Window *w, register struct amii_WinDesc *cw); /* winkey.c */ -int amii_nh_poskey(int *x, int *y, int *mod); +int amii_nh_poskey(coordxy *x, coordxy *y, int *mod); int amii_nhgetch(void); void amii_get_nh_event(void); void amii_getret(void); /* winmenu.c */ void amii_start_menu(winid window, unsigned long); -void amii_add_menu(winid, int, const anything *, CHAR_P, CHAR_P, int, - const char *, unsigned int); +void amii_add_menu(winid, const glyph_info *, const anything *, CHAR_P, CHAR_P, + int, int, const char *, unsigned int); void amii_end_menu(winid, const char *); int amii_select_menu(winid, int, menu_item **); int DoMenuScroll(int win, int blocking, int how, menu_item **); @@ -55,7 +54,6 @@ struct Window *OpenShWindow(struct NewWindow *nw); void CloseShWindow(struct Window *win); int ConvertKey(struct IntuiMessage *message); int kbhit(void); -int kbhit(void); int amikbhit(void); int WindowGetchar(void); WETYPE WindowGetevent(void); @@ -101,19 +99,19 @@ void amii_resume_nhwindows(void); void amii_bell(void); void removetopl(int cnt); void port_help(void); -void amii_print_glyph(winid win, coordxy x, coordxy y, int glyph, int bkglyph); +void amii_print_glyph(winid win, coordxy x, coordxy y, const glyph_info *glyphinfo, const glyph_info *bkglyphinfo); void amii_raw_print(const char *s); void amii_raw_print_bold(const char *s); -void amii_update_inventory(void); +void amii_update_inventory(int); void amii_mark_synch(void); void amii_wait_synch(void); void amii_setclipped(void); void amii_cliparound(int x, int y); void amii_set_text_font(char *font, int size); -BitMapHeader ReadImageFiles(char **, struct BitMap **, char **); +BitMapHeader ReadImageFile(const char *, struct BitMap **); +void FreeImageFile(struct BitMap **); BitMapHeader ReadTileImageFiles(void); -void FreeImageFiles(char **, struct BitMap **); -void FreeTileImageFiles(); +void FreeTileImageFiles(void); /* winami.c */ #ifdef SHAREDLIB @@ -124,12 +122,10 @@ void amii_askname(void); void amii_player_selection(void); void RandomWindow(char *name); int amii_get_ext_cmd(void); -char amii_yn_function(const char *prompt, const char *resp, char def); char amii_yn_function(const char *query, const char *resp, char def); void amii_display_file(const char *fn, boolean complain); void SetBorder(struct Gadget *gd); -void *malloc(register unsigned size); -void free(void *q); +/* malloc/free provided by stdlib.h */ #ifdef SHAREDLIB /* amilib.c */ @@ -141,6 +137,8 @@ void setup_librefs(WinamiBASE *base); void Abort(long rc); #endif +win_request_info *amii_ctrl_nhwindow(winid, int, win_request_info *); + /* amirip.c */ void amii_outrip(winid tmpwin, int how, time_t when); @@ -151,4 +149,3 @@ int GlyphToIcon(int glyph); void dispmap_sanity(void); int dispmap_sanity1(int); #endif -void FreeTileImageFiles(void); diff --git a/outdated/sys/amiga/winreq.c b/sys/amiga/winreq.c similarity index 95% rename from outdated/sys/amiga/winreq.c rename to sys/amiga/winreq.c index af4c303de..910adeb86 100644 --- a/outdated/sys/amiga/winreq.c +++ b/sys/amiga/winreq.c @@ -67,16 +67,16 @@ struct NewWindow StrWindow = { void ClearCol(struct Window *w); void -EditColor() +EditColor(void) { - extern char configfile[]; + const char *configfile = get_configfile(); int i, done = 0, okay = 0; long code, qual, class; - register struct Gadget *gd, *dgad; - register struct Window *nw; - register struct IntuiMessage *imsg; - register struct PropInfo *pip; - register struct Screen *scrn; + struct Gadget *gd, *dgad; + struct Window *nw; + struct IntuiMessage *imsg; + struct PropInfo *pip; + struct Screen *scrn; long aidx; int msx, msy; int curcol = 0, drag = 0; @@ -128,11 +128,7 @@ EditColor() #ifdef INTUI_NEW_LOOK Col_NewWindowStructure1.Extension = wintags; Col_NewWindowStructure1.Flags |= WFLG_NW_EXTENDED; -#ifdef __GNUC__ fillhook.h_Entry = (void *) &LayerFillHook; -#else - fillhook.h_Entry = (ULONG (*) ()) LayerFillHook; -#endif fillhook.h_Data = (void *) -2; fillhook.h_SubEntry = 0; #endif @@ -248,7 +244,7 @@ EditColor() for (i = 0; i < amii_numcolors; ++i) { fprintf(nfp, "%03x", colors[i]); if ((i + 1) < amii_numcolors) - putc(',', nfp); + putc('/', nfp); } putc('\n', nfp); } @@ -356,11 +352,11 @@ EditClipping(void) char buf[40]; int done = 0, okay = 0; long code, qual, class; - register struct Gadget *gd, *dgad; - register struct Window *nw; - register struct IntuiMessage *imsg; - register struct PropInfo *pip; - register struct Screen *scrn; + struct Gadget *gd, *dgad; + struct Window *nw; + struct IntuiMessage *imsg; + struct PropInfo *pip; + struct Screen *scrn; long aidx; int lmxsize = mxsize, lmysize = mysize; int lxclipbord = xclipbord, lyclipbord = yclipbord; @@ -388,11 +384,7 @@ EditClipping(void) #ifdef INTUI_NEW_LOOK ClipNewWindowStructure1.Extension = wintags; ClipNewWindowStructure1.Flags |= WFLG_NW_EXTENDED; -#ifdef __GNUC__ fillhook.h_Entry = (void *) &LayerFillHook; -#else - fillhook.h_Entry = (ULONG (*) ()) LayerFillHook; -#endif fillhook.h_Data = (void *) -2; fillhook.h_SubEntry = 0; #endif @@ -546,8 +538,7 @@ EditClipping(void) } char * -dirname(str) -char *str; +dirname(char *str) { char *t, c; static char dir[300]; @@ -555,9 +546,9 @@ char *str; t = strrchr(str, '/'); if (!t) t = strrchr(str, ':'); - if (!t) - t = str; - else { + if (!t) { + dir[0] = '\0'; + } else { c = *t; *t = 0; strcpy(dir, str); @@ -567,8 +558,7 @@ char *str; } char * -basename(str) -char *str; +basename(char *str) { char *t; @@ -582,7 +572,8 @@ char *str; return (t); } -filecopy(from, to) char *from, *to; +int +filecopy(char *from, char *to) { char *buf; int i = 0; @@ -606,7 +597,7 @@ filecopy(from, to) char *from, *to; } /* The colornames, and the default values for the pens */ -static struct COLDEF { +struct COLDEF { char *name, *defval; }; struct COLDEF amii_colnames[AMII_MAXCOLORS] = { @@ -657,10 +648,7 @@ ClearCol(struct Window *w) } void -DrawCol(w, idx, colors) -struct Window *w; -int idx; -UWORD *colors; +DrawCol(struct Window *w, int idx, UWORD *colors) { int bxorx, bxory, bxxlen, bxylen; int i, incx, incy, r, g, b; @@ -742,10 +730,7 @@ UWORD *colors; } void -DispCol(w, idx, colors) -struct Window *w; -int idx; -UWORD *colors; +DispCol(struct Window *w, int idx, UWORD *colors) { char buf[50]; char *colname, *defval; @@ -824,26 +809,21 @@ amii_setpens(int count) /* Generate a requester for a string value. */ void -amii_getlin(prompt, bufp) -const char *prompt; -char *bufp; +amii_getlin(const char *prompt, char *bufp) { getlind(prompt, bufp, 0); } /* and with default */ void -getlind(prompt, bufp, dflt) -const char *prompt; -char *bufp; -const char *dflt; +getlind(const char *prompt, char *bufp, const char *dflt) { #ifndef TOPL_GETLINE - register struct Window *cwin; - register struct IntuiMessage *imsg; - register long class, code, qual; - register int aredone = 0; - register struct Gadget *gd; + struct Window *cwin; + struct IntuiMessage *imsg; + long class, code, qual; + int aredone = 0; + struct Gadget *gd; static int once; *StrString = 0; @@ -872,11 +852,7 @@ const char *dflt; #ifdef INTUI_NEW_LOOK StrWindow.Extension = wintags; StrWindow.Flags |= WFLG_NW_EXTENDED; -#ifdef __GNUC__ fillhook.h_Entry = (void *) &LayerFillHook; -#else - fillhook.h_Entry = (ULONG (*) ()) LayerFillHook; -#endif fillhook.h_Data = (void *) -2; fillhook.h_SubEntry = 0; #endif @@ -1006,9 +982,7 @@ const char *dflt; } void -amii_change_color(pen, val, rev) -int pen, rev; -long val; +amii_change_color(int pen, long val, int rev) { if (rev) sysflags.amii_curmap[pen] = ~val; @@ -1020,12 +994,13 @@ long val; } char * -amii_get_color_string() +amii_get_color_string(void) { int i; char s[10]; static char buf[BUFSZ]; + *buf = 0; for (i = 0; i < min(32, amii_numcolors); ++i) { sprintf(s, "%s%03lx", i ? "/" : "", (long) sysflags.amii_curmap[i]); diff --git a/outdated/sys/amiga/winstr.c b/sys/amiga/winstr.c similarity index 95% rename from outdated/sys/amiga/winstr.c rename to sys/amiga/winstr.c index ea7d65343..a3801fff1 100644 --- a/outdated/sys/amiga/winstr.c +++ b/sys/amiga/winstr.c @@ -14,10 +14,7 @@ /* Put a string into the indicated window using the indicated attribute */ void -amii_putstr(window, attr, str) -winid window; -int attr; -const char *str; +amii_putstr(winid window, int attr, const char *str) { int fudge; int len; @@ -27,6 +24,7 @@ const char *str; int i, j, n0, bottom, totalvis, wheight; static int wrapping = 0; + /* Always try to avoid a panic when there is no window */ if (window == WIN_ERR) { window = WIN_BASE; @@ -101,13 +99,13 @@ const char *str; memcpy(cw->data, &cw->data[1], (iflags.msg_history - 1) * sizeof(char *)); cw->data[iflags.msg_history - 1] = - (char *) alloc(strlen(gt.toplines) + 5); + (char *) alloc(strlen(gt.toplines) + SOFF + 4); strcpy(cw->data[i = iflags.msg_history - 1] + SOFF + (scrollmsg != 0), gt.toplines); } else { /* Otherwise, allocate a new one and copy the line in */ - cw->data[cw->maxrow] = (char *) alloc(strlen(gt.toplines) + 5); + cw->data[cw->maxrow] = (char *) alloc(strlen(gt.toplines) + SOFF + 4); strcpy(cw->data[i = cw->maxrow++] + SOFF + (scrollmsg != 0), gt.toplines); } @@ -196,7 +194,10 @@ const char *str; /* Display when beam at top to avoid flicker... */ WaitTOF(); - Text(w->RPort, (char *) str, strlen((char *) str)); + { int slen = strlen((char *) str); + if (slen > cw->cols) slen = cw->cols; + Text(w->RPort, (char *) str, slen); + } if (cw->cols > strlen(str)) TextSpaces(w->RPort, cw->cols - strlen(str)); @@ -286,9 +287,7 @@ const char *str; } void -amii_scrollmsg(w, cw) -register struct Window *w; -register struct amii_WinDesc *cw; +amii_scrollmsg(struct Window *w, struct amii_WinDesc *cw) { int bottom, wheight; @@ -311,8 +310,7 @@ register struct amii_WinDesc *cw; } int -amii_msgborder(w) -struct Window *w; +amii_msgborder(struct Window *w) { register int bottom; @@ -325,8 +323,7 @@ struct Window *w; } void -outmore(cw) -register struct amii_WinDesc *cw; +outmore(struct amii_WinDesc *cw) { struct Window *w = cw->win; @@ -359,11 +356,7 @@ register struct amii_WinDesc *cw; } void -outsubstr(cw, str, len, fudge) -register struct amii_WinDesc *cw; -char *str; -int len; -int fudge; +outsubstr(struct amii_WinDesc *cw, char *str, int len, int fudge) { struct Window *w = cw->win; @@ -387,10 +380,7 @@ int fudge; /* Put a graphics character onto the screen */ void -amii_putsym(st, i, y, c) -winid st; -int i, y; -CHAR_P c; +amii_putsym(winid st, int i, int y, CHAR_P c) { amii_curs(st, i, y); Text(amii_wins[st]->win->RPort, &c, 1); @@ -399,8 +389,7 @@ CHAR_P c; /* Add to the last line in the message window */ void -amii_addtopl(s) -const char *s; +amii_addtopl(const char *s) { register struct amii_WinDesc *cw = amii_wins[WIN_MESSAGE]; @@ -413,9 +402,7 @@ const char *s; } void -TextSpaces(rp, nr) -struct RastPort *rp; -int nr; +TextSpaces(struct RastPort *rp, int nr) { if (nr < 1) return; @@ -429,7 +416,7 @@ int nr; } void -amii_remember_topl() +amii_remember_topl(void) { /* ignore for now. I think this will be done automatically by * the code writing to the message window, but I could be wrong. @@ -437,12 +424,13 @@ amii_remember_topl() } int -amii_doprev_message() +amii_doprev_message(void) { struct amii_WinDesc *cw; struct Window *w; char *str; + if (WIN_MESSAGE == WIN_ERR || (cw = amii_wins[WIN_MESSAGE]) == NULL || (w = cw->win) == NULL) { panic(winpanicstr, WIN_MESSAGE, "doprev_message"); diff --git a/sys/amiga/xpm2iff_host.c b/sys/amiga/xpm2iff_host.c new file mode 100644 index 000000000..589d4f56f --- /dev/null +++ b/sys/amiga/xpm2iff_host.c @@ -0,0 +1,346 @@ +/* xpm2iff_host.c - host-side .xpm -> Amiga BMAP IFF converter. + * Copyright (c) 2026 by Ingo Paschke. + * NetHack may be freely redistributed. See license for details. + * + * Adapted from sys/amiga/xpm2iff.c, Copyright (c) 1995 by Gregg Wonderly. + * Rewritten for host-side cross-compilation using POSIX file I/O with + * explicit big-endian output instead of AmigaOS IFFParse library calls. + * + * Input: an XPM2 file, 1 char per pixel. + * Output: a BMAP IFF file readable by sys/amiga/winchar.c (tomb.iff). + */ + +#include +#include +#include +#include +#include + +/* ------------------------------------------------------------------ + * XPM screen descriptor and color translation table + * ------------------------------------------------------------------ */ + +static struct { + int Width; + int Height; + int Colors; + int BytesPerRow; +} XpmScreen; + +/* Translation table: indexed by the single XPM character code. + * slot = output palette index (0-based) + * flag = 1 if this entry is valid + * r,g,b = RGB components (0-255) */ +static struct { + unsigned char flag; + unsigned char r, g, b; + int slot; +} ttable[256]; + +/* ------------------------------------------------------------------ + * XPM parsing + * Adapted directly from sys/amiga/xpm2iff.c (already POSIX). + * ------------------------------------------------------------------ */ + +static FILE *xpmfh; + +#define XBUFSZ 2048 +static char xbuf[XBUFSZ]; + +/* Read the next quoted line from the XPM file. + * Returns a pointer to the content between the first pair of double-quotes, + * or NULL on EOF. Trailing ", and whitespace are stripped. */ +static char * +xpmgetline(void) +{ + char *bp; + do { + if (fgets(xbuf, XBUFSZ, xpmfh) == NULL) + return NULL; + } while (xbuf[0] != '"'); + + /* strip trailing <",> and whitespace */ + for (bp = xbuf; *bp; bp++) + ; + bp--; + while (isspace((unsigned char)*bp)) + bp--; + if (*bp == ',') + bp--; + if (*bp == '"') + bp--; + bp++; + *bp = '\0'; + + return &xbuf[1]; +} + +/* Open an XPM file and parse its header + color table. + * Populates XpmScreen and ttable[]. + * Returns 1 on success, 0 on failure. */ +static int +fopen_xpm_file(const char *fn) +{ + int temp; + char *xb; + + xpmfh = fopen(fn, "r"); + if (!xpmfh) + return 0; + + /* read dimensions header: "W H Colors 1" */ + xb = xpmgetline(); + if (!xb) + return 0; + if (sscanf(xb, "%d %d %d %d", + &XpmScreen.Width, &XpmScreen.Height, + &XpmScreen.Colors, &temp) != 4) + return 0; + if (temp != 1) { + fprintf(stderr, "xpm2iff_host: only 1 char/pixel XPM files supported\n"); + return 0; + } + + /* read color map: "%c c #rrggbb" */ + { + int ccount = 0; + while (ccount < XpmScreen.Colors) { + char idx; + int r, g, b; + xb = xpmgetline(); + if (!xb) + return 0; + if (sscanf(xb, "%c c #%2x%2x%2x", &idx, &r, &g, &b) != 4) { + fprintf(stderr, "xpm2iff_host: bad color entry: %s\n", xb); + return 0; + } + ttable[(unsigned char)idx].flag = 1; + ttable[(unsigned char)idx].r = (unsigned char)r; + ttable[(unsigned char)idx].g = (unsigned char)g; + ttable[(unsigned char)idx].b = (unsigned char)b; + ttable[(unsigned char)idx].slot = ccount; + ccount++; + } + } + return 1; +} + +/* ------------------------------------------------------------------ + * Bitplane packing + * ------------------------------------------------------------------ */ + +static char **planes; + +#define SETBIT(plane, plane_offset, col, value) \ + do { \ + if (value) \ + planes[plane][plane_offset + ((col) / 8)] \ + |= (char)(1 << (7 - ((col) & 7))); \ + } while (0) + +static void +conv_image(int nplanes) +{ + int row, col, planeno; + + for (row = 0; row < XpmScreen.Height; row++) { + char *xb = xpmgetline(); + int plane_offset; + if (!xb) + return; + plane_offset = row * XpmScreen.BytesPerRow; + for (col = 0; col < XpmScreen.Width; col++) { + int color = (unsigned char)xb[col]; + int slot; + if (!ttable[color].flag) { + fprintf(stderr, "xpm2iff_host: bad image data at row %d col %d\n", + row, col); + continue; + } + slot = ttable[color].slot; + for (planeno = 0; planeno < nplanes; planeno++) + SETBIT(planeno, plane_offset, col, slot & (1 << planeno)); + } + } +} + +/* ------------------------------------------------------------------ + * Big-endian IFF output helpers + * ------------------------------------------------------------------ */ + +static FILE *iff_out; + +static void +wr32(uint32_t v) +{ + fputc((v >> 24) & 0xff, iff_out); + fputc((v >> 16) & 0xff, iff_out); + fputc((v >> 8) & 0xff, iff_out); + fputc( v & 0xff, iff_out); +} + +static void +write_chunk(const char *id, const void *data, uint32_t size) +{ + fwrite(id, 1, 4, iff_out); + wr32(size); + fwrite(data, 1, size, iff_out); + if (size & 1) + fputc(0, iff_out); +} + +/* ------------------------------------------------------------------ + * main + * ------------------------------------------------------------------ */ + +int +main(int argc, char **argv) +{ + int i, nplanes, colors; + uint32_t pbytes, plne_size, form_size; + + if (argc != 3) { + fprintf(stderr, "Usage: %s source.xpm destination.iff\n", argv[0]); + return 1; + } + + if (!fopen_xpm_file(argv[1])) { + fprintf(stderr, "%s: failed to open or parse XPM file\n", argv[1]); + return 1; + } + + /* nplanes = ceil(log2(Colors)) */ + nplanes = 0; + i = XpmScreen.Colors - 1; + while (i > 0) { nplanes++; i >>= 1; } + + colors = 1 << nplanes; + + XpmScreen.BytesPerRow = ((XpmScreen.Width + 15) / 16) * 2; + pbytes = (uint32_t)XpmScreen.BytesPerRow * (uint32_t)XpmScreen.Height; + + /* Allocate zero-initialised bitplane buffers */ + planes = malloc(nplanes * sizeof(char *)); + if (!planes) { perror("malloc"); return 1; } + for (i = 0; i < nplanes; ++i) { + planes[i] = calloc(1, pbytes); + if (!planes[i]) { perror("calloc"); return 1; } + } + + /* Pack pixel data into bitplanes */ + conv_image(nplanes); + fclose(xpmfh); + + /* Open output IFF file */ + iff_out = fopen(argv[2], "wb"); + if (!iff_out) { perror(argv[2]); return 1; } + + /* Pre-compute FORM size: + * 4 (BMAP type tag) + * 8 + 20 (BMHD) + * 8 + 4 (CAMG) + * 8 + colors*3 (CMAP; colors is a power of 2, so colors*3 is even) + * 8 + 28 (PDAT: 7 x uint32_t) + * 8 + nplanes*pbytes (PLNE; pbytes is always even) + */ + plne_size = (uint32_t)nplanes * pbytes; + form_size = 4 + + (8 + 20) + + (8 + 4) + + (8 + (uint32_t)colors * 3) + + (8 + 28) + + (8 + plne_size); + + /* FORM header */ + fwrite("FORM", 1, 4, iff_out); + wr32(form_size); + fwrite("BMAP", 1, 4, iff_out); + + /* BMHD chunk */ + { + uint8_t bmhd[20]; + uint8_t *p = bmhd; + uint16_t w = (uint16_t)XpmScreen.Width; + uint16_t h = (uint16_t)XpmScreen.Height; + + p[0] = w >> 8; p[1] = w & 0xff; p += 2; /* w */ + p[0] = h >> 8; p[1] = h & 0xff; p += 2; /* h */ + memset(p, 0, 4); p += 4; /* x=0, y=0 */ + *p++ = (uint8_t)nplanes; /* nPlanes */ + *p++ = 0; /* masking: none */ + *p++ = 0; /* compression: none */ + *p++ = 0; /* reserved1 */ + p[0] = 0; p[1] = 0; p += 2; /* transparentColor */ + *p++ = 100; /* xAspect */ + *p++ = 100; /* yAspect */ + p[0] = 0; p[1] = 0; p += 2; /* pageWidth (not used) */ + p[0] = 0; p[1] = 0; p += 2; /* pageHeight (not used) */ + + write_chunk("BMHD", bmhd, 20); + } + + /* CAMG chunk: HIRES | LACE = 0x00008004 */ + { + uint8_t camg[4] = { 0x00, 0x00, 0x80, 0x04 }; + write_chunk("CAMG", camg, 4); + } + + /* CMAP chunk: built from ttable (no color reordering for XPM images) */ + { + uint8_t *cmap = calloc(colors, 3); + if (!cmap) { perror("calloc"); return 1; } + for (i = 0; i < 256; i++) { + if (ttable[i].flag) { + int s = ttable[i].slot; + cmap[s * 3 + 0] = ttable[i].r; + cmap[s * 3 + 1] = ttable[i].g; + cmap[s * 3 + 2] = ttable[i].b; + } + } + write_chunk("CMAP", cmap, (uint32_t)colors * 3); + free(cmap); + } + + /* PDAT chunk: 7 x uint32_t big-endian + * nplanes, pbytes, across=0, down=0, npics=1, xsize=Width, ysize=Height */ + { + uint32_t vals[7]; + uint8_t pdat[28]; + uint8_t *p = pdat; + + vals[0] = (uint32_t)nplanes; + vals[1] = pbytes; + vals[2] = 0; /* across: not a tile sheet */ + vals[3] = 0; /* down */ + vals[4] = 1; /* npics */ + vals[5] = (uint32_t)XpmScreen.Width; + vals[6] = (uint32_t)XpmScreen.Height; + + for (i = 0; i < 7; i++) { + p[0] = (vals[i] >> 24) & 0xff; + p[1] = (vals[i] >> 16) & 0xff; + p[2] = (vals[i] >> 8) & 0xff; + p[3] = vals[i] & 0xff; + p += 4; + } + write_chunk("PDAT", pdat, 28); + } + + /* PLNE chunk: concatenated bitplane data */ + fwrite("PLNE", 1, 4, iff_out); + wr32(plne_size); + for (i = 0; i < nplanes; ++i) + fwrite(planes[i], 1, pbytes, iff_out); + if (plne_size & 1) + fputc(0, iff_out); + + fclose(iff_out); + + for (i = 0; i < nplanes; ++i) free(planes[i]); + free(planes); + + printf("tomb.iff: %dx%d, %d colors (%d planes), %u bytes/plane\n", + XpmScreen.Width, XpmScreen.Height, colors, nplanes, + (unsigned)pbytes); + return 0; +} diff --git a/sys/libnh/libnhmain.c b/sys/libnh/libnhmain.c index 562de1c78..44765987e 100644 --- a/sys/libnh/libnhmain.c +++ b/sys/libnh/libnhmain.c @@ -810,7 +810,7 @@ EM_JS(void, js_helpers_init, (), { // used by update_inventory function displayInventory() { // Asyncify.handleAsync(async () => { - return _display_inventory(0, 0); + return _repopulate_perminvent(); // }); } diff --git a/sys/msdos/Install.dos b/sys/msdos/Install.dos index 10b94888d..e60eb72b3 100644 --- a/sys/msdos/Install.dos +++ b/sys/msdos/Install.dos @@ -42,7 +42,7 @@ II. There once was a time when people built NetHack right on their DOS machine. a DOS-extender (for including in msdos packaging) from: http://sandmann.dotster.com/cwsdpmi/csdpmi7b.zip and Lua from: - http://www.lua.org/ftp/lua-5.4.6.tar.gz + http://www.lua.org/ftp/lua-5.4.8.tar.gz If you want the DOSVGA build, to support higher resolutions and Unicode symbols, you also need: @@ -123,7 +123,7 @@ Appendix B - Common Difficulties Encountered For example: make CROSS_TO_MSDOS=1 package - ( cd src ; make LUA_VERSION=5.4.6 nethack ) + ( cd src ; make LUA_VERSION=5.4.8 nethack ) make[1]: Entering directory '/home/johndoe/NHsource/src' mkdir -p ../targets/msdos ; make ../targets/msdos/exceptn.o ; make[2]: Entering directory '/home/johndoe/NHsource/src' diff --git a/sys/msdos/Makefile.GCC b/sys/msdos/Makefile.GCC index 069b2679a..782e1cd5c 100644 --- a/sys/msdos/Makefile.GCC +++ b/sys/msdos/Makefile.GCC @@ -56,7 +56,7 @@ endif # Location of LUA # # Original source needs to be obtained from: -# http://www.lua.org/ftp/lua-5.4.6.tar.gz +# http://www.lua.org/ftp/lua-5.4.8.tar.gz # # This build assumes that the LUA sources are located # at the specified location. If they are actually elsewhere @@ -276,27 +276,27 @@ VOBJ02 = $(O)ball.o $(O)bones.o $(O)botl.o $(O)calendar.o $(O)cfgfile VOBJ03 = $(O)cmd.o $(O)coloratt.o $(O)date.o $(O)dbridge.o $(O)decl.o VOBJ04 = $(O)detect.o $(O)dig.o $(O)display.o $(O)do.o $(O)do_name.o VOBJ05 = $(O)do_wear.o $(O)dog.o $(O)dogmove.o $(O)dokick.o $(O)dothrow.o -VOBJ06 = $(O)drawing.o $(O)dungeon.o $(O)eat.o $(O)end.o $(O)engrave.o -VOBJ07 = $(O)exper.o $(O)explode.o $(O)extralev.o $(O)files.o $(O)fountain.o -VOBJ08 = $(O)getpos.o $(O)glyphs.o $(O)getline.o $(O)hack.o $(O)hacklib.o -VOBJ09 = $(O)insight.o $(O)invent.o $(O)isaac64.o $(O)lock.o $(O)mail.o -VOBJ10 = $(O)main.o $(O)makemon.o $(O)mcastu.o $(O)mdlib.o $(O)mhitm.o -VOBJ11 = $(O)mhitu.o $(O)minion.o $(O)mkmap.o $(O)mklev.o $(O)mkmaze.o -VOBJ12 = $(O)mkobj.o $(O)mkroom.o $(O)mon.o $(O)mondata.o $(O)monmove.o -VOBJ13 = $(O)monst.o $(O)mplayer.o $(O)mthrowu.o $(O)muse.o $(O)music.o -VOBJ14 = $(O)o_init.o $(O)objects.o $(O)objnam.o $(O)options.o $(O)pickup.o -VOBJ15 = $(O)pline.o $(O)polyself.o $(O)potion.o $(O)quest.o $(O)questpgr.o -VOBJ16 = $(O)pager.o $(O)pray.o $(O)priest.o $(O)read.o $(O)rect.o -VOBJ17 = $(O)region.o $(O)report.o $(O)restore.o $(O)rip.o $(O)rnd.o -VOBJ18 = $(O)role.o $(O)rumors.o $(O)save.o $(O)selvar.o $(O)sfstruct.o -VOBJ19 = $(O)shk.o $(O)shknam.o $(O)sit.o $(O)sounds.o $(O)sp_lev.o -VOBJ20 = $(O)spell.o $(O)stairs.o $(O)steal.o $(O)steed.o $(O)symbols.o -VOBJ21 = $(O)sys.o $(O)teleport.o $(O)strutil.o $(O)termcap.o $(O)timeout.o -VOBJ22 = $(O)topl.o $(O)topten.o $(O)trap.o $(O)u_init.o $(O)uhitm.o -VOBJ23 = $(O)utf8map.o $(O)vault.o $(O)track.o $(O)vision.o $(O)weapon.o -VOBJ24 = $(O)were.o $(O)wield.o $(O)windows.o $(O)wintty.o $(O)wizard.o -VOBJ25 = $(O)wizcmds.o $(O)worm.o $(O)worn.o $(O)write.o $(O)zap.o -VOBJ26 = $(O)light.o $(O)dlb.o $(REGEX) +VOBJ06 = $(O)drawing.o $(O)dungeon.o $(O)earlyarg.o $(O)eat.o $(O)end.o +VOBJ07 = $(O)engrave.o $(O)exper.o $(O)explode.o $(O)extralev.o $(O)files.o +VOBJ08 = $(O)fountain.o $(O)getpos.o $(O)glyphs.o $(O)getline.o $(O)hack.o +VOBJ09 = $(O)hacklib.o $(O)iactions.o $(O)insight.o $(O)invent.o $(O)isaac64.o +VOBJ10 = $(O)lock.o $(O)mail.o $(O)main.o $(O)makemon.o $(O)mcastu.o +VOBJ11 = $(O)mdlib.o $(O)mhitm.o $(O)mhitu.o $(O)minion.o $(O)mkmap.o +VOBJ12 = $(O)mklev.o $(O)mkmaze.o $(O)mkobj.o $(O)mkroom.o $(O)mon.o +VOBJ13 = $(O)mondata.o $(O)monmove.o $(O)monst.o $(O)mplayer.o $(O)mthrowu.o +VOBJ14 = $(O)muse.o $(O)music.o $(O)o_init.o $(O)objects.o $(O)objnam.o +VOBJ15 = $(O)options.o $(O)pickup.o $(O)pline.o $(O)polyself.o $(O)potion.o +VOBJ16 = $(O)quest.o $(O)questpgr.o $(O)pager.o $(O)pray.o $(O)priest.o +VOBJ17 = $(O)read.o $(O)rect.o $(O)region.o $(O)report.o $(O)restore.o +VOBJ18 = $(O)rip.o $(O)rnd.o $(O)role.o $(O)rumors.o $(O)save.o +VOBJ19 = $(O)selvar.o $(O)sfstruct.o $(O)shk.o $(O)shknam.o $(O)sit.o +VOBJ20 = $(O)sounds.o $(O)sp_lev.o $(O)spell.o $(O)stairs.o $(O)steal.o +VOBJ21 = $(O)steed.o $(O)symbols.o $(O)sys.o $(O)teleport.o $(O)strutil.o +VOBJ22 = $(O)termcap.o $(O)timeout.o $(O)topl.o $(O)topten.o $(O)trap.o +VOBJ23 = $(O)u_init.o $(O)uhitm.o $(O)utf8map.o $(O)vault.o $(O)track.o +VOBJ24 = $(O)vision.o $(O)weapon.o $(O)were.o $(O)wield.o $(O)windows.o +VOBJ25 = $(O)wintty.o $(O)wizard.o $(O)wizcmds.o $(O)worm.o $(O)worn.o +VOBJ26 = $(O)write.o $(O)zap.o $(O)light.o $(O)dlb.o $(REGEX) SOBJ = $(O)msdos.o $(O)pcsys.o $(O)tty.o $(O)unix.o \ $(O)video.o $(O)vidtxt.o $(O)pckeys.o @@ -324,7 +324,7 @@ ALLOBJ = $(VOBJ) $(SOBJ) $(TILOBJ) $(TILOBJ2) $(VVOBJ) #===============-================================================= # LUA library -# Source from http://www.lua.org/ftp/lua-5.4.6.tar.gz +# Source from http://www.lua.org/ftp/lua-5.4.8.tar.gz #================================================================= LUASRC = $(LUATOP)/src @@ -416,6 +416,7 @@ GLOBAL_H = $(PCCONF_H) $(INCL)/coord.h $(INCL)/global.h HACK_H = $(CONFIG_H) $(INCL)/context.h $(DUNGEON_H) \ $(DECL_H) $(DISPLAY_H) $(INCL)/sym.h \ $(INCL)/defsym.h $(INCL)/mkroom.h $(INCL)/objclass.h \ + $(INCL)/mcastu.h \ $(INCL)/trap.h $(INCL)/flag.h $(RM_H) \ $(INCL)/vision.h $(INCL)/wintype.h $(INCL)/engrave.h \ $(INCL)/rect.h $(INCL)/hack.h $(REGION_H) \ @@ -1388,6 +1389,7 @@ $(TARGETPFX)drawing.o: drawing.c $(CONFIG_H) ../include/color.h \ ../include/objects.h ../include/wintype.h ../include/sym.h $(TARGETPFX)dungeon.o: dungeon.c $(HACK_H) ../include/dgn_file.h \ ../include/dlb.h +$(TARGETPFX)earlyarg.o: earlyarg.c $(HACK_H) $(TARGETPFX)eat.o: eat.c $(HACK_H) $(TARGETPFX)end.o: end.c $(HACK_H) ../include/dlb.h $(TARGETPFX)engrave.o: engrave.c $(HACK_H) @@ -1399,6 +1401,7 @@ $(TARGETPFX)files.o: files.c $(HACK_H) ../include/dlb.h ../include/wintty.h \ $(TARGETPFX)fountain.o: fountain.c $(HACK_H) $(TARGETPFX)hack.o: hack.c $(HACK_H) $(TARGETPFX)hacklib.o: hacklib.c $(HACK_H) +$(TARGETPFX)iactions.o: iactions.c $(HACK_H) $(TARGETPFX)insight.o: insight.c $(HACK_H) $(TARGETPFX)invent.o: invent.c $(HACK_H) $(TARGETPFX)isaac64.o: isaac64.c $(CONFIG_H) ../include/isaac64.h @@ -1406,7 +1409,7 @@ $(TARGETPFX)light.o: light.c $(HACK_H) $(TARGETPFX)lock.o: lock.c $(HACK_H) $(TARGETPFX)mail.o: mail.c $(HACK_H) ../include/mail.h $(TARGETPFX)makemon.o: makemon.c $(HACK_H) -$(TARGETPFX)mcastu.o: mcastu.c $(HACK_H) +$(TARGETPFX)mcastu.o: mcastu.c $(HACK_H) ../include/mcastu.h $(TARGETPFX)mdlib.o: mdlib.c $(CONFIG_H) ../include/permonst.h \ ../include/align.h ../include/monattk.h ../include/monflag.h \ ../include/monsters.h ../include/objclass.h \ diff --git a/sys/msdos/fetch-cross-compiler.sh b/sys/msdos/fetch-cross-compiler.sh index 36558a00c..037d3aaf3 100755 --- a/sys/msdos/fetch-cross-compiler.sh +++ b/sys/msdos/fetch-cross-compiler.sh @@ -15,7 +15,7 @@ if [ -z "$GCCVER" ]; then fi if [ -z "$LUA_VERSION" ]; then - export LUA_VERSION=5.4.6 + export LUA_VERSION=5.4.8 fi if [ ! -d "$(pwd)/lib" ]; then diff --git a/sys/msdos/msdos.c b/sys/msdos/msdos.c index 8a059008d..86adbd4bb 100644 --- a/sys/msdos/msdos.c +++ b/sys/msdos/msdos.c @@ -449,7 +449,7 @@ filesize_nh(char *file) * Chdrive() changes the default drive. */ void -chdrive(char *str) +chdrive(const char *str) { #define SELECTDISK 0x0E char *ptr; diff --git a/sys/msdos/video.c b/sys/msdos/video.c index 916a3518d..57911edab 100644 --- a/sys/msdos/video.c +++ b/sys/msdos/video.c @@ -174,28 +174,28 @@ term_curs_set(int visibility) return; if (!visibility) { - if (!iflags.grmode) { + if (!iflags.grmode) { txt_hide_cursor(); #ifdef SCREEN_VGA - } else if (iflags.usevga) { - vga_hide_cursor(); + } else if (iflags.usevga) { + vga_hide_cursor(); #endif #ifdef SCREEN_VESA - } else if (iflags.usevesa) { - vesa_hide_cursor(); - } + } else if (iflags.usevesa) { + vesa_hide_cursor(); + } #endif } else if (visibility) { - if (!iflags.grmode) { + if (!iflags.grmode) { txt_show_cursor(); #ifdef SCREEN_VGA - } else if (iflags.usevga) { + } else if (iflags.usevga) { vga_show_cursor(); #endif #ifdef SCREEN_VESA - } else if (iflags.usevesa) { + } else if (iflags.usevesa) { vesa_show_cursor(); - } + } #endif } vis = visibility; diff --git a/sys/share/pcmain.c b/sys/share/pcmain.c index 8850e2d82..39e5aec4e 100644 --- a/sys/share/pcmain.c +++ b/sys/share/pcmain.c @@ -128,6 +128,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ choose_windows(DEFAULT_WINDOW_SYS); + #if !defined(AMIGA) && !defined(GNUDOS) /* Save current directory and make sure it gets restored when * the game is exited. @@ -147,6 +148,10 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ if (dir == (char *) 0) dir = exepath(argv[0]); #endif +#if defined(AMIGA) && defined(HACKDIR) + if (dir == (char *) 0) + dir = HACKDIR; +#endif #ifdef _MSC_VER if (IsDebuggerPresent()) { static char exepath[_MAX_PATH]; @@ -666,7 +671,7 @@ nhusage(void) #ifdef CHDIR void -chdirx(char *dir, boolean wr) +chdirx(const char *dir, boolean wr) { #ifdef AMIGA static char thisdir[] = ""; diff --git a/sys/share/uudecode.c b/sys/share/uudecode.c index 223b94031..9d3a4802b 100644 --- a/sys/share/uudecode.c +++ b/sys/share/uudecode.c @@ -43,7 +43,10 @@ * * Modified 05 Jan 2024 to avoid K&R function declarations, marked KR_PROTO. * - * $NHDT-Date: 1432512787 2015/05/25 00:13:07 $ $NHDT-Branch: master $:$NHDT-Revision: 1.7 $ + * Modified 09 Apr 2026 to change mode variable from (int) to (unsigned int) + * to match prototype of sscanf %o. + * + * $NHDT-Date: 1775744389 2026/04/09 14:19:49 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.21 $ */ #ifndef lint @@ -99,7 +102,7 @@ int main(int argc, char **argv) { FILE *in, *out; - int mode; + unsigned int mode; char dest[128]; char buf[80]; diff --git a/sys/unix/Makefile.src b/sys/unix/Makefile.src index 695b43f28..0dfa265b6 100644 --- a/sys/unix/Makefile.src +++ b/sys/unix/Makefile.src @@ -198,7 +198,7 @@ ARFLAGS = rcs # The Qt and Be window systems are written in C++, while the rest of # NetHack is standard C. If using Qt, uncomment the LINK line here to get # the C++ libraries linked in. -CXXFLAGS = $(CCXXFLAGS) -I. -I$(QTDIR)/include $(QTCXXFLAGS) +CXXFLAGS ?= $(CCXXFLAGS) -I. -I$(QTDIR)/include $(QTCXXFLAGS) CXX ?= g++ MOC ?= moc MOCPATH ?= $(QTDIR)/bin/$(MOC) @@ -502,7 +502,7 @@ TARGET_HACKLIB = $(TARGETPFX)hacklib.a MAKEDEFS = ../util/makedefs # -lm required by lua -LUA_VERSION ?=5.4.6 +LUA_VERSION ?=5.4.8 LUABASE = liblua-$(LUA_VERSION).a LUALIB = ../lib/lua/$(LUABASE) LUALIBS = $(LUALIB) -lm $(DLLIB) @@ -517,9 +517,9 @@ HACK_H = ../src/hack.h-t HACKCSRC = allmain.c alloc.c apply.c artifact.c attrib.c ball.c bones.c \ botl.c calendar.c cmd.c coloratt.c dbridge.c decl.c detect.c dig.c display.c \ dlb.c do.c do_name.c do_wear.c dog.c dogmove.c dokick.c dothrow.c \ - drawing.c dungeon.c eat.c end.c engrave.c exper.c explode.c \ + drawing.c dungeon.c earlyarg.c eat.c end.c engrave.c exper.c explode.c \ extralev.c files.c fountain.c hack.c hacklib.c \ - getpos.c glyphs.c insight.c invent.c isaac64.c light.c \ + getpos.c glyphs.c iactions.c insight.c invent.c isaac64.c light.c \ lock.c mail.c makemon.c mcastu.c mdlib.c mhitm.c \ mhitu.c minion.c mklev.c mkmap.c mkmaze.c mkobj.c mkroom.c mon.c \ mondata.c monmove.c monst.c mplayer.c mthrowu.c muse.c music.c \ @@ -571,6 +571,7 @@ HACKINCL = align.h artifact.h artilist.h attrib.h botl.h \ color.h config.h config1.h context.h coord.h cstd.h decl.h \ defsym.h display.h dlb.h dungeon.h engrave.h extern.h flag.h \ fnamesiz.h func_tab.h global.h warnings.h hack.h lint.h mextra.h \ + mcastu.h \ micro.h mfndpos.h mkroom.h monattk.h mondata.h monflag.h monst.h \ monsters.h nhmd4.h obj.h objects.h objclass.h optlist.h patchlevel.h \ pcconf.h permonst.h prop.h rect.h region.h savefile.h selvar.h sym.h \ @@ -597,21 +598,21 @@ HOBJ = $(TARGETPFX)allmain.o $(TARGETPFX)alloc.o \ $(TARGETPFX)dlb.o $(TARGETPFX)do.o $(TARGETPFX)do_name.o \ $(TARGETPFX)do_wear.o $(TARGETPFX)dog.o $(TARGETPFX)dogmove.o \ $(TARGETPFX)dokick.o $(TARGETPFX)dothrow.o $(TARGETPFX)drawing.o \ - $(TARGETPFX)dungeon.o $(TARGETPFX)eat.o $(TARGETPFX)end.o \ - $(TARGETPFX)engrave.o $(TARGETPFX)exper.o $(TARGETPFX)explode.o \ - $(TARGETPFX)extralev.o $(TARGETPFX)files.o $(TARGETPFX)fountain.o \ - $(TARGETPFX)getpos.o $(TARGETPFX)glyphs.o $(TARGETPFX)hack.o \ - $(TARGETPFX)insight.o $(TARGETPFX)invent.o $(TARGETPFX)isaac64.o \ - $(TARGETPFX)light.o $(TARGETPFX)lock.o $(TARGETPFX)mail.o \ - $(TARGETPFX)makemon.o $(TARGETPFX)mcastu.o $(TARGETPFX)mdlib.o \ - $(TARGETPFX)mhitm.o $(TARGETPFX)mhitu.o $(TARGETPFX)minion.o \ - $(TARGETPFX)mklev.o $(TARGETPFX)mkmap.o $(TARGETPFX)mkmaze.o \ - $(TARGETPFX)mkobj.o $(TARGETPFX)mkroom.o $(TARGETPFX)mon.o \ - $(TARGETPFX)mondata.o $(TARGETPFX)monmove.o $(TARGETPFX)monst.o \ - $(TARGETPFX)mplayer.o $(TARGETPFX)mthrowu.o $(TARGETPFX)muse.o \ - $(TARGETPFX)music.o $(TARGETPFX)nhlua.o $(TARGETPFX)nhlsel.o \ - $(TARGETPFX)nhlobj.o $(TARGETPFX)nhmd4.o \ - $(TARGETPFX)objects.o $(TARGETPFX)o_init.o \ + $(TARGETPFX)dungeon.o $(TARGETPFX)earlyarg.o $(TARGETPFX)eat.o \ + $(TARGETPFX)end.o $(TARGETPFX)engrave.o $(TARGETPFX)exper.o \ + $(TARGETPFX)explode.o $(TARGETPFX)extralev.o $(TARGETPFX)files.o \ + $(TARGETPFX)fountain.o $(TARGETPFX)getpos.o $(TARGETPFX)glyphs.o \ + $(TARGETPFX)hack.o $(TARGETPFX)iactions.o $(TARGETPFX)insight.o \ + $(TARGETPFX)invent.o $(TARGETPFX)isaac64.o $(TARGETPFX)light.o \ + $(TARGETPFX)lock.o $(TARGETPFX)mail.o $(TARGETPFX)makemon.o \ + $(TARGETPFX)mcastu.o $(TARGETPFX)mdlib.o $(TARGETPFX)mhitm.o \ + $(TARGETPFX)mhitu.o $(TARGETPFX)minion.o $(TARGETPFX)mklev.o \ + $(TARGETPFX)mkmap.o $(TARGETPFX)mkmaze.o $(TARGETPFX)mkobj.o \ + $(TARGETPFX)mkroom.o $(TARGETPFX)mon.o $(TARGETPFX)mondata.o \ + $(TARGETPFX)monmove.o $(TARGETPFX)monst.o $(TARGETPFX)mplayer.o \ + $(TARGETPFX)mthrowu.o $(TARGETPFX)muse.o $(TARGETPFX)music.o \ + $(TARGETPFX)nhlua.o $(TARGETPFX)nhlsel.o $(TARGETPFX)nhlobj.o \ + $(TARGETPFX)nhmd4.o $(TARGETPFX)objects.o $(TARGETPFX)o_init.o \ $(TARGETPFX)objnam.o $(TARGETPFX)options.o $(TARGETPFX)pager.o \ $(TARGETPFX)pickup.o $(TARGETPFX)pline.o $(TARGETPFX)polyself.o \ $(TARGETPFX)potion.o $(TARGETPFX)pray.o $(TARGETPFX)priest.o \ @@ -642,7 +643,11 @@ DATE_O = $(TARGETPFX)date.o all: $(GAME) @echo "" -pregame: +create_responsefiles: + echo $(CC_COMPILER_SWITCHES) >$(CC_RESPONSEFILE) + echo $(CXX_COMPILER_SWITCHES) >$(CXX_RESPONSEFILE) + +pregame: $(RESPONSEFILES) $(PREGAME) $(GAME): pregame $(MAKEDEFS) $(LUALIB) $(WAVS) $(SYSTEM) @@ -807,7 +812,8 @@ tags: $(CSOURCES) clean: -rm -f *.o $(HACK_H) $(CONFIG_H) -rm -f monstr.c vis_tab.c ../include/vis_tab.h #obsolete generated files - true; $(CLEANMORE) + -true; $(CLEANMORE) + true; $(CLEAN_CC_RESPONSEFILE) $(CLEAN_CXX_RESPONSEFILE) spotless: clean -rm -f a.out core $(HACKLIB) $(GAMEBIN) Sys* @@ -890,6 +896,7 @@ $(HACK_H): $(CONFIG_H) ../include/align.h ../include/artilist.h \ ../include/decl.h ../include/defsym.h ../include/display.h \ ../include/dungeon.h ../include/engrave.h ../include/flag.h \ ../include/hack.h ../include/lint.h ../include/mextra.h \ + ../include/mcastu.h \ ../include/mkroom.h ../include/monattk.h ../include/mondata.h \ ../include/monflag.h ../include/monst.h ../include/monsters.h \ ../include/nhlua.h ../include/obj.h ../include/objclass.h \ @@ -1158,6 +1165,7 @@ $(TARGETPFX)drawing.o: drawing.c $(CONFIG_H) ../include/defsym.h \ ../include/sym.h ../include/wintype.h $(TARGETPFX)dungeon.o: dungeon.c $(HACK_H) ../include/dgn_file.h \ ../include/dlb.h +$(TARGETPFX)earlyarg.o: earlyarg.c $(HACK_H) $(TARGETPFX)eat.o: eat.c $(HACK_H) $(TARGETPFX)end.o: end.c $(HACK_H) ../include/dlb.h $(TARGETPFX)engrave.o: engrave.c $(HACK_H) @@ -1171,6 +1179,7 @@ $(TARGETPFX)getpos.o: getpos.c $(HACK_H) $(TARGETPFX)glyphs.o: glyphs.c $(HACK_H) $(TARGETPFX)hack.o: hack.c $(HACK_H) $(TARGETPFX)hacklib.o: hacklib.c $(HACK_H) +$(TARGETPFX)iactions.o: iactions.c $(HACK_H) $(TARGETPFX)insight.o: insight.c $(HACK_H) $(TARGETPFX)invent.o: invent.c $(HACK_H) $(TARGETPFX)isaac64.o: isaac64.c $(CONFIG_H) ../include/isaac64.h @@ -1178,7 +1187,7 @@ $(TARGETPFX)light.o: light.c $(HACK_H) $(TARGETPFX)lock.o: lock.c $(HACK_H) $(TARGETPFX)mail.o: mail.c $(HACK_H) ../include/mail.h $(TARGETPFX)makemon.o: makemon.c $(HACK_H) -$(TARGETPFX)mcastu.o: mcastu.c $(HACK_H) +$(TARGETPFX)mcastu.o: mcastu.c $(HACK_H) ../include/mcastu.h $(TARGETPFX)mdlib.o: mdlib.c $(CONFIG_H) ../include/align.h \ ../include/artilist.h ../include/attrib.h \ ../include/context.h ../include/defsym.h ../include/dlb.h \ diff --git a/sys/unix/Makefile.top b/sys/unix/Makefile.top index 6e45eafa9..c108f3552 100644 --- a/sys/unix/Makefile.top +++ b/sys/unix/Makefile.top @@ -85,7 +85,7 @@ VARDAT = $(VARDATD) $(VARDATND) #CHGRP = chgrp # Lua version -LUA_VERSION = 5.4.6 +LUA_VERSION = 5.4.8 # # end of configuration @@ -440,6 +440,8 @@ update: $(PRECHECK) $(GAME) recover $(VARDAT) spec_levs sys/unix/sysconf touch $(VARDIR)/perm $(VARDIR)/record # sysconf, but only if it does not exist true; $(SYSCONFENSURE) +# other steps from hints file + true; $(POSTUPDATE) # and a reminder @echo You may also want to install the man pages via the doc Makefile. diff --git a/sys/unix/Makefile.utl b/sys/unix/Makefile.utl index 84f73b4f6..862a319da 100644 --- a/sys/unix/Makefile.utl +++ b/sys/unix/Makefile.utl @@ -394,8 +394,10 @@ $(TARGETPFX)sf-version.o: ../src/version.c $(HACK_H) $(TARGETPFX)sf-worm.o: ../src/worm.c $(HACK_H) $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/worm.c -sftags: sftags.o $(HACKLIB) - $(CLINK) $(LFLAGS) -o $@ sftags.o $(HACKLIB) $(LIBS) +sftags: sftags.o $(HACKLIB) $(TARGETPFX)sf-alloc.o $(TARGETPFX)panic.o + $(CLINK) $(LFLAGS) -o $@ sftags.o \ + $(TARGETPFX)sf-alloc.o $(TARGETPFX)panic.o \ + $(HACKLIB) $(LIBS) sftags.o: sftags.c $(HACK_H) $(CC) $(CFLAGS) -c sftags.c ../include/sfproto.h: sf.tags sftags diff --git a/sys/unix/NetHack.xcodeproj/project.pbxproj b/sys/unix/NetHack.xcodeproj/project.pbxproj index 0ba3338fb..e379e51f8 100644 --- a/sys/unix/NetHack.xcodeproj/project.pbxproj +++ b/sys/unix/NetHack.xcodeproj/project.pbxproj @@ -202,12 +202,14 @@ BAE8015627B99CAE002B3786 /* lstrlib.c in Sources */ = {isa = PBXBuildFile; fileRef = BAE8013427B99CAD002B3786 /* lstrlib.c */; }; BAE8015727B99CAE002B3786 /* lzio.c in Sources */ = {isa = PBXBuildFile; fileRef = BAE8013527B99CAD002B3786 /* lzio.c */; }; BAE8015A27B9C872002B3786 /* date.c in Sources */ = {isa = PBXBuildFile; fileRef = 5439B3BB275AADC600B8FB2F /* date.c */; }; + F50818312F6F564300D2AAFA /* iactions.c in Sources */ = {isa = PBXBuildFile; fileRef = F50818302F6F564300D2AAFA /* iactions.c */; }; F5457B212DED146B00039D83 /* hacklib.c in Sources */ = {isa = PBXBuildFile; fileRef = F5457B202DED146B00039D83 /* hacklib.c */; }; F5457B2C2DED16F200039D83 /* libhacklib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F5457B1C2DED143E00039D83 /* libhacklib.a */; }; F5457B2D2DED171800039D83 /* libhacklib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F5457B1C2DED143E00039D83 /* libhacklib.a */; }; F5857AA22DED026700A8CB4F /* version.c in Sources */ = {isa = PBXBuildFile; fileRef = 31B8A32721A238010055BD01 /* version.c */; }; F5857AA42DED032D00A8CB4F /* cfgfiles.c in Sources */ = {isa = PBXBuildFile; fileRef = F5857AA32DED032C00A8CB4F /* cfgfiles.c */; }; F5857AA62DED03BB00A8CB4F /* sfbase.c in Sources */ = {isa = PBXBuildFile; fileRef = F5857AA52DED03BB00A8CB4F /* sfbase.c */; }; + F5E66A222F7D4B30000E30B7 /* earlyarg.c in Sources */ = {isa = PBXBuildFile; fileRef = F5E66A212F7D4B30000E30B7 /* earlyarg.c */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -561,40 +563,41 @@ 54FB2B4A246310A600397C0E /* symbols.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = symbols.c; path = ../../src/symbols.c; sourceTree = ""; }; 54FCE8282223261F00F393C8 /* isaac64.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = isaac64.c; path = ../../src/isaac64.c; sourceTree = ""; }; BAE8010A27B97760002B3786 /* libnhlua.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libnhlua.a; sourceTree = BUILT_PRODUCTS_DIR; }; - BAE8011427B99CAB002B3786 /* lopcodes.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lopcodes.c; path = "../../lib/lua-5.4.6/src/lopcodes.c"; sourceTree = ""; }; - BAE8011527B99CAB002B3786 /* linit.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = linit.c; path = "../../lib/lua-5.4.6/src/linit.c"; sourceTree = ""; }; - BAE8011627B99CAB002B3786 /* lobject.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lobject.c; path = "../../lib/lua-5.4.6/src/lobject.c"; sourceTree = ""; }; - BAE8011727B99CAC002B3786 /* loslib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = loslib.c; path = "../../lib/lua-5.4.6/src/loslib.c"; sourceTree = ""; }; - BAE8011827B99CAC002B3786 /* lcorolib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lcorolib.c; path = "../../lib/lua-5.4.6/src/lcorolib.c"; sourceTree = ""; }; - BAE8011927B99CAC002B3786 /* lutf8lib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lutf8lib.c; path = "../../lib/lua-5.4.6/src/lutf8lib.c"; sourceTree = ""; }; - BAE8011A27B99CAC002B3786 /* ltable.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ltable.c; path = "../../lib/lua-5.4.6/src/ltable.c"; sourceTree = ""; }; - BAE8011B27B99CAC002B3786 /* ltablib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ltablib.c; path = "../../lib/lua-5.4.6/src/ltablib.c"; sourceTree = ""; }; - BAE8011C27B99CAC002B3786 /* llex.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = llex.c; path = "../../lib/lua-5.4.6/src/llex.c"; sourceTree = ""; }; - BAE8011D27B99CAC002B3786 /* lcode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lcode.c; path = "../../lib/lua-5.4.6/src/lcode.c"; sourceTree = ""; }; - BAE8011E27B99CAC002B3786 /* lua.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lua.c; path = "../../lib/lua-5.4.6/src/lua.c"; sourceTree = ""; }; - BAE8011F27B99CAC002B3786 /* ldo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ldo.c; path = "../../lib/lua-5.4.6/src/ldo.c"; sourceTree = ""; }; - BAE8012027B99CAC002B3786 /* ldump.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ldump.c; path = "../../lib/lua-5.4.6/src/ldump.c"; sourceTree = ""; }; - BAE8012127B99CAC002B3786 /* lparser.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lparser.c; path = "../../lib/lua-5.4.6/src/lparser.c"; sourceTree = ""; }; - BAE8012227B99CAC002B3786 /* lstate.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lstate.c; path = "../../lib/lua-5.4.6/src/lstate.c"; sourceTree = ""; }; - BAE8012327B99CAC002B3786 /* lapi.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lapi.c; path = "../../lib/lua-5.4.6/src/lapi.c"; sourceTree = ""; }; - BAE8012427B99CAC002B3786 /* ldebug.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ldebug.c; path = "../../lib/lua-5.4.6/src/ldebug.c"; sourceTree = ""; }; - BAE8012527B99CAC002B3786 /* lstring.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lstring.c; path = "../../lib/lua-5.4.6/src/lstring.c"; sourceTree = ""; }; - BAE8012627B99CAC002B3786 /* ltm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ltm.c; path = "../../lib/lua-5.4.6/src/ltm.c"; sourceTree = ""; }; - BAE8012727B99CAC002B3786 /* lmem.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lmem.c; path = "../../lib/lua-5.4.6/src/lmem.c"; sourceTree = ""; }; - BAE8012827B99CAC002B3786 /* lvm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lvm.c; path = "../../lib/lua-5.4.6/src/lvm.c"; sourceTree = ""; }; - BAE8012927B99CAC002B3786 /* lfunc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lfunc.c; path = "../../lib/lua-5.4.6/src/lfunc.c"; sourceTree = ""; }; - BAE8012A27B99CAC002B3786 /* lgc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lgc.c; path = "../../lib/lua-5.4.6/src/lgc.c"; sourceTree = ""; }; - BAE8012B27B99CAD002B3786 /* lundump.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lundump.c; path = "../../lib/lua-5.4.6/src/lundump.c"; sourceTree = ""; }; - BAE8012C27B99CAD002B3786 /* lbaselib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lbaselib.c; path = "../../lib/lua-5.4.6/src/lbaselib.c"; sourceTree = ""; }; - BAE8012D27B99CAD002B3786 /* ldblib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ldblib.c; path = "../../lib/lua-5.4.6/src/ldblib.c"; sourceTree = ""; }; - BAE8012E27B99CAD002B3786 /* lauxlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lauxlib.c; path = "../../lib/lua-5.4.6/src/lauxlib.c"; sourceTree = ""; }; - BAE8012F27B99CAD002B3786 /* liolib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = liolib.c; path = "../../lib/lua-5.4.6/src/liolib.c"; sourceTree = ""; }; - BAE8013027B99CAD002B3786 /* lctype.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lctype.c; path = "../../lib/lua-5.4.6/src/lctype.c"; sourceTree = ""; }; - BAE8013127B99CAD002B3786 /* luac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = luac.c; path = "../../lib/lua-5.4.6/src/luac.c"; sourceTree = ""; }; - BAE8013227B99CAD002B3786 /* loadlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = loadlib.c; path = "../../lib/lua-5.4.6/src/loadlib.c"; sourceTree = ""; }; - BAE8013327B99CAD002B3786 /* lmathlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lmathlib.c; path = "../../lib/lua-5.4.6/src/lmathlib.c"; sourceTree = ""; }; - BAE8013427B99CAD002B3786 /* lstrlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lstrlib.c; path = "../../lib/lua-5.4.6/src/lstrlib.c"; sourceTree = ""; }; - BAE8013527B99CAD002B3786 /* lzio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lzio.c; path = "../../lib/lua-5.4.6/src/lzio.c"; sourceTree = ""; }; + BAE8011427B99CAB002B3786 /* lopcodes.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lopcodes.c; path = "../../lib/lua-5.4.8/src/lopcodes.c"; sourceTree = ""; }; + BAE8011527B99CAB002B3786 /* linit.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = linit.c; path = "../../lib/lua-5.4.8/src/linit.c"; sourceTree = ""; }; + BAE8011627B99CAB002B3786 /* lobject.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lobject.c; path = "../../lib/lua-5.4.8/src/lobject.c"; sourceTree = ""; }; + BAE8011727B99CAC002B3786 /* loslib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = loslib.c; path = "../../lib/lua-5.4.8/src/loslib.c"; sourceTree = ""; }; + BAE8011827B99CAC002B3786 /* lcorolib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lcorolib.c; path = "../../lib/lua-5.4.8/src/lcorolib.c"; sourceTree = ""; }; + BAE8011927B99CAC002B3786 /* lutf8lib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lutf8lib.c; path = "../../lib/lua-5.4.8/src/lutf8lib.c"; sourceTree = ""; }; + BAE8011A27B99CAC002B3786 /* ltable.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ltable.c; path = "../../lib/lua-5.4.8/src/ltable.c"; sourceTree = ""; }; + BAE8011B27B99CAC002B3786 /* ltablib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ltablib.c; path = "../../lib/lua-5.4.8/src/ltablib.c"; sourceTree = ""; }; + BAE8011C27B99CAC002B3786 /* llex.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = llex.c; path = "../../lib/lua-5.4.8/src/llex.c"; sourceTree = ""; }; + BAE8011D27B99CAC002B3786 /* lcode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lcode.c; path = "../../lib/lua-5.4.8/src/lcode.c"; sourceTree = ""; }; + BAE8011E27B99CAC002B3786 /* lua.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lua.c; path = "../../lib/lua-5.4.8/src/lua.c"; sourceTree = ""; }; + BAE8011F27B99CAC002B3786 /* ldo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ldo.c; path = "../../lib/lua-5.4.8/src/ldo.c"; sourceTree = ""; }; + BAE8012027B99CAC002B3786 /* ldump.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ldump.c; path = "../../lib/lua-5.4.8/src/ldump.c"; sourceTree = ""; }; + BAE8012127B99CAC002B3786 /* lparser.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lparser.c; path = "../../lib/lua-5.4.8/src/lparser.c"; sourceTree = ""; }; + BAE8012227B99CAC002B3786 /* lstate.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lstate.c; path = "../../lib/lua-5.4.8/src/lstate.c"; sourceTree = ""; }; + BAE8012327B99CAC002B3786 /* lapi.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lapi.c; path = "../../lib/lua-5.4.8/src/lapi.c"; sourceTree = ""; }; + BAE8012427B99CAC002B3786 /* ldebug.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ldebug.c; path = "../../lib/lua-5.4.8/src/ldebug.c"; sourceTree = ""; }; + BAE8012527B99CAC002B3786 /* lstring.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lstring.c; path = "../../lib/lua-5.4.8/src/lstring.c"; sourceTree = ""; }; + BAE8012627B99CAC002B3786 /* ltm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ltm.c; path = "../../lib/lua-5.4.8/src/ltm.c"; sourceTree = ""; }; + BAE8012727B99CAC002B3786 /* lmem.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lmem.c; path = "../../lib/lua-5.4.8/src/lmem.c"; sourceTree = ""; }; + BAE8012827B99CAC002B3786 /* lvm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lvm.c; path = "../../lib/lua-5.4.8/src/lvm.c"; sourceTree = ""; }; + BAE8012927B99CAC002B3786 /* lfunc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lfunc.c; path = "../../lib/lua-5.4.8/src/lfunc.c"; sourceTree = ""; }; + BAE8012A27B99CAC002B3786 /* lgc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lgc.c; path = "../../lib/lua-5.4.8/src/lgc.c"; sourceTree = ""; }; + BAE8012B27B99CAD002B3786 /* lundump.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lundump.c; path = "../../lib/lua-5.4.8/src/lundump.c"; sourceTree = ""; }; + BAE8012C27B99CAD002B3786 /* lbaselib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lbaselib.c; path = "../../lib/lua-5.4.8/src/lbaselib.c"; sourceTree = ""; }; + BAE8012D27B99CAD002B3786 /* ldblib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ldblib.c; path = "../../lib/lua-5.4.8/src/ldblib.c"; sourceTree = ""; }; + BAE8012E27B99CAD002B3786 /* lauxlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lauxlib.c; path = "../../lib/lua-5.4.8/src/lauxlib.c"; sourceTree = ""; }; + BAE8012F27B99CAD002B3786 /* liolib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = liolib.c; path = "../../lib/lua-5.4.8/src/liolib.c"; sourceTree = ""; }; + BAE8013027B99CAD002B3786 /* lctype.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lctype.c; path = "../../lib/lua-5.4.8/src/lctype.c"; sourceTree = ""; }; + BAE8013127B99CAD002B3786 /* luac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = luac.c; path = "../../lib/lua-5.4.8/src/luac.c"; sourceTree = ""; }; + BAE8013227B99CAD002B3786 /* loadlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = loadlib.c; path = "../../lib/lua-5.4.8/src/loadlib.c"; sourceTree = ""; }; + BAE8013327B99CAD002B3786 /* lmathlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lmathlib.c; path = "../../lib/lua-5.4.8/src/lmathlib.c"; sourceTree = ""; }; + BAE8013427B99CAD002B3786 /* lstrlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lstrlib.c; path = "../../lib/lua-5.4.8/src/lstrlib.c"; sourceTree = ""; }; + BAE8013527B99CAD002B3786 /* lzio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lzio.c; path = "../../lib/lua-5.4.8/src/lzio.c"; sourceTree = ""; }; + F50818302F6F564300D2AAFA /* iactions.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = iactions.c; path = ../../src/iactions.c; sourceTree = ""; }; F515A6F32DED074D006E1F63 /* sfctool.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = sfctool.c; path = ../../util/sfctool.c; sourceTree = ""; }; F515A6F52DED0916006E1F63 /* date.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = date.c; path = ../../src/date.c; sourceTree = ""; }; F515A6F62DED0916006E1F63 /* sfbase.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = sfbase.c; path = ../../src/sfbase.c; sourceTree = ""; }; @@ -632,6 +635,7 @@ F54C1AE42E43D22A006CAA8E /* sfprocs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = sfprocs.h; path = ../../include/include/sfprocs.h; sourceTree = ""; }; F5857AA32DED032C00A8CB4F /* cfgfiles.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = cfgfiles.c; path = ../../src/cfgfiles.c; sourceTree = ""; }; F5857AA52DED03BB00A8CB4F /* sfbase.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = sfbase.c; path = ../../src/sfbase.c; sourceTree = ""; }; + F5E66A212F7D4B30000E30B7 /* earlyarg.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = earlyarg.c; path = ../../src/earlyarg.c; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ @@ -691,6 +695,8 @@ 3189576821A1FCC100FB2ABE = { isa = PBXGroup; children = ( + F5E66A212F7D4B30000E30B7 /* earlyarg.c */, + F50818302F6F564300D2AAFA /* iactions.c */, F515A73B2DED0B47006E1F63 /* hacklib.c */, F5457B202DED146B00039D83 /* hacklib.c */, F515A72A2DED0A7C006E1F63 /* sftags.c */, @@ -1570,6 +1576,7 @@ "$(NH_DAT_DIR)/bigrm-10.lua", "$(NH_DAT_DIR)/bigrm-11.lua", "$(NH_DAT_DIR)/bigrm-12.lua", + "$(NH_DAT_DIR)/bigrm-13.lua", "$(NH_DAT_DIR)/bigrm-2.lua", "$(NH_DAT_DIR)/bigrm-3.lua", "$(NH_DAT_DIR)/bigrm-4.lua", @@ -1872,6 +1879,7 @@ 31B8A41321A23F650055BD01 /* version.c in Sources */, 31B8A3BB21A238060055BD01 /* allmain.c in Sources */, 31B8A39521A238060055BD01 /* windows.c in Sources */, + F5E66A222F7D4B30000E30B7 /* earlyarg.c in Sources */, 31B8A38621A238060055BD01 /* mondata.c in Sources */, 31B8A41921A244940055BD01 /* objects.c in Sources */, 31B8A3AA21A238060055BD01 /* wizard.c in Sources */, @@ -1979,6 +1987,7 @@ 31B8A38421A238060055BD01 /* do_name.c in Sources */, 31B8A3C421A238060055BD01 /* apply.c in Sources */, 31B8A3F721A23DD10055BD01 /* unixres.c in Sources */, + F50818312F6F564300D2AAFA /* iactions.c in Sources */, 31B8A3BD21A238060055BD01 /* files.c in Sources */, 31B8A39221A238060055BD01 /* do_wear.c in Sources */, 31B8A3E521A238B30055BD01 /* dlb.c in Sources */, @@ -2189,7 +2198,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = "$(SRCROOT)/../../include"; - LUA_VERSION = 5.4.6; + LUA_VERSION = 5.4.8; MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -2272,7 +2281,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = "$(SRCROOT)/../../include"; - LUA_VERSION = 5.4.6; + LUA_VERSION = 5.4.8; MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; diff --git a/sys/unix/NewInstall.unx b/sys/unix/NewInstall.unx index 97e066df9..831b1b870 100644 --- a/sys/unix/NewInstall.unx +++ b/sys/unix/NewInstall.unx @@ -104,7 +104,8 @@ additional interfaces. See below. | | | | support in your build. | | | | | | | | | | One possible way: | -| | | | brew install Qt | +| | | | brew install pkg-config | +| | | | brew install Qt6 | |----------+-------+-----------------+--------------------------------------| | Linux | tty | WANT_WIN_TTY | | | (Ubuntu) | | | | diff --git a/sys/unix/README-hints b/sys/unix/README-hints index 9c5f06934..03c3fd893 100644 --- a/sys/unix/README-hints +++ b/sys/unix/README-hints @@ -49,7 +49,10 @@ make musl=1 Build with settings appropriate for linking with musl libc, instead of glibc. This causes NOCRASHREPORT to be defined, and avoids the use of 'col' during the build. - +make resp=1 Place the majority of the compiler switches into + a response file to de-clutter the build compiler + command lines and eliminate some of the + unsightly wrapping that occurs. make CROSS_TO_MSDOS=1 package Cross-compile for an MSDOS target package. make CROSS_TO_WASM=1 Cross-compile for a WASM target. make CROSS_TO_MIPS=1 Cross-compile for a mips target. diff --git a/sys/unix/hints/include/compiler.370 b/sys/unix/hints/include/compiler.370 index 5ef6f801e..100d86299 100755 --- a/sys/unix/hints/include/compiler.370 +++ b/sys/unix/hints/include/compiler.370 @@ -55,6 +55,7 @@ CFLAGS+=-Wall -Wextra \ CFLAGS+=-pedantic CFLAGS+=-Wmissing-declarations #CFLAGS+=-Wformat=2 +#CFLAGS+=-Wdiscarded-qualifiers # these are left out of the C++ flags CFLAGS+=-Wformat-nonliteral @@ -98,6 +99,7 @@ CXX=clang++ -std=gnu++11 # clang-specific follows CLANGGTEQ12 := $(shell expr `$(CC) -dumpversion | cut -f1 -d.` \>= 12) CLANGGTEQ14 := $(shell expr `$(CC) -dumpversion | cut -f1 -d.` \>= 14) +CLANGGTEQ21 := $(shell expr `$(CC) -dumpversion | cut -f1 -d.` \>= 21) ifeq "$(CLANGGTEQ12)" "1" CFLAGS+=-Wimplicit-fallthrough endif @@ -109,6 +111,9 @@ else # older versions complain about things newer ones don't without this CFLAGS+=-Wno-missing-field-initializers endif +ifeq "$(CLANGGTEQ21)" "1" +CFLAGS+=-Wno-deprecated-octal-literals +endif # none endif # clang-specific ends here @@ -230,6 +235,20 @@ ifeq "$(C23)" "1" HAVECSTD=c2x endif +ifeq "$(c2x)" "1" +HAVECSTD=c2x +endif +ifeq "$(C2X)" "1" +HAVECSTD=c2x +endif + +ifeq "$(c2y)" "1" +HAVECSTD=c2y +endif +ifeq "$(C2Y)" "1" +HAVECSTD=c2y +endif + ifneq "$(HAVECSTD)" "" CSTD = -std=$(HAVECSTD) endif diff --git a/sys/unix/hints/include/cross-post.370 b/sys/unix/hints/include/cross-post.370 index 9d7c5b548..f28ec0fe1 100644 --- a/sys/unix/hints/include/cross-post.370 +++ b/sys/unix/hints/include/cross-post.370 @@ -57,14 +57,14 @@ dospkg: dodata dosfonts $(GAMEBIN) $(TARGETPFX)recover.exe ../dat/nhtiles.bmp cp ../sys/share/NetHack.cnf $(TARGETPFX)pkg/NETHACK.CNF cp ../sys/msdos/sysconf $(TARGETPFX)pkg/SYSCONF cp ../doc/nethack.txt $(TARGETPFX)pkg/NETHACK.TXT - cp $(DOSFONT)/ter-u16b.psf $(TARGETPFX)pkg/TER-U16B.PSF - cp $(DOSFONT)/ter-u16v.psf $(TARGETPFX)pkg/TER-U16V.PSF - cp $(DOSFONT)/ter-u18b.psf $(TARGETPFX)pkg/TER-U18B.PSF - cp $(DOSFONT)/ter-u20b.psf $(TARGETPFX)pkg/TER-U20B.PSF - cp $(DOSFONT)/ter-u22b.psf $(TARGETPFX)pkg/TER-U22B.PSF - cp $(DOSFONT)/ter-u24b.psf $(TARGETPFX)pkg/TER-U24B.PSF - cp $(DOSFONT)/ter-u28b.psf $(TARGETPFX)pkg/TER-U28B.PSF - cp $(DOSFONT)/ter-u32b.psf $(TARGETPFX)pkg/TER-U32B.PSF + -cp $(DOSFONT)/ter-u16b.psf $(TARGETPFX)pkg/TER-U16B.PSF + -cp $(DOSFONT)/ter-u16v.psf $(TARGETPFX)pkg/TER-U16V.PSF + -cp $(DOSFONT)/ter-u18b.psf $(TARGETPFX)pkg/TER-U18B.PSF + -cp $(DOSFONT)/ter-u20b.psf $(TARGETPFX)pkg/TER-U20B.PSF + -cp $(DOSFONT)/ter-u22b.psf $(TARGETPFX)pkg/TER-U22B.PSF + -cp $(DOSFONT)/ter-u24b.psf $(TARGETPFX)pkg/TER-U24B.PSF + -cp $(DOSFONT)/ter-u28b.psf $(TARGETPFX)pkg/TER-U28B.PSF + -cp $(DOSFONT)/ter-u32b.psf $(TARGETPFX)pkg/TER-U32B.PSF cp ../lib/djgpp/cwsdpmi/bin/CWSDPMI.EXE $(TARGETPFX)pkg/CWSDPMI.EXE ( if [ -f ../lib/djgpp/target/bin/symify.exe ]; then \ cp ../lib/djgpp/target/bin/symify.exe $(TARGETPFX)pkg/SYMIFY.EXE; \ @@ -229,6 +229,113 @@ mipspkg: dodata $(GAMEBIN) $(TARGETPFX)recover @echo MIPS package zip file $(TARGETPFX)nh370mips.zip endif # CROSS_TO_MIPS + +ifdef CROSS_TO_AMIGA +$(TARGETPFX)amidos.o : ../sys/amiga/amidos.c $(HACK_H) +$(TARGETPFX)amigst.o : ../sys/amiga/amigst.c $(HACK_H) +$(TARGETPFX)amirip.o : ../sys/amiga/amirip.c $(HACK_H) +$(TARGETPFX)amistack.o : ../sys/amiga/amistack.c $(HACK_H) +$(TARGETPFX)amitty.o : ../sys/amiga/amitty.c $(HACK_H) +$(TARGETPFX)amiwind.o : ../sys/amiga/amiwind.c \ + ../sys/amiga/amimenu.c $(HACK_H) +$(TARGETPFX)winami.o : ../sys/amiga/winami.c $(HACK_H) +$(TARGETPFX)winchar.o : ../sys/amiga/winchar.c tile.c $(HACK_H) +$(TARGETPFX)winfuncs.o : ../sys/amiga/winfuncs.c $(HACK_H) +$(TARGETPFX)winkey.o : ../sys/amiga/winkey.c $(HACK_H) +$(TARGETPFX)winamenu.o : ../sys/amiga/winamenu.c $(HACK_H) +$(TARGETPFX)winreq.o : ../sys/amiga/winreq.c \ + ../sys/amiga/colorwin.c \ + ../sys/amiga/clipwin.c $(HACK_H) +$(TARGETPFX)winstr.o : ../sys/amiga/winstr.c $(HACK_H) +$(GAMEBIN) : $(HOBJ) $(LUACROSSLIB) + $(TARGET_LINK) $(TARGET_LFLAGS) -o $(GAMEBIN) \ + $(HOBJ) $(WINLIB) $(TARGET_LIBS) +# +# Host-side IFF tile conversion tools (run on Linux, produce Amiga IFF files) +# +AMISRC = ../sys/amiga + +$(TARGETPFX)xpm2iff_host: $(AMISRC)/xpm2iff_host.c + $(CC) $(CFLAGS) -o $@ $< +$(TARGETPFX)tomb.iff: $(AMISRC)/grave16.xpm $(TARGETPFX)xpm2iff_host + $(TARGETPFX)xpm2iff_host $(AMISRC)/grave16.xpm $@ + +$(TARGETPFX)bmp2iff_host: $(AMISRC)/bmp2iff_host.c + $(CC) $(CFLAGS) -o $@ $< +$(TARGETPFX)tiles16.iff: ../dat/nhtiles.bmp $(TARGETPFX)bmp2iff_host + $(TARGETPFX)bmp2iff_host -planes 4 ../dat/nhtiles.bmp $@ +$(TARGETPFX)tiles32.iff: ../dat/nhtiles.bmp $(TARGETPFX)bmp2iff_host + $(TARGETPFX)bmp2iff_host -planes 5 ../dat/nhtiles.bmp $@ + +AMITILES = $(TARGETPFX)tiles16.iff $(TARGETPFX)tiles32.iff $(TARGETPFX)tomb.iff + +AMIREGEX_URL = https://github.com/garyhouston/regex.git +AMIREGEX_SRCDIR = $(AMISRC)/regex + +.PHONY: fetch-regex amigapkg amitiles + +fetch-regex: + @DSTDIR=sys/amiga/regex; \ + if [ ! -d src ]; then DSTDIR=../$$DSTDIR; fi; \ + if [ -f $$DSTDIR/regcomp.c ]; then \ + echo "BSD regex already present"; \ + else \ + echo "Fetching BSD regex from $(AMIREGEX_URL)"; \ + tmpdir=$$(mktemp -d) && \ + git clone --depth 1 $(AMIREGEX_URL) $$tmpdir && \ + cd $$tmpdir && \ + sh ./mkh -p regcomp.c > regcomp.ih && \ + sh ./mkh -p engine.c > engine.ih && \ + sh ./mkh -p regexec.c > regexec.ih && \ + sh ./mkh -p regerror.c > regerror.ih && \ + sh ./mkh -i _REGEX_H_ regex2.h regcomp.c \ + regexec.c regerror.c regfree.c > regex.h && \ + cd - > /dev/null && \ + mkdir -p $$DSTDIR && \ + for f in regcomp.c regexec.c regerror.c regfree.c \ + engine.c regex.h regex2.h cclass.h cname.h \ + utils.h regcomp.ih engine.ih regexec.ih \ + regerror.ih \ + COPYRIGHT; do \ + cp $$tmpdir/$$f $$DSTDIR/; \ + done && \ + rm -rf $$tmpdir && \ + echo "BSD regex installed in $$DSTDIR"; \ + fi +amitiles: $(AMITILES) + +UUDECODE = ../util/uudecode + +../util/uudecode: ../sys/share/uudecode.c + $(CC) $(CFLAGS) -o $@ $< + +amigapkg: $(AMITILES) ../util/uudecode + mkdir -p $(TARGETPFX)pkg/tiles $(TARGETPFX)pkg/hack + cp $(GAMEBIN) $(TARGETPFX)pkg/nethack + cp ../dat/nhdat $(TARGETPFX)pkg/nhdat + cp ../dat/license $(TARGETPFX)pkg/license + cp ../dat/symbols $(TARGETPFX)pkg/symbols + cp $(TARGETPFX)tiles16.iff $(TARGETPFX)pkg/tiles/tiles16.iff + cp $(TARGETPFX)tiles32.iff $(TARGETPFX)pkg/tiles/tiles32.iff + cp $(TARGETPFX)tomb.iff $(TARGETPFX)pkg/tomb.iff + cp ../sys/msdos/sysconf $(TARGETPFX)pkg/sysconf + cp ../doc/nethack.txt $(TARGETPFX)pkg/nethack.txt + ( cd $(TARGETPFX)pkg && ../../../util/uudecode ../../../sys/amiga/amifont8.uu && mv 8 hack/8 ) + ( cd $(TARGETPFX)pkg && ../../../util/uudecode ../../../sys/amiga/amifont.uu ) + cp $(AMISRC)/nethack.cnf $(TARGETPFX)pkg/nethack.cnf + -( cd $(TARGETPFX)pkg && test -f ../../../sys/amiga/dflticon.uu && \ + ../../../util/uudecode ../../../sys/amiga/dflticon.uu ) + -( cd $(TARGETPFX)pkg && test -f ../../../sys/amiga/NHinfo.uu && \ + ../../../util/uudecode ../../../sys/amiga/NHinfo.uu ) + -( cd $(TARGETPFX)pkg && test -f ../../../sys/amiga/NewGame.uu && \ + ../../../util/uudecode ../../../sys/amiga/NewGame.uu ) + -( cd $(TARGETPFX)pkg && test -f ../../../sys/amiga/HackWB.uu && \ + ../../../util/uudecode ../../../sys/amiga/HackWB.uu ) + touch $(TARGETPFX)pkg/record + ( cd $(TARGETPFX)pkg && zip -9r ../NH370AMI.ZIP * ) + @echo amiga package zip file $(TARGETPFX)NH370AMI.ZIP +endif # CROSS_TO_AMIGA + ifdef CROSS_SHARED # shared file dependencies $(TARGETPFX)pcmain.o : ../sys/share/pcmain.c $(HACK_H) diff --git a/sys/unix/hints/include/cross-pre1.370 b/sys/unix/hints/include/cross-pre1.370 index 203c06048..867886d9b 100644 --- a/sys/unix/hints/include/cross-pre1.370 +++ b/sys/unix/hints/include/cross-pre1.370 @@ -38,6 +38,16 @@ override TARGETPFX = $(TARGETDIR)/ override TARGET_LIBS= endif +ifdef CROSS_TO_AMIGA +CROSS=1 +BUILD_TARGET_LUA=1 +CROSS_SHARED=1 +override TARGET = amiga +override TARGETDIR=../targets/$(TARGET) +override TARGETPFX = $(TARGETDIR)/ +override TARGET_LIBS= +endif + ifdef CROSS override PREGAME= override BUILDMORE= diff --git a/sys/unix/hints/include/cross-pre2.370 b/sys/unix/hints/include/cross-pre2.370 index df4ef6105..3476682ad 100644 --- a/sys/unix/hints/include/cross-pre2.370 +++ b/sys/unix/hints/include/cross-pre2.370 @@ -7,9 +7,9 @@ ifdef BUILD_TARGET_LUA #===============-================================================= # LUA library -# Source from http://www.lua.org/ftp/lua-5.4.6.tar.gz +# Source from http://www.lua.org/ftp/lua-5.4.8.tar.gz #================================================================= -LUA_VERSION ?=5.4.6 +LUA_VERSION ?=5.4.8 LUATOP ?= ../lib/lua-$(LUA_VERSION) LUASRCDIR ?= $(LUATOP)/src LUAOBJFILES1 = $(TARGETPFX)lapi.o $(TARGETPFX)lauxlib.o \ @@ -119,7 +119,7 @@ ifdef CROSS_TO_MSDOS # 2. Then # make CROSS_TO_MSDOS=1 WANT_WIN_TTY=1 WANT_WIN_CURSES=1 all # -# Source from http://www.lua.org/ftp/lua-5.4.6.tar.gz +# Source from http://www.lua.org/ftp/lua-5.4.8.tar.gz #================================================================= CFLAGS += -DCROSSCOMPILE @@ -188,6 +188,7 @@ MSDOS_TARGET_CXXFLAGS = -c -O $(DBGFLAGS) -I../include -I../sys/msdos -I../win/s PDCINCL += -I$(PDCPORT) PDC_TARGET_CFLAGS = $(MSDOS_TARGET_CFLAGS) -Wno-unused-parameter \ -Wno-missing-prototypes +ifndef SKIP_FONTS_IN_CI FONTVER = terminus-font-4.49.1 FONTTOP = ../lib/$(FONTVER) DOSFONT = ../sys/msdos/fonts @@ -195,6 +196,9 @@ FONTTARGETS = $(DOSFONT)/ter-u16b.psf $(DOSFONT)/ter-u16v.psf \ $(DOSFONT)/ter-u18b.psf $(DOSFONT)/ter-u20b.psf \ $(DOSFONT)/ter-u22b.psf $(DOSFONT)/ter-u24b.psf \ $(DOSFONT)/ter-u28b.psf $(DOSFONT)/ter-u32b.psf +else +FONTTARGETS= +endif LUABIN = ../lib/lua-$(LUA_VERSION)/src/lua LUA_TARGET_CFLAGS = $(MSDOS_TARGET_CFLAGS) override TARGET_CFLAGS = $(MSDOS_TARGET_CFLAGS) $(MSDOS_PEDANTIC) @@ -275,7 +279,7 @@ EMCC_LFLAGS += -s ALLOW_TABLE_GROWTH EMCC_LFLAGS += -s ASYNCIFY -s ASYNCIFY_IMPORTS='["local_callback"]' EMCC_LFLAGS += -O3 EMCC_LFLAGS += -s MODULARIZE -EMCC_LFLAGS += -s EXPORTED_FUNCTIONS='["_main", "_shim_graphics_set_callback", "_display_inventory", "_malloc"]' +EMCC_LFLAGS += -s EXPORTED_FUNCTIONS='["_main", "_shim_graphics_set_callback", "_repopulate_perminvent", "_malloc"]' EMCC_LFLAGS += -s EXPORTED_RUNTIME_METHODS='["cwrap", "ccall", "addFunction", \ "removeFunction", "UTF8ToString", "stringToUTF8", "getValue", \ "setValue", "ENV", "FS", "IDBFS"]' @@ -456,6 +460,97 @@ NCURSES_PLATFORM=MIPS endif # CROSS_TO_MIPS #================================================================= +#================================================================= + +ifdef CROSS_TO_AMIGA +#===============-================================================= +# AmigaOS m68k cross-compile recipe +#===============-================================================= +# Uses an Amiga M68K cross-compiler on linux or macOS. +# +# Cross-compiler: https://franke.ms/git/bebbo/amiga-gcc +# Install to /opt/amiga, then: +# sys/unix/setup.sh sys/unix/hints/linux.370 +# make fetch-lua +# make CROSS_TO_AMIGA=1 fetch-regex +# make CROSS_TO_AMIGA=1 all +# make CROSS_TO_AMIGA=1 package +#================================================================= + +CFLAGS += -DCROSSCOMPILE + +# +# Override the build tools and some obj files to +# reflect the amiga-gcc cross-compiler. +# +TOOLTOP = /opt/amiga/bin +TOOLARCH = -m68000 +override REGEXOBJ = $(TARGETPFX)posixregex.o +AMIREGEXOBJ = $(TARGETPFX)regcomp.o $(TARGETPFX)regexec.o \ + $(TARGETPFX)regerror.o $(TARGETPFX)regfree.o +override TARGET_CC = $(TOOLTOP)/m68k-amigaos-gcc +override TARGET_CXX = $(TOOLTOP)/m68k-amigaos-c++ +override TARGET_AR = $(TOOLTOP)/m68k-amigaos-ar +override TARGET_STUBEDIT= +override TARGET_CFLAGS = -c -O2 -noixemul $(TOOLARCH) \ + -include sys/types.h \ + -I../include \ + -I../sys/amiga -I../win/share \ + $(LUAINCL) -DAMIGA -DNOTTYGRAPHICS -DNO_TERMS -DNO_SIGNAL \ + -DTILES_IN_GLYPHMAP $(PDCURSESDEF) \ + -DCROSSCOMPILE -DCROSSCOMPILE_TARGET -DCROSS_TO_AMIGA \ + -DAMIGA_VERSION_STRING=\""VER: NetHack 3.7.0"\" +override TARGET_CXXFLAGS = $(TARGET_CFLAGS) +LUA_TARGET_CFLAGS = $(TARGET_CFLAGS) +ifeq "$(REGEXOBJ)" "$(TARGETPFX)cppregex.o" +override TARGET_LINK = $(TARGET_CXX) +else +override TARGET_LINK = $(TARGET_CC) +endif +override TARGET_LFLAGS= $(TOOLARCH) -noixemul -Wl,--allow-multiple-definition +override TARGET_LIBS += $(LIBLM) +VARDATND += nhtiles.bmp +override SYSSRC = ../sys/amiga/amidos.c ../sys/amiga/amigst.c \ + ../sys/amiga/amimenu.c ../sys/amiga/amirip.c \ + ../sys/amiga/amistack.c ../sys/amiga/amitty.c \ + ../sys/amiga/amiwind.c ../sys/amiga/clipwin.c \ + ../sys/amiga/colorwin.c \ + ../sys/amiga/winami.c ../sys/amiga/winchar.c \ + ../sys/amiga/winfuncs.c ../sys/amiga/winkey.c \ + ../sys/amiga/winamenu.c ../sys/amiga/winreq.c \ + ../sys/amiga/winstr.c ../sys/share/pcmain.c \ + ../win/share/bmptiles.c ../win/share/giftiles.c \ + ../win/share/tileset.c +override SYSOBJ = $(TARGETPFX)amidos.o $(TARGETPFX)amigst.o \ + $(TARGETPFX)amirip.o $(TARGETPFX)amistack.o \ + $(TARGETPFX)amitty.o $(TARGETPFX)amiwind.o \ + $(TARGETPFX)winami.o $(TARGETPFX)winchar.o \ + $(TARGETPFX)winfuncs.o $(TARGETPFX)winkey.o \ + $(TARGETPFX)winamenu.o $(TARGETPFX)winreq.o \ + $(TARGETPFX)winstr.o $(TARGETPFX)pcmain.o \ + $(TARGETPFX)bmptiles.o $(TARGETPFX)giftiles.o \ + $(TARGETPFX)tileset.o \ + $(AMIREGEXOBJ) +override WINLIB= +override LUALIB= +override LUALIBS= +override TOPLUALIB= +override DLLIB= +override WINOBJ= +override GAMEBIN = $(TARGETPFX)nethack +override PACKAGE = amigapkg +override PREGAME += mkdir -p $(TARGETDIR) ; +override CLEANMORE += rm -r $(TARGETDIR) ; +# +# Rule for files in sys/amiga +$(TARGETPFX)%.o : ../sys/amiga/%.c + $(TARGET_CC) $(TARGET_CFLAGS) -o$@ $< +# Rule for BSD regex in sys/amiga/regex +$(TARGETPFX)%.o : ../sys/amiga/regex/%.c + $(TARGET_CC) $(TARGET_CFLAGS) -I../sys/amiga/regex -o$@ $< +endif # CROSS_TO_AMIGA +#================================================================= + ifdef WANT_WIN_CURSES ifdef BUILD_PDCURSES # Rules for PDCurses files diff --git a/sys/unix/hints/include/misc.370 b/sys/unix/hints/include/misc.370 index 6608cf45a..7f57697b4 100644 --- a/sys/unix/hints/include/misc.370 +++ b/sys/unix/hints/include/misc.370 @@ -70,34 +70,38 @@ NROFF = mandoc MAN2TXTPRE = -T ascii MAN2TXTPOST= | col -b else -#detection of groff -NROFFISGROFF := $(shell echo `nroff --version | grep "GNU groff version"`) +# +# Detect groff. +NROFFISGROFF := $(shell echo `nroff --version | grep -c 'GNU.*groff.*version'`) #$(info NROFFISGROFF=$(NROFFISGROFF)) -ifneq "$(NROFFISGROFF)" "" -# get the version of groff and flag if it is gt or eq to 1.23 -GROFFGE123 := $(shell expr `echo $(NROFFISGROFF) | cut -f2 -d.` \>= 23) -# or less than 1.24 -GROFFLT124 := $(shell expr `echo $(NROFFISGROFF) | cut -f2 -d.` \< 24) -# -Wtab -Wrange are for the sake of tmac.n. -NROFF_FLAGS := -wall -Wtab -Wrange -ifneq "$(GROFFLT124)" "" -NROFF_FLAGS += -Wel -Wscale -endif -endif # NROFFISGROFF +ifneq "$(NROFFISGROFF)" "0" +# Gather groff's minor version number (register `.y`). +GROFFMINORVERSION := $(shell printf '.tm \\n[.y]\n' | nroff 2>&1) +#$(info GROFFMINORVERSION=$(GROFFMINORVERSION)) +# Silence warnings produced by tmac.n, which NetHack does not modify. +NROFF_FLAGS := -wall -Wrange -Wscale -Wtab +# groff <= 1.23 also supported an "el" warning category that was buggy. +GROFFLE123 := $(shell expr $(GROFFMINORVERSION) \<= 23) +#$(info GROFFLE123=$(GROFFLE123)) +ifeq "$(GROFFLE123)" "1" +NROFF_FLAGS += -Wel +endif # end groff less than 1.23 +endif # end NROFFISGROFF +# $(info NROFF_FLAGS=$(NROFF_FLAGS)) ifneq "$(NROFFISGROFF)" "" # It's groff # add the -Tascii flag used by groff MAN2TXTPRE += -Tascii -ifneq "$(GROFFGE123)" "" # It's groff 1.23 or greater -#$(info GROFFGE123=$(GROFFGE123)) # nroff in groff 1.23 supports the -P option to pass arguments to the # output driver. -cbou are flags to grotty(1). +GROFFGE123 := $(shell expr $(GROFFMINORVERSION) \>= 23) +#$(info GROFFGE123=$(GROFFGE123)) +ifeq "$(GROFFGE123)" "1" MAN2TXTPRE += -P -cbou MAN2TXTPOST= else MAN2TXTPRE += -c -# groff less than 1.23 -endif +endif # end groff less than 1.23 endif # end groff-specific endif # not USE_MANDOC diff --git a/sys/unix/hints/include/multiw-2.370 b/sys/unix/hints/include/multiw-2.370 index c580063b6..e1cf1633d 100644 --- a/sys/unix/hints/include/multiw-2.370 +++ b/sys/unix/hints/include/multiw-2.370 @@ -15,7 +15,7 @@ # - WINOBJ0 #--- # User selections could be specified as combinations of any of the following: -# WIN_WANT_TTY=1, WIN_WANT_CURSES=1, WIN_WANT_QT=1, WIN_WANT_X11=1 +# WANT_WIN_TTY=1, WANT_WIN_CURSES=1, WANT_WIN_QT=1, WANT_WIN_X11=1 # The selections will all be linked into the same binary. # # Assuming you have the prerequisite packages mentioned above, you can diff --git a/sys/unix/hints/include/response.370 b/sys/unix/hints/include/response.370 new file mode 100644 index 000000000..4e893ea3e --- /dev/null +++ b/sys/unix/hints/include/response.370 @@ -0,0 +1,33 @@ +#------------------------------------------------------------------------------ +# NetHack 3.7 response.370 $NHDT-Date: 1668359835 2022/11/13 17:17:15 $ $NHDT-Branch: NetHack-3.7 $ + +ifeq "$(RESP)" "1" +USE_RESPONSEFILE=1 +endif +ifeq "$(resp)" "1" +USE_RESPONSEFILE=1 +endif +ifeq "$(RESPONSE)" "1" +USE_RESPONSEFILE=1 +endif +ifeq "$(response)" "1" +USE_RESPONSEFILE=1 +endif + +ifeq "$(USE_RESPONSEFILE)" "1" +RESPONSEFILES=create_responsefiles +CXXFLAGS = $(CCXXFLAGS) -I. -I$(QTDIR)/include $(QTCXXFLAGS) +CC_COMPILER_SWITCHES := $(subst \,\\,$(CFLAGS)) +CC_COMPILER_SWITCHES := $(subst ",\",$(CC_COMPILER_SWITCHES)) +CXX_COMPILER_SWITCHES := $(subst \,\\,$(CXXFLAGS)) +CXX_COMPILER_SWITCHES := $(subst ",\",$(CXX_COMPILER_SWITCHES)) +CC_RESPONSEFILE=../src/nethack_cc.rsp +CXX_RESPONSEFILE=../src/nethack_cxx.rsp +CFLAGS=@$(CC_RESPONSEFILE) +CXXFLAGS=@$(CXX_RESPONSEFILE) +CLEAN_CC_RESPONSEFILE=rm -f $(CC_RESPONSEFILE); +CLEAN_CXX_RESPONSEFILE=rm -f $(CXX_RESPONSEFILE); +endif + +#end of response.370 +#------------------------------------------------------------------------------ diff --git a/sys/unix/hints/linux.370 b/sys/unix/hints/linux.370 index 3ce7d47d4..3ceb7cda3 100755 --- a/sys/unix/hints/linux.370 +++ b/sys/unix/hints/linux.370 @@ -390,13 +390,15 @@ endif #?WANT_SOURCE_INSTALL INSTDIR=$(HACKDIR) VARDIR = $(HACKDIR) -ifneq "$(CCISCLANG)" "" -# gdb may not be installed if clang is chosen compiler so the game -# won't start in that case due to a sysconf error. Comment out -# relevant lines in sysconf. -POSTINSTALL+= sed -i -e 's;^GDBPATH=/usr/bin/gdb;\#GDBPATH=/usr/bin/gdb;' \ +ifdef MAKEFILE_TOP +TESTGDBPATH=/usr/bin/gdb +POSTINSTALL+= test -f $(TESTGDBPATH) || \ + sed -i -e 's;^GDBPATH=/usr/bin/gdb;\#GDBPATH=/usr/bin/gdb;' \ -e 's;PANICTRACE_GDB=1;PANICTRACE_GDB=0;' $(INSTDIR)/sysconf; -endif +POSTUPDATE+= test -f $(TESTGDBPATH) || \ + sed -i -e 's;^GDBPATH=/usr/bin/gdb;\#GDBPATH=/usr/bin/gdb;' \ + -e 's;PANICTRACE_GDB=1;PANICTRACE_GDB=0;' $(INSTDIR)/sysconf; +endif #MAKEFILE_TOP ifeq '$(USE_NONOSTATICFN)' '1' CFLAGS += -DNONOSTATICFN @@ -448,6 +450,10 @@ MANDIR=/usr/share/man/man6 #-INCLUDE multisnd2-pre.370 # +# +#-INCLUDE response.370 +# + #-POST # diff --git a/sys/unix/hints/macOS.370 b/sys/unix/hints/macOS.370 index ad522bb2f..31279a3cd 100755 --- a/sys/unix/hints/macOS.370 +++ b/sys/unix/hints/macOS.370 @@ -281,12 +281,12 @@ WINLIB += $(shell PKG_CONFIG_PATH=$(QTDIR)/lib/pkgconfig \ pkg-config Qt5Gui Qt5Widgets Qt5Multimedia --libs) endif # WANT_WIN_QT5 ifdef WANT_WIN_QT6 -QTCXXFLAGS += -std=c++17 -I $(QTDIR)/include -I $(QTDIR)/include/QtCore \ - -I $(QTDIR)/include/QtMultimedia +QTCXXFLAGS += $(sort $(shell PKG_CONFIG_PATH=$(QTDIR)/lib/pkgconfig \ + pkg-config Qt6Gui Qt6Widgets Qt6Multimedia --cflags)) MOC = moc MOCPATH = $(QTDIR)/share/qt/libexec/moc -WINLIB += -F $(QTDIR)/Frameworks -framework QtCore -framework QtGui \ - -framework QtWidgets -framework QtMultimedia +WINLIB += $(shell PKG_CONFIG_PATH=$(QTDIR)/lib/pkgconfig \ + pkg-config Qt6Gui Qt6Widgets Qt6Multimedia --libs) endif # WANT_WIN_QT6 VARDATND0 += nhtiles.bmp rip.xpm nhsplash.xpm # XXX if /Developer/qt exists and QTDIR not set, use that @@ -461,6 +461,9 @@ SYSCONFENSURE = (if ! test -f $(INSTDIR)/sysconf ; then \ # #-INCLUDE multisnd2-pre.370 # +# +#-INCLUDE response.370 +# ifdef MAKEFILE_TOP .PHONY: bundle diff --git a/sys/unix/unixmain.c b/sys/unix/unixmain.c index e74cfc8c5..65a5401c4 100644 --- a/sys/unix/unixmain.c +++ b/sys/unix/unixmain.c @@ -26,17 +26,11 @@ extern struct passwd *getpwuid(int); #endif extern struct passwd *getpwnam(const char *); #ifdef CHDIR -static void chdirx(const char *, boolean); +void chdirx(const char *, boolean); #endif /* CHDIR */ -static boolean whoami(void); -static char *lopt(char *, int, const char *, const char *, int *, char ***); +boolean whoami(void); static void process_options(int, char **); -static void consume_arg(int, int *, char ***); -static void consume_two_args(int, int *, char ***); -static void early_options(int *, char ***, char **); -ATTRNORETURN static void opt_terminate(void) NORETURN; -ATTRNORETURN static void opt_usage(const char *) NORETURN; -ATTRNORETURN static void scores_only(int, char **, const char *) NORETURN; + #ifdef SND_LIB_INTEGRATED uint32_t soundlibchoice = soundlib_nosound; #endif @@ -128,12 +122,14 @@ main(int argc, char *argv[]) if (!dir) dir = nh_getenv("HACKDIR"); #endif /* CHDIR */ + program_state.early_options = 1; #ifdef ENHANCED_SYMBOLS if (argcheck(argc, argv, ARG_DUMPGLYPHIDS) == 2) exit(EXIT_SUCCESS); #endif /* handle -dalthackdir, -s , --version, --showpaths */ early_options(&argc, &argv, &dir); + program_state.early_options = 0; #ifdef CHDIR /* * Change directories before we initialize the window system so @@ -325,93 +321,6 @@ main(int argc, char *argv[]) return 0; } -static char ArgVal_novalue[] = "[nothing]"; /* note: not 'const' */ - -enum cmdlinearg { - ArgValRequired = 0, ArgValOptional = 1, - ArgValDisallowed = 2, ArgVal_mask = (1 | 2), - ArgNamOneLetter = 4, ArgNam_mask = 4, - ArgErrSilent = 0, ArgErrComplain = 8, ArgErr_mask = 8 -}; - -/* approximate 'getopt_long()' for one option; all the comments refer to - "-windowtype" but the code isn't specific to that */ -static char * -lopt( - char *arg, /* command line token; beginning matches 'optname' */ - int lflags, /* cmdlinearg | errorhandling */ - const char *optname, /* option's name; "-windowtype" in examples below */ - const char *origarg, /* 'arg' might have had a dash prefix removed */ - int *argc_p, /* argc that can have changes passed to caller */ - char ***argv_p) /* argv[] ditto */ -{ - int argc = *argc_p; - char **argv = *argv_p; - char *p, *nextarg = (argc > 1 && argv[1][0] != '-') ? argv[1] : 0; - int l, opttype = (lflags & ArgVal_mask); - boolean oneletterok = ((lflags & ArgNam_mask) == ArgNamOneLetter), - complain = ((lflags & ArgErr_mask) == ArgErrComplain); - - /* first letter must match */ - if (arg[1] != optname[1]) { - loptbail: - if (complain) - config_error_add("Unknown option: %.60s", origarg); - return (char *) 0; - loptnotallowed: - if (complain) - config_error_add("Value not allowed: %.60s", origarg); - return (char *) 0; - loptrequired: - if (complain) - config_error_add("Missing required value: %.60s", origarg); - return (char *) 0; - } - - if ((p = strchr(arg, '=')) == 0) - p = strchr(arg, ':'); - if (p && opttype == ArgValDisallowed) - goto loptnotallowed; - - l = (int) (p ? (long) (p - arg) : (long) strlen(arg)); - if ((l > 2 || oneletterok) && !strncmp(arg, optname, l)) { - /* "-windowtype[=foo]" */ - if (p) - ++p; /* past '=' or ':' */ - else if (opttype == ArgValRequired) - p = eos(arg); /* we have "-w[indowtype]" w/o "=foo" - * so we'll take foo from next element */ - else - return ArgVal_novalue; - } else if (oneletterok) { - /* "-w..." but not "-w[indowtype[=foo]]" */ - if (!p) { - p = &arg[2]; /* past 'w' of "-wfoo" */ -#if 0 /* -x:value could work but is not supported (callers don't expect it) */ - } else if (p == arg + 2) { - ++p; /* past ':' of "-w:foo" */ -#endif - } else { - /* "-w...=foo" but not "-w[indowtype]=foo" */ - goto loptbail; - } - } else { - goto loptbail; - } - if (!p || !*p) { - /* "-w[indowtype]" w/o '='/':' if there is a next element, use - it for "foo"; if not, supply a non-Null bogus value */ - if (nextarg && (opttype == ArgValRequired - || opttype == ArgValOptional)) - p = nextarg, --(*argc_p), ++(*argv_p); - else if (opttype == ArgValRequired) - goto loptrequired; - else - p = ArgVal_novalue; /* there is no next element */ - } - return p; -} - /* caveat: argv elements might be arbitrarily long */ static void process_options(int argc, char *argv[]) @@ -573,272 +482,8 @@ process_options(int argc, char *argv[]) return; } -/* move argv[ndx] to end of argv[] array, then reduce argc to hide it; - prevents process_options() from encountering it after early_options() - has processed it; elements get reordered but all remain intact */ -static void -consume_arg(int ndx, int *ac_p, char ***av_p) -{ - char *gone, **av = *av_p; - int i, ac = *ac_p; - - /* "-one -two -three -four" -> "-two -three -four -one" */ - if (ac > 2) { - gone = av[ndx]; - for (i = ndx + 1; i < ac; ++i) - av[i - 1] = av[i]; - av[ac - 1] = gone; - } - --(*ac_p); -} - -/* consume two tokens for '-argname value' w/o '=' or ':' */ -static void -consume_two_args(int ndx, int *ac_p, char ***av_p) -{ - /* when consuming "-two arg" from "-two arg -three -four", - the *ac_p manipulation results in "-three -four -two arg" - rather than the "-three -four arg -two" that would happen - with just two ordinary consume_arg() calls */ - consume_arg(ndx, ac_p, av_p); - ++(*ac_p); /* bring the final slot back into view */ - consume_arg(ndx, ac_p, av_p); - --(*ac_p); /* take away restored slot */ -} - -/* process some command line arguments before loading options */ -static void -early_options(int *argc_p, char ***argv_p, char **hackdir_p) -{ - char **argv, *arg, *origarg; - int argc, oldargc, ndx = 0, consumed = 0; - - config_error_init(FALSE, "command line", FALSE); - - /* treat "nethack ?" as a request for usage info; due to shell - processing, player likely has to use "nethack \?" or "nethack '?'" - [won't work if used as "nethack -dpath ?" or "nethack -d path ?"] */ - if (*argc_p > 1 && !strcmp((*argv_p)[1], "?")) - opt_usage(*hackdir_p); /* doesn't return */ - - /* - * Both *argc_p and *argv_p account for the program name as (*argv_p)[0]; - * local argc and argv impicitly discard that (by starting 'ndx' at 1). - * argcheck() doesn't mind, prscore() (via scores_only()) does (for the - * number of args it gets passed, not for the value of argv[0]). - */ - for (ndx = 1; ndx < *argc_p; ndx += (consumed ? 0 : 1)) { - consumed = 0; - argc = *argc_p - ndx; - argv = *argv_p + ndx; - - arg = origarg = argv[0]; - /* skip any args intended for deferred options */ - if (*arg != '-') - continue; - /* allow second dash if arg name is longer than one character */ - if (arg[0] == '-' && arg[1] == '-' && arg[2] != '\0' - && (arg[3] != '\0' && arg[3] != '=' && arg[3] != ':')) - ++arg; - - switch (arg[1]) { /* char after leading dash */ - case 'b': -#ifdef CRASHREPORT - // --bidshow - if (argcheck(argc, argv, ARG_BIDSHOW) == 2){ - opt_terminate(); - /*NOTREACHED*/ - } -#endif - break; - case 'd': - if (argcheck(argc, argv, ARG_DEBUG) == 1) { - consume_arg(ndx, argc_p, argv_p), consumed = 1; -#ifndef NODUMPENUMS - } else if (argcheck(argc, argv, ARG_DUMPENUMS) == 2) { - opt_terminate(); - /*NOTREACHED*/ -#endif - } else if (argcheck(argc, argv, ARG_DUMPMONGEN) == 2) { - opt_terminate(); - /*NOTREACHED*/ - } else if (argcheck(argc, argv, ARG_DUMPWEIGHTS) == 2) { - opt_terminate(); - /*NOTREACHED*/ - } else { #ifdef CHDIR - oldargc = argc; - arg = lopt(arg, - (ArgValRequired | ArgNamOneLetter | ArgErrSilent), - "-directory", origarg, &argc, &argv); - if (!arg) - error("Flag -d must be followed by a directory name."); - if (*arg != 'e') { /* avoid matching -decgraphics or -debug */ - *hackdir_p = arg; - if (oldargc == argc) - consume_arg(ndx, argc_p, argv_p), consumed = 1; - else - consume_two_args(ndx, argc_p, argv_p), consumed = 2; - } -#endif /* CHDIR */ - } - break; - case 'h': - case '?': - if (lopt(arg, ArgValDisallowed, "-help", origarg, &argc, &argv) - || lopt(arg, ArgValDisallowed | ArgNamOneLetter, "-?", - origarg, &argc, &argv)) - opt_usage(*hackdir_p); /* doesn't return */ - break; - case 'n': - oldargc = argc; - if (!strcmp(arg, "-no-nethackrc")) /* no abbreviation allowed */ - arg = nhStr("/dev/null"); - else - arg = lopt(arg, (ArgValRequired | ArgErrComplain), - "-nethackrc", origarg, &argc, &argv); - if (arg) { - gc.cmdline_rcfile = dupstr(arg); - if (oldargc == argc) - consume_arg(ndx, argc_p, argv_p), consumed = 1; - else - consume_two_args(ndx, argc_p, argv_p), consumed = 2; - } - break; - case 's': - if (argcheck(argc, argv, ARG_SHOWPATHS) == 2) { - gd.deferred_showpaths = TRUE; - gd.deferred_showpaths_dir = *hackdir_p; - config_error_done(); - return; - } - /* check for "-s" request to show scores */ - if (lopt(arg, ((ArgValDisallowed | ArgErrComplain) - /* only accept one-letter if there is just one - dash; reject "--s" because prscore() via - scores_only() doesn't understand it */ - | ((origarg[1] != '-') ? ArgNamOneLetter : 0)), - /* [ought to omit val-disallowed and accept - --scores=foo since -s foo and -sfoo are - allowed, but -s form can take more than one - space-separated argument and --scores=foo - isn't suited for that] */ - "-scores", origarg, &argc, &argv)) { - /* at this point, argv[0] contains "-scores" or a leading - substring of it; prscore() (via scores_only()) expects - that to be in argv[1] so we adjust the pointer to make - that be the case; if there are any non-early args waiting - to be passed along to process_options(), the resulting - argv[0] will be one of those rather than the program - name but prscore() doesn't care */ - scores_only(argc + 1, argv - 1, *hackdir_p); - /*NOTREACHED*/ - } - break; - case 'u': - if (lopt(arg, ArgValDisallowed, "-usage", origarg, &argc, &argv)) - opt_usage(*hackdir_p); - break; - case 'v': - if (argcheck(argc, argv, ARG_VERSION) == 2) { - opt_terminate(); - /*NOTREACHED*/ - } - break; - case 'w': /* windowtype: "-wfoo" or "-w[indowtype]=foo" - * or "-w[indowtype]:foo" or "-w[indowtype] foo" */ - arg = lopt(arg, - (ArgValRequired | ArgNamOneLetter | ArgErrComplain), - "-windowtype", origarg, &argc, &argv); - if (gc.cmdline_windowsys) - free((genericptr_t) gc.cmdline_windowsys); - gc.cmdline_windowsys = arg ? dupstr(arg) : NULL; - break; - default: - break; - } - } - /* empty or "N errors on command line" */ - config_error_done(); - return; -} - -/* for command-line options that perform some immediate action and then - terminate the program without starting play, like 'nethack --version' - or 'nethack -s Zelda'; do some cleanup before that termination */ -ATTRNORETURN static void -opt_terminate(void) -{ - config_error_done(); /* free memory allocated by config_error_init() */ - - nh_terminate(EXIT_SUCCESS); - /*NOTREACHED*/ -} - -ATTRNORETURN static void -opt_usage(const char *hackdir) -{ -#ifdef CHDIR - chdirx(hackdir, TRUE); -#else - nhUse(hackdir); -#endif - dlb_init(); - - genl_display_file(USAGEHELP, TRUE); - opt_terminate(); -} - -/* show the sysconf file name, playground directory, run-time configuration - file name, dumplog file name if applicable, and some other things */ -ATTRNORETURN void -after_opt_showpaths(const char *dir) -{ -#ifdef CHDIR - chdirx(dir, FALSE); -#else - nhUse(dir); -#endif - opt_terminate(); - /*NOTREACHED*/ -} - -/* handle "-s [character-names]" to show all the entries - in the high scores file ('record') belonging to particular characters; - nethack will end after doing so without starting play */ -ATTRNORETURN static void -scores_only(int argc, char **argv, const char *dir) -{ - /* do this now rather than waiting for final termination, in case there - is an error summary coming */ - config_error_done(); - -#ifdef CHDIR - chdirx(dir, FALSE); -#else - nhUse(dir); -#endif -#ifdef SYSCF - iflags.initoptions_noterminate = TRUE; - initoptions(); /* sysconf options affect whether panictrace is enabled */ - iflags.initoptions_noterminate = FALSE; -#endif -#ifdef PANICTRACE - ARGV0 = gh.hname; /* save for possible stack trace */ -#ifndef NO_SIGNAL - panictrace_setsignals(TRUE); -#endif -#endif - (void) whoami(); /* set up default plname[] */ - - prscore(argc, argv); - - nh_terminate(EXIT_SUCCESS); /* bypass opt_terminate() */ - /*NOTREACHED*/ -} - -#ifdef CHDIR -static void +void chdirx(const char *dir, boolean wr) { if (dir /* User specified directory? */ @@ -905,7 +550,7 @@ chdirx(const char *dir, boolean wr) #endif /* CHDIR */ /* returns True iff we set plname[] to username which contains a hyphen */ -static boolean +boolean whoami(void) { /* diff --git a/sys/vms/Install.vms b/sys/vms/Install.vms index 730eb47f3..5b0b7e184 100644 --- a/sys/vms/Install.vms +++ b/sys/vms/Install.vms @@ -76,7 +76,7 @@ used if symbols are set up to run them. Or if you have them but do not have symbols set up, you may edit spec_lev.com to have it run them. If neither of those situations applies, spec_lev.com will default to - copying pre-genearated versions of the appropriate files (dgn_lex.c, + copying pre-generated versions of the appropriate files (dgn_lex.c, lev_lex.c, dgn_yacc.c, lev_yacc.c, dgn_comp.h, and lev_comp.h) from [.sys.share] into [.util]*.c and [.include]*.h. diff --git a/sys/vms/Install370.vms b/sys/vms/Install370.vms index 12d020c82..edca9e9d0 100644 --- a/sys/vms/Install370.vms +++ b/sys/vms/Install370.vms @@ -16,7 +16,7 @@ Last tested with VSI C x86-64 V7.5-009 (GEM 50XBR) on OpenVMS x86_64 V9.2-2. [.dat] -- data files [.doc] -- documentation files [.include] -- C header files - [.lib.lua546] -- Lua distribution from https://www.lua.org/ + [.lib.lua548] -- Lua distribution from https://www.lua.org/ [.src] -- primary source files [.sys] -- parent for [.sys.*] [.sys.share] -- files shared by several ports, including VMS diff --git a/sys/vms/Makefile.src b/sys/vms/Makefile.src index dee166f8e..20ee1643f 100644 --- a/sys/vms/Makefile.src +++ b/sys/vms/Makefile.src @@ -149,9 +149,9 @@ HACK_H = $(SRC)hack.h-t HACKCSRC = allmain.c alloc.c apply.c artifact.c attrib.c ball.c bones.c \ botl.c calendar.c cfgfiles.c cmd.c coloratt.c dbridge.c decl.c detect.c \ dig.c display.c dlb.c do.c do_name.c do_wear.c dog.c dogmove.c dokick.c \ - dothrow.c drawing.c dungeon.c eat.c end.c engrave.c exper.c \ + dothrow.c drawing.c dungeon.c earlyarg.c eat.c end.c engrave.c exper.c \ explode.c extralev.c files.c fountain.c getpos.c glyphs.c hack.c \ - hacklib.c insight.c invent.c light.c lock.c \ + hacklib.c iactions.c insight.c invent.c light.c lock.c \ mail.c makemon.c mcastu.c mhitm.c mhitu.c minion.c \ mklev.c mkmap.c mkmaze.c mkobj.c mkroom.c mon.c mondata.c \ monmove.c monst.c mplayer.c mthrowu.c muse.c music.c o_init.c \ @@ -198,9 +198,9 @@ HOBJ1 = allmain.obj,alloc.obj,apply.obj,artifact.obj,attrib.obj, \ coloratt.obj,dbridge.obj,decl.obj,detect.obj,dig.obj,display.obj, \ dlb.obj,do.obj,do_name.obj,do_wear.obj HOBJ2 = dog.obj,dogmove.obj,dokick.obj,dothrow.obj,drawing.obj, \ - dungeon.obj,eat.obj,end.obj,engrave.obj,exper.obj,explode.obj, \ + dungeon.obj,earlyarg.obj,eat.obj,end.obj,engrave.obj,exper.obj,explode.obj, \ extralev.obj,files.obj,fountain.obj,getpos.obj,glyphs.obj,hack.obj, \ - hacklib.obj,insight.obj,invent.obj + hacklib.obj,iactions.obj,insight.obj,invent.obj HOBJ3 = light.obj,lock.obj,mail.obj,makemon.obj,mcastu.obj, \ mhitm.obj,mhitu.obj,minion.obj,mklev.obj,mkmap.obj,mkmaze.obj, \ mkobj.obj,mkroom.obj,mon.obj,mondata.obj,monmove.obj @@ -519,6 +519,7 @@ dokick.obj : dokick.c $(HACK_H) dothrow.obj : dothrow.c $(HACK_H) drawing.obj : drawing.c $(HACK_H) $(INC)tcap.h dungeon.obj : dungeon.c $(HACK_H) $(INC)dgn_file.h $(INC)dlb.h +earlyarg.obj : earlyarg.c $(HACK_H) eat.obj : eat.c $(HACK_H) end.obj : end.c $(HACK_H) $(INC)dlb.h engrave.obj : engrave.c $(HACK_H) @@ -529,6 +530,7 @@ files.obj : files.c $(HACK_H) $(INC)dlb.h $(INC)wintty.h #zlib.h fountain.obj : fountain.c $(HACK_H) hack.obj : hack.c $(HACK_H) hacklib.obj : hacklib.c $(HACK_H) +iactions.obj : iactions.c $(HACK_H) insight.obj : insight.c $(HACK_H) invent.obj : invent.c $(HACK_H) light.obj : light.c $(HACK_H) diff --git a/sys/vms/Makefile_dat.vms b/sys/vms/Makefile_dat.vms index 37dd5e2d3..09979bb63 100644 --- a/sys/vms/Makefile_dat.vms +++ b/sys/vms/Makefile_dat.vms @@ -24,9 +24,9 @@ WINSHIM=[-.win.shim] WINCHAIN=[-.win.chain] # Lua location relative to src -LUAVER=546 -LUADOTVER=5.4.6 -LUAUNDERVER=5_4_6 +LUAVER=548 +LUADOTVER=5.4.8 +LUAUNDERVER=5_4_8 LUAINC=[-.lib.lua.lua$(LUAVER).src] LUALIB=[-.lib.lua]lua$(LUAVER).olb LUASRCDIR =[-.lib.lua.lua$(LUAVER).src] diff --git a/sys/vms/Makefile_doc.vms b/sys/vms/Makefile_doc.vms index f797df0b7..64184d092 100644 --- a/sys/vms/Makefile_doc.vms +++ b/sys/vms/Makefile_doc.vms @@ -25,9 +25,9 @@ WINSHIM=[-.win.shim] WINCHAIN=[-.win.chain] # Lua location relative to src -LUAVER=546 -LUADOTVER=5.4.6 -LUAUNDERVER=5_4_6 +LUAVER=548 +LUADOTVER=5.4.8 +LUAUNDERVER=5_4_8 LUAINC=[-.lib.lua.lua$(LUAVER).src] LUALIB=[-.lib.lua]lua$(LUAVER).olb LUASRCDIR =[-.lib.lua.lua$(LUAVER).src] diff --git a/sys/vms/Makefile_src.vms b/sys/vms/Makefile_src.vms index bf7355722..74f22ea55 100644 --- a/sys/vms/Makefile_src.vms +++ b/sys/vms/Makefile_src.vms @@ -35,9 +35,9 @@ WINSHIM=[-.win.shim] WINCHAIN=[-.win.chain] # Lua -LUAVER=546 -LUADOTVER=5.4.6 -LUAUNDERVER=5_4_6 +LUAVER=548 +LUADOTVER=5.4.8 +LUAUNDERVER=5_4_8 # Lua location relative to src LUAINC=[-.lib.lua.lua$(LUAVER).src] LUALIB=[-.lib.lua]lua$(LUAVER).olb @@ -117,10 +117,10 @@ HACK_H = $(addsuffix .h, $(addprefix $(INCL), $(CONFIGBASEH) $(HACKBASEH))) HACKFILES := allmain alloc apply artifact attrib ball bones botl \ calendar cfgfiles cmd coloratt dbridge decl detect \ dig display dlb do do_name do_wear dog dogmove dokick \ - dothrow drawing dungeon eat end engrave exper explode extralev \ - files fountain getpos glyphs hack hacklib insight invent isaac64 \ - light lock mail makemon mcastu mdlib mhitm mhitu minion mklev \ - mkmap mkmaze mkobj mkroom mon \ + dothrow drawing dungeon earlyarg eat end engrave exper explode \ + extralev files fountain getpos glyphs hack hacklib iactions \ + insight invent isaac64 light lock mail makemon mcastu mdlib mhitm \ + mhitu minion mklev mkmap mkmaze mkobj mkroom mon \ mondata monmove monst mplayer mthrowu muse music \ nhlua nhlsel nhlobj objnam o_init objects \ options pager pickup pline polyself potion pray \ @@ -735,6 +735,7 @@ $(TARGETPFX)getpos.obj: getpos.c $(HACK_H) $(TARGETPFX)glyphs.obj: glyphs.c $(HACK_H) $(TARGETPFX)hack.obj: hack.c $(HACK_H) $(TARGETPFX)hacklib.obj: hacklib.c $(HACK_H) +$(TARGETPFX)iactions.obj: iactions.c $(HACK_H) $(TARGETPFX)insight.obj: insight.c $(HACK_H) $(TARGETPFX)invent.obj: invent.c $(HACK_H) $(TARGETPFX)isaac64.obj: isaac64.c $(CONFIG_H) $(INCL)isaac64.h diff --git a/sys/vms/Makefile_top.vms b/sys/vms/Makefile_top.vms index 7a28ff098..47028c80d 100644 --- a/sys/vms/Makefile_top.vms +++ b/sys/vms/Makefile_top.vms @@ -54,9 +54,9 @@ WINSHIM=[.win.shim] WINCHAIN=[.win.chain] # Lua macros -LUAVER=546 -LUADOTVER=5.4.6 -LUAUNDERVER=5_4_6 +LUAVER=548 +LUADOTVER=5.4.8 +LUAUNDERVER=5_4_8 LUAINC=[.lib.lua.lua$(LUAVER).src] LUALIB=[.lib.lua]lua$(LUAVER).olb LUASRCDIR =[.lib.lua.lua$(LUAVER).src] diff --git a/sys/vms/Makefile_utl.vms b/sys/vms/Makefile_utl.vms index 83c112c95..c17283d13 100644 --- a/sys/vms/Makefile_utl.vms +++ b/sys/vms/Makefile_utl.vms @@ -30,9 +30,9 @@ WINSHIM=[-.win.shim] WINCHAIN=[-.win.chain] # Lua location relative to here -LUAVER=546 -LUADOTVER=5.4.6 -LUAUNDERVER=5_4_6 +LUAVER=548 +LUADOTVER=5.4.8 +LUAUNDERVER=5_4_8 LUAINC=[-.lib.lua.lua$(LUAVER).src] LUALIB=[-.lib.lua]lua$(LUAVER).olb LUASRCDIR =[-.lib.lua.lua$(LUAVER).src] diff --git a/sys/vms/vmsbuild.com b/sys/vms/vmsbuild.com index 737fbf015..6146cd5f1 100755 --- a/sys/vms/vmsbuild.com +++ b/sys/vms/vmsbuild.com @@ -28,9 +28,9 @@ $ ! one, use "" in the earlier one's position, such as $ ! $ @[-.sys.vms]vmsbuild "" "" "" "" "TTY+CURSES" $ ! $ ! Lua Version -$ luaver = "546" -$ luadotver = "5.4.6" -$ luaunderver = "5_4_6" +$ luaver = "548" +$ luadotver = "5.4.8" +$ luaunderver = "5_4_8" $ $ decc_dflt = f$trnlnm("DECC$CC_DEFAULT") $ j = (decc_dflt.nes."") .and. 1 @@ -425,7 +425,7 @@ $ c_list = "allmain,apply,artifact,attrib,ball,bones,botl,calendar,cmd" - + ",do_wear,dog,dogmove,dokick,dungeon,eat,end,engrave,exper,explode" - + ",extralev,files,fountain,getpos,glyphs" $ gosub compile_list -$ c_list = "hack,hacklib,insight,invent,light,lock,mail,makemon" - +$ c_list = "hack,hacklib,iactions,insight,invent,light,lock,mail,makemon" - + ",mcastu,mdlib,mhitm,mhitu,minion,mklev,mkmap,mkmaze" - + ",mkobj,mkroom,mon,mondata,monmove,mplayer,mthrowu,muse" - + ",music" @@ -453,7 +453,7 @@ $ gosub compile_list $ milestone "" $ link /EXECUTABLE=nethack.exe vmsmain.obj,date.obj- +[-.src]nethack.olb/library - - +sys$disk:[-.lib.lua]lua546.olb/library + +sys$disk:[-.lib.lua]lua548.olb/library $ milestone "NetHack" $ if c_opt.eq.o_LINK then goto done !"LINK" only $special: diff --git a/sys/windows/.gitattributes b/sys/windows/.gitattributes index 823c775c1..86544ef62 100644 --- a/sys/windows/.gitattributes +++ b/sys/windows/.gitattributes @@ -5,6 +5,6 @@ sysconf NHSUBST *.rc NHSUBST *.bat NHSUBST *.def NH_header=no -* NH_filestag=(file%s_for_Windows_7/8.x/10/11_version) +* NH_filestag=(file%s_for_Windows_10/11_version) ..files NH_filegenerated=nethack.ico nethack.ico NH_filesgentag=(file%s_generated_by_uudecode_at_compile_time) diff --git a/sys/windows/GNUmakefile b/sys/windows/GNUmakefile index aac5608d4..4bc3df970 100644 --- a/sys/windows/GNUmakefile +++ b/sys/windows/GNUmakefile @@ -104,7 +104,7 @@ USE_LUADLL = Y WANT_LUAC = N ifndef LUA_VERSION -LUAVER=5.4.6 +LUAVER=5.4.8 else LUAVER=$(LUA_VERSION) endif @@ -299,6 +299,18 @@ USE_DLB = Y #========================================== #========================================== +ifeq "$(MSYSTEM)" "MINGW32" +arch = x86 +else +arch = x64 +endif +MACH := $(shell echo $$PROCESSOR_IDENTIFIER | cut -f1 -d ' ') +MACH := $(strip $(MACH)) +# Print the detected architecture (for debugging) +#$(info Detected Architecture:$(MACH).) +ifeq "$(MACH)" "ARMv8" +arch=arm64 +endif ifdef CI_COMPILER cc = gcc -c cxx = g++ -c @@ -311,16 +323,17 @@ ld = gcc endif ifeq "$(MSYSTEM)" "MINGW32" rc = windres --target=pe-i386 -else # MINGW64 +else # MINGW64 or arm64 +ifeq "$(arch)" "arm64" +#rc = windres --target=pe-arm64 +rc = windres +else rc = windres --target=pe-x86-64 endif - -ifeq "$(MSYSTEM)" "MINGW32" -arch = x86 -else # MINGW64 -arch = x64 endif + + # # Handle user settings # @@ -380,7 +393,7 @@ CLEAN_DIR = $(GAMEDIR) $(OBJ) #================================================================= # LUA library -# Source from http://www.lua.org/ftp/lua-5.4.6.tar.gz +# Source from http://www.lua.org/ftp/lua-5.4.8.tar.gz #================================================================= OLUA = $(O)lua @@ -414,8 +427,15 @@ $(U)lua.exe: $(OLUA)/lua.o $(LUALIB) $(LUADLL): $(ULUADLL) cp $< $@ + +ifeq "$(arch)" "arm64" +lparam=-Wl,--export-all-symbols +else +lparam=-Wl,--export-all-symbols -Wl,--add-stdcall-alias +endif + $(ULUADLL) $(LUAIMP): $(LUAOBJS) | $(OLUA) - $(ld) $(LDFLAGS) -fPIC -shared -Wl,--export-all-symbols -Wl,--add-stdcall-alias \ + $(ld) $(LDFLAGS) -fPIC -shared $(lparam) \ -Wl,--out-implib=$(LUAIMP) $^ -o$(ULUADLL) $(LUASTATIC): $(LUAOBJS) | $(OLUA) @@ -455,7 +475,7 @@ CLEAN_FILE += $(NHLUAH) LUALIST = air Arc-fila Arc-filb Arc-goal Arc-loca Arc-strt \ asmodeus astral baalz Bar-fila Bar-filb Bar-goal \ Bar-loca Bar-strt bigrm-1 bigrm-10 bigrm-11 bigrm-2 \ - bigrm-12 \ + bigrm-12 bigrm-13 \ bigrm-3 bigrm-4 bigrm-5 bigrm-6 bigrm-7 bigrm-8 \ bigrm-9 castle Cav-fila Cav-filb Cav-goal Cav-loca \ Cav-strt dungeon earth fakewiz1 fakewiz2 fire \ @@ -774,6 +794,7 @@ HACK_H = $(CONFIG_H) ../include/align.h ../include/artilist.h \ ../include/decl.h ../include/defsym.h ../include/display.h \ ../include/dungeon.h ../include/engrave.h ../include/flag.h \ ../include/hack.h ../include/lint.h ../include/mextra.h \ + ../include/mcastu.h \ ../include/mkroom.h ../include/monattk.h ../include/mondata.h \ ../include/monflag.h ../include/monst.h ../include/monsters.h \ ../include/nhlua.h ../include/obj.h ../include/objclass.h \ @@ -1045,8 +1066,8 @@ fetchpdcurses: git submodule update ../submodules/$(PDCURSES) ; fi # git submodule update --remote ../submodules/$(PDCURSES) ; fi else # GIT_AVAILABLE no -CURLLUASRC=http://www.lua.org/ftp/lua-5.4.6.tar.gz -CURLLUADST=lua-5.4.6.tar.gz +CURLLUASRC=http://www.lua.org/ftp/lua-5.4.8.tar.gz +CURLLUADST=lua-5.4.8.tar.gz CURLPDCSRC=https://github.com/Bill-Gray/PDCursesMod/archive/refs/tags/v4.5.1.zip CURLPDCDST=$(PDCURSES).zip @@ -1211,21 +1232,21 @@ COREOBJS = $(addsuffix .o, allmain alloc apply artifact attrib ball bones botl \ calendar cfgfiles cmd coloratt cppregex \ dbridge decl detect dig display dlb do do_name do_wear \ dog dogmove dokick dothrow drawing dungeon \ - eat end engrave exper explode extralev files fountain \ - getpos glyphs hack insight invent isaac64 light lock \ + earlyarg eat end engrave exper explode extralev files fountain \ + getpos glyphs hack iactions insight invent isaac64 light lock \ mail makemon mcastu mdlib mhitm mhitu minion mklev mkmap mkmaze mkobj mkroom \ mon mondata monmove monst mplayer mthrowu muse music \ nhlobj nhlsel nhlua windsound o_init objects objnam options \ pager pickup pline polyself potion pray priest quest questpgr \ random read rect region report restore rip rnd role rumors \ - safeproc save sfbase sfstruct shk shknam sit selvar sounds sp_lev \ + save sfbase sfstruct shk shknam sit selvar sounds sp_lev \ spell stairs steal steed strutil \ symbols sys teleport timeout topten track trap u_init uhitm utf8map \ vault version vision weapon were wield windmain windows windsys wizard \ wizcmds worm worn write zap $(SOUNDLIBOBJS)) -CFLAGSW = $(CFLAGS) $(CFLAGSXTRA) $(SOUNDLIBINCL) $(COMMONDEF) $(DLBFLG) -DTILES -D_WINDOWS -DMSWIN_GRAPHICS -DSAFEPROCS -DNOTTYGRAPHICS $(SOUNDLIBDEFS) -CPPFLAGSW = $(CFLAGS) $(CPPFLAGSXTRA) $(COMMONDEF) $(DLBFLG) -DTILES -D_WINDOWS -DMSWIN_GRAPHICS -DSAFEPROCS -DNOTTYGRAPHICS $(SOUNDLIBDEFS) +CFLAGSW = $(CFLAGS) $(CFLAGSXTRA) $(SOUNDLIBINCL) $(COMMONDEF) $(DLBFLG) -DTILES -D_WINDOWS -DMSWIN_GRAPHICS -DNOTTYGRAPHICS $(SOUNDLIBDEFS) +CPPFLAGSW = $(CFLAGS) $(CPPFLAGSXTRA) $(COMMONDEF) $(DLBFLG) -DTILES -D_WINDOWS -DMSWIN_GRAPHICS -DNOTTYGRAPHICS $(SOUNDLIBDEFS) ONHW = $(O)nethackw NHWONLY = $(addsuffix .o, mhaskyn mhdlg mhfont mhinput mhmain mhmap mhmenu \ @@ -1327,8 +1348,8 @@ CLEAN_FILE += $(NHWTARGETS) $(NHWOBJS) $(NHWRES) $(BMPS) $(WAV) #========================================== # nethack #========================================== -CFLAGSNH = $(CFLAGSU) $(CFLAGSXTRA) $(SOUNDLIBINCL) -DNO_TILE_C -DSAFEPROCS -D_LIB -DWIN32CON $(SOUNDLIBDEFS) -CPPFLAGSNH = $(CFLAGSU) $(CPPFLAGSXTRA) -DNO_TILE_C -DSAFEPROCS -D_LIB -DWIN32CON $(SOUNDLIBDEFS) +CFLAGSNH = $(CFLAGSU) $(CFLAGSXTRA) $(SOUNDLIBINCL) -DNO_TILE_C -D_LIB -DWIN32CON $(SOUNDLIBDEFS) +CPPFLAGSNH = $(CFLAGSU) $(CPPFLAGSXTRA) -DNO_TILE_C -D_LIB -DWIN32CON $(SOUNDLIBDEFS) ONH = $(O)nethack @@ -1357,7 +1378,16 @@ $(ONH)/%.o: $(SRC)/%.c $(NHLUAH) | $(ONH) #========================================== # package #========================================== +ifeq "$(arch)" "x64" TARGET_CPU = x64 +else +ifeq "$(arch)" "arm64" +TARGET_CPU = arm64 +else +TARGET_CPU = x86 +endif +endif + NHV=370 PKGFILES = nethackrc.template Guidebook.txt license NetHack.exe NetHack.txt \ NetHackW.exe opthelp nhdat370 record symbols sysconf.template $(notdir $(LUADLL)) diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index 2d92eb582..d450ecbe8 100644 --- a/sys/windows/Makefile.nmake +++ b/sys/windows/Makefile.nmake @@ -1,5 +1,5 @@ # NetHack 3.7 Makefile.nmake -# Copyright (c) NetHack Development Team 1993-2025 +# Copyright (c) NetHack Development Team 1993-2026 # #============================================================================== # Build Tools Environment @@ -8,8 +8,8 @@ # MS Visual Studio Visual C++ compiler # # Visual Studio Compilers Tested: -# - Microsoft Visual Studio 2019 Community Edition v 16.11.42 -# - Microsoft Visual Studio 2022 Community Edition v 17.14.13 +# - Microsoft Visual Studio Community 2022 v 17.14.29 +# - Microsoft Visual Studio Community 2026 v 18.4.2 # #============================================================================== # This is used for building two distinct executables of NetHack: @@ -107,6 +107,7 @@ GIT_AVAILABLE = N #TARGET_CPU=x64 #TARGET_CPU=x86 +#TARGET_CPU=arm64 #============================================================================== #======================== End of Modification Section ========================= @@ -141,7 +142,7 @@ Q=@ SKIP_NETHACKW = N !IFNDEF LUA_VERSION -LUAVER=5.4.6 +LUAVER=5.4.8 !ELSE LUAVER=$(LUA_VERSION) !ENDIF @@ -242,7 +243,7 @@ GAMEDIR = $(R_GAMEDIR)$(BACKSLASH) #U_BinDir = $(BinDir:\=/) #U_PkgDir = $(PkgDir:\=/) #U_SOUNDDIR = $(SOUNDDIR:\=/) -U_GAMEDIR = $(GAMEDIR:\=/) +U_GAMEDIR = $(R_GAMEDIR:\=/) # #!MESSAGE $(U_SRC) #!MESSAGE $(U_GAMEDIR) @@ -393,6 +394,7 @@ ADD_CURSES = N #developer command prompts. #VSCMD_ARG_HOST_ARCH=x64 #VSCMD_ARG_TGT_ARCH=x86 +#VSCMD_ARG_TGT_ARCH=arm64 # We need to do this here, so some output files can # incorporate TARGET_CPU into their names. @@ -404,7 +406,11 @@ ADD_CURSES = N ! IF "$(VSCMD_ARG_TGT_ARCH)"=="x64" TARGET_CPU=x64 ! ELSE +! IF "$(VSCMD_ARG_TGT_ARCH)"=="arm64" +TARGET_CPU=arm64 +! ELSE TARGET_CPU=x86 +! ENDIF ! ENDIF ! ENDIF !ENDIF @@ -460,37 +466,37 @@ DLBDEF = # Object file directories. # -R_OBJTTY_B = objtty -R_OBJGUI_B = objgui -R_OBJUTIL_B = objutil -R_OBJLUA_B = objlua -R_OBJPDC_B = objpdc -R_OBJPDCC_B = objpdcc -R_OBJPDCG_B = objpdcg +R_OBJTTY_B = objtty +R_OBJGUI_B = objgui +R_OBJUTIL_B = objutil +R_OBJLUA_B = objlua +R_OBJPDC_B = objpdc +R_OBJPDCTXT_B = objpdctxt +R_OBJPDCGUI_B = objpdcgui OBJTTY_B = $(R_OBJTTY_B)$(BACKSLASH) OBJGUI_B = $(R_OBJGUI_B)$(BACKSLASH) OBJUTIL_B = $(R_OBJUTIL_B)$(BACKSLASH) OBJLUA_B = $(R_OBJLUA_B)$(BACKSLASH) OBJPDC_B = $(R_OBJPDC_B)$(BACKSLASH) -OBJPDCC_B = $(R_OBJPDCC_B)$(BACKSLASH) -OBJPDCG_B = $(R_OBJPDCG_B)$(BACKSLASH) +OBJPDCTXT_B = $(R_OBJPDCTXT_B)$(BACKSLASH) +OBJPDCGUI_B = $(R_OBJPDCGUI_B)$(BACKSLASH) R_OBJTTY = $(OBJTTY_B)$(TARGET_CPU) R_OBJGUI = $(OBJGUI_B)$(TARGET_CPU) R_OBJUTIL = $(OBJUTIL_B)$(TARGET_CPU) R_OBJLUA = $(OBJLUA_B)$(TARGET_CPU) R_OBJPDC = $(OBJPDC_B)$(TARGET_CPU) -R_OBJPDCC = $(OBJPDCC_B)$(TARGET_CPU) -R_OBJPDCG = $(OBJPDCG_B)$(TARGET_CPU) +R_OBJPDCTXT = $(OBJPDCTXT_B)$(TARGET_CPU) +R_OBJPDCGUI = $(OBJPDCGUI_B)$(TARGET_CPU) OBJTTY = $(R_OBJTTY)$(BACKSLASH) OBJGUI = $(R_OBJGUI)$(BACKSLASH) OBJUTIL = $(R_OBJUTIL)$(BACKSLASH) OBJLUA = $(R_OBJLUA)$(BACKSLASH) OBJPDC = $(R_OBJPDC)$(BACKSLASH) -OBJPDCC = $(R_OBJPDCC)$(BACKSLASH) -OBJPDCG = $(R_OBJPDCG)$(BACKSLASH) +OBJPDCTXT = $(R_OBJPDCTXT)$(BACKSLASH) +OBJPDCGUI = $(R_OBJPDCGUI)$(BACKSLASH) # # Shorten up the location for some files @@ -501,8 +507,8 @@ OGUI = $(OBJGUI) OUTL = $(OBJUTIL) OLUA = $(OBJLUA) OPDC = $(OBJPDC) -OPDCC = $(OBJPDCC) -OPDCG = $(OBJPDCG) +OPDCTXT = $(OBJPDCTXT) +OPDCGUI = $(OBJPDCGUI) U = $(UTIL) @@ -528,9 +534,9 @@ GITBRANCH = -DNETHACK_GIT_BRANCH=\"$(GIT_BRANCH)\" GITPREFIX = -DNETHACK_GIT_PREFIX=\"$(GIT_PREFIX)\" !ENDIF -#===============-================================================= +#================================================================= # LUA library -# Official source for Lua is http://www.lua.org/ftp/lua-5.4.6.tar.gz +# Official source for Lua is http://www.lua.org/ftp/lua-5.4.8.tar.gz # # Source for the NetHack repository submodule in ../submodules/lua # is https://github.com/lua/lua.git @@ -539,7 +545,7 @@ GITPREFIX = -DNETHACK_GIT_PREFIX=\"$(GIT_PREFIX)\" # Location of LUA # # Original source needs to be obtained from: -# http://www.lua.org/ftp/lua-5.4.6.tar.gz +# http://www.lua.org/ftp/lua-5.4.8.tar.gz # # This build assumes that the LUA sources are located # at the specified location. If they are actually elsewhere @@ -549,7 +555,7 @@ GITPREFIX = -DNETHACK_GIT_PREFIX=\"$(GIT_PREFIX)\" # !IFNDEF LUAVER -LUAVER=5.4.6 +LUAVER=5.4.8 !ENDIF LUALIB = $(LIBDIR)lua$(LUAVER)-$(TARGET_CPU)-static.lib @@ -592,20 +598,23 @@ LUAOBJFILES = $(LUAOBJFILES) $(OLUA)lbitlib.o #================================================================= !IF "$(ADD_CURSES)" == "Y" +PDCTXTFOLDER = wincon +#PDCTXTFOLDER = vt PDCURSES_CURSES_H = $(PDCURSES_TOP)curses.h PDCURSES_CURSPRIV_H = $(PDCURSES_TOP)curspriv.h PDCURSES_HEADERS = $(PDCURSES_CURSES_H) $(PDCURSES_CURSPRIV_H) R_PDCSRC = $(PDCURSES_TOP)pdcurses PDCSRC = $(R_PDCSRC)$(BACKSLASH) !IF "$(CURSES_CONSOLE)" == "Y" -R_PDCWINCON = $(PDCURSES_TOP)wincon -PDCWINCON = $(PDCURSES_TOP)wincon$(BACKSLASH) +R_PDCTXT = $(PDCURSES_TOP)$(PDCTXTFOLDER) +PDCTXT = $(PDCURSES_TOP)$(PDCTXTFOLDER)$(BACKSLASH) +#!MESSAGE PDCTXT = $(PDCTXT) !ENDIF !IF "$(CURSES_GRAPHICAL)" == "Y" -R_PDCWINGUI = $(PDCURSES_TOP)wingui -PDCWINGUI = $(PDCURSES_TOP)wingui$(BACKSLASH) +R_PDCGUI = $(PDCURSES_TOP)wingui +PDCGUI = $(PDCURSES_TOP)wingui$(BACKSLASH) !ENDIF -PDCDEP = $(PDCURSES_TOP)curses.h +PDCDEP = $(PDCURSES_TOP)curses.h PDCCOMMONOBJS = $(OPDC)addch.o $(OPDC)addchstr.o $(OPDC)addstr.o $(OPDC)attr.o $(OPDC)beep.o \ $(OPDC)bkgd.o $(OPDC)border.o $(OPDC)clear.o $(OPDC)color.o $(OPDC)debug.o \ @@ -619,23 +628,23 @@ PDCCOMMONOBJS = $(OPDC)addch.o $(OPDC)addchstr.o $(OPDC)addstr.o $(OPDC)attr.o $ $(OPDC)util.o $(OPDC)window.o PDCINCL = /I$(R_PDCURSES_TOP) /I$(R_PDCSRC) !IF "$(CURSES_CONSOLE)" == "Y" -PDCINCLCON = /I$(PDCWINCON) -PDCWINCONOBJS = $(OPDCC)pdcclip.o $(OPDCC)pdcdisp.o $(OPDCC)pdcgetsc.o \ - $(OPDCC)pdckbd.o $(OPDCC)pdcscrn.o $(OPDCC)pdcsetsc.o $(OPDCC)pdcutil.o +PDCINCLTXT = /I$(R_PDCTXT) +PDCTXTOBJS = $(OPDCTXT)pdcclip.o $(OPDCTXT)pdcdisp.o $(OPDCTXT)pdcgetsc.o \ + $(OPDCTXT)pdckbd.o $(OPDCTXT)pdcscrn.o $(OPDCTXT)pdcsetsc.o $(OPDCTXT)pdcutil.o !ENDIF !IF "$(CURSES_GRAPHICAL)" == "Y" -PDCWINGUIOBJS = $(OPDCG)pdcclip.o $(OPDCG)pdcdisp.o $(OPDCG)pdcgetsc.o \ - $(OPDCG)pdckbd.o $(OPDCG)pdcscrn.o $(OPDCG)pdcsetsc.o $(OPDCG)pdcutil.o -PDCINCLGUI = /I$(PDCWINCON) +PDCGUIOBJS = $(OPDCGUI)pdcclip.o $(OPDCGUI)pdcdisp.o $(OPDCGUI)pdcgetsc.o \ + $(OPDCGUI)pdckbd.o $(OPDCGUI)pdcscrn.o $(OPDCGUI)pdcsetsc.o $(OPDCGUI)pdcutil.o +PDCINCLGUI = /I$(R_PDCGUI) !ENDIF !ELSE PDCDEP= PDCCOMMONOBJS= -PDCWINCONOBJS= -PDCWINGUIOBJS= +PDCTXTOBJS= +PDCGUIOBJS= PDCINCL= PDINCLGUI= -PDCINCLCON= +PDCINCLTXT= !ENDIF @@ -644,67 +653,68 @@ PDCINCLCON= # windowing-system specific HACKCSRC = \ - $(SRC)allmain.c $(SRC)alloc.c $(SRC)apply.c $(SRC)artifact.c \ - $(SRC)attrib.c $(SRC)ball.c $(SRC)bones.c $(SRC)botl.c \ - $(SRC)calendar.c $(SRC)cmd.c $(SRC)coloratt.c $(SRC)dbridge.c \ - $(SRC)decl.c $(SRC)detect.c $(SRC)dig.c $(SRC)display.c \ - $(SRC)dlb.c $(SRC)do.c $(SRC)do_name.c $(SRC)do_wear.c \ - $(SRC)dog.c $(SRC)dogmove.c $(SRC)dokick.c $(SRC)dothrow.c \ - $(SRC)drawing.c $(SRC)dungeon.c $(SRC)eat.c $(SRC)end.c \ - $(SRC)engrave.c $(SRC)exper.c $(SRC)explode.c $(SRC)files.c \ - $(SRC)fountain.c $(SRC)getpos.c $(SRC)glyphs.c $(SRC)hack.c \ - $(SRC)insight.c $(SRC)invent.c $(SRC)isaac64.c $(SRC)light.c \ - $(SRC)lock.c $(SRC)mail.c $(SRC)makemon.c $(SRC)mcastu.c \ - $(SRC)mdlib.c $(SRC)mhitm.c $(SRC)mhitu.c $(SRC)minion.c \ - $(SRC)mklev.c $(SRC)mkmap.c $(SRC)mkmaze.c $(SRC)mkobj.c \ - $(SRC)mkroom.c $(SRC)mon.c $(SRC)mondata.c $(SRC)monmove.c \ - $(SRC)monst.c $(SRC)mplayer.c $(SRC)mthrowu.c $(SRC)muse.c \ - $(SRC)music.c $(SRC)nhlua.c $(SRC)nhlsel.c $(SRC)nhlobj.c \ - $(SRC)o_init.c $(SRC)objects.c $(SRC)objnam.c $(SRC)options.c \ - $(SRC)pager.c $(SRC)pickup.c $(SRC)pline.c $(SRC)polyself.c \ - $(SRC)potion.c $(SRC)pray.c $(SRC)priest.c $(SRC)quest.c \ - $(SRC)questpgr.c $(SRC)read.c $(SRC)rect.c $(SRC)region.c \ - $(SRC)restore.c $(SRC)rip.c $(SRC)rnd.c $(SRC)role.c \ - $(SRC)rumors.c $(SRC)save.c $(SRC)selvar.c\ - $(SRC)sfbase.c $(SRC)sfstruct.c \ - $(SRC)shk.c $(SRC)shknam.c $(SRC)sit.c $(SRC)sounds.c \ - $(SRC)sp_lev.c $(SRC)spell.c $(SRC)stairs.c $(SRC)steal.c \ - $(SRC)steed.c $(SRC)symbols.c $(SRC)sys.c $(SRC)teleport.c \ - $(SRC)timeout.c $(SRC)topten.c $(SRC)track.c $(SRC)trap.c \ - $(SRC)u_init.c $(SRC)uhitm.c $(SRC)utf8map.c $(SRC)vault.c \ - $(SRC)version.c $(SRC)vision.c $(SRC)weapon.c $(SRC)were.c \ - $(SRC)wield.c $(SRC)windows.c $(SRC)wizard.c $(SRC)worm.c \ - $(SRC)worn.c $(SRC)write.c $(SRC)zap.c + $(SRC)allmain.c $(SRC)alloc.c $(SRC)apply.c $(SRC)artifact.c \ + $(SRC)attrib.c $(SRC)ball.c $(SRC)bones.c $(SRC)botl.c \ + $(SRC)calendar.c $(SRC)cmd.c $(SRC)coloratt.c $(SRC)dbridge.c \ + $(SRC)decl.c $(SRC)detect.c $(SRC)dig.c $(SRC)display.c \ + $(SRC)dlb.c $(SRC)do.c $(SRC)do_name.c $(SRC)do_wear.c \ + $(SRC)dog.c $(SRC)dogmove.c $(SRC)dokick.c $(SRC)dothrow.c \ + $(SRC)drawing.c $(SRC)dungeon.c $(SRC)earlyarg.c $(SRC)eat.c \ + $(SRC)end.c $(SRC)engrave.c $(SRC)exper.c $(SRC)explode.c \ + $(SRC)files.c $(SRC)fountain.c $(SRC)getpos.c $(SRC)glyphs.c \ + $(SRC)hack.c $(SRC)iactions.c $(SRC)insight.c $(SRC)invent.c \ + $(SRC)isaac64.c $(SRC)light.c $(SRC)lock.c $(SRC)mail.c \ + $(SRC)makemon.c $(SRC)mcastu.c $(SRC)mdlib.c $(SRC)mhitm.c \ + $(SRC)mhitu.c $(SRC)minion.c $(SRC)mklev.c $(SRC)mkmap.c \ + $(SRC)mkmaze.c $(SRC)mkobj.c $(SRC)mkroom.c $(SRC)mon.c \ + $(SRC)mondata.c $(SRC)monmove.c $(SRC)monst.c $(SRC)mplayer.c \ + $(SRC)mthrowu.c $(SRC)muse.c $(SRC)music.c $(SRC)nhlua.c \ + $(SRC)nhlsel.c $(SRC)nhlobj.c $(SRC)o_init.c $(SRC)objects.c \ + $(SRC)objnam.c $(SRC)options.c $(SRC)pager.c $(SRC)pickup.c \ + $(SRC)pline.c $(SRC)polyself.c $(SRC)potion.c $(SRC)pray.c \ + $(SRC)priest.c $(SRC)quest.c $(SRC)questpgr.c $(SRC)read.c \ + $(SRC)rect.c $(SRC)region.c $(SRC)restore.c $(SRC)rip.c \ + $(SRC)rnd.c $(SRC)role.c $(SRC)rumors.c $(SRC)save.c \ + $(SRC)selvar.c $(SRC)sfbase.c $(SRC)sfstruct.c $(SRC)shk.c \ + $(SRC)shknam.c $(SRC)sit.c $(SRC)sounds.c $(SRC)sp_lev.c \ + $(SRC)spell.c $(SRC)stairs.c $(SRC)steal.c $(SRC)steed.c \ + $(SRC)symbols.c $(SRC)sys.c $(SRC)teleport.c $(SRC)timeout.c \ + $(SRC)topten.c $(SRC)track.c $(SRC)trap.c $(SRC)u_init.c \ + $(SRC)uhitm.c $(SRC)utf8map.c $(SRC)vault.c $(SRC)version.c \ + $(SRC)vision.c $(SRC)weapon.c $(SRC)were.c $(SRC)wield.c \ + $(SRC)windows.c $(SRC)wizard.c $(SRC)worm.c $(SRC)worn.c \ + $(SRC)write.c $(SRC)zap.c # all .h files except date.h HACKINCL = \ - $(INCL)align.h $(INCL)artifact.h $(INCL)artilist.h \ - $(INCL)attrib.h $(INCL)botl.h $(INCL)color.h \ - $(INCL)config.h $(INCL)config1.h $(INCL)context.h \ - $(INCL)coord.h $(INCL)cstd.h $(INCL)decl.h \ - $(INCL)defsym.h $(INCL)display.h $(INCL)dlb.h \ - $(INCL)dungeon.h $(INCL)engrave.h $(INCL)extern.h \ - $(INCL)flag.h $(INCL)fnamesiz.h $(INCL)func_tab.h \ - $(INCL)global.h $(INCL)warnings.h $(INCL)hack.h \ - $(INCL)lint.h $(INCL)mextra.h $(INCL)micro.h \ - $(INCL)mfndpos.h $(INCL)mkroom.h $(INCL)monattk.h \ - $(INCL)mondata.h $(INCL)monflag.h $(INCL)monst.h \ - $(INCL)monsters.h $(INCL)nhmd4.h $(INCL)obj.h \ - $(INCL)objects.h $(INCL)objclass.h $(INCL)optlist.h \ - $(INCL)patchlevel.h $(INCL)pcconf.h $(INCL)permonst.h \ - $(INCL)prop.h $(INCL)rect.h $(INCL)region.h \ - $(INCL)savefile.h $(INCL)selvar.h $(INCL)sfprocs.h \ - $(INCL)sym.h $(INCL)rm.h $(INCL)sp_lev.h \ - $(INCL)spell.h $(INCL)sndprocs.h $(INCL)seffects.h \ - $(INCL)stairs.h $(INCL)sys.h $(INCL)tcap.h \ - $(INCL)timeout.h $(INCL)tradstdc.h $(INCL)trap.h \ - $(INCL)unixconf.h $(INCL)vision.h $(INCL)vmsconf.h \ - $(INCL)wintty.h $(INCL)wincurs.h $(INCL)winX.h \ - $(INCL)winprocs.h $(INCL)wintype.h $(INCL)you.h \ - $(INCL)youprop.h $(INCL)weight.h + $(INCL)align.h $(INCL)artifact.h $(INCL)artilist.h \ + $(INCL)attrib.h $(INCL)botl.h $(INCL)color.h \ + $(INCL)config.h $(INCL)config1.h $(INCL)context.h \ + $(INCL)coord.h $(INCL)cstd.h $(INCL)decl.h \ + $(INCL)defsym.h $(INCL)display.h $(INCL)dlb.h \ + $(INCL)dungeon.h $(INCL)engrave.h $(INCL)extern.h \ + $(INCL)flag.h $(INCL)fnamesiz.h $(INCL)func_tab.h \ + $(INCL)global.h $(INCL)warnings.h $(INCL)hack.h \ + $(INCL)lint.h $(INCL)mextra.h $(INCL)micro.h \ + $(INCL)mcastu.h $(INCL)mfndpos.h $(INCL)mkroom.h \ + $(INCL)monattk.h $(INCL)mondata.h $(INCL)monflag.h \ + $(INCL)monst.h $(INCL)monsters.h $(INCL)nhmd4.h \ + $(INCL)obj.h $(INCL)objects.h $(INCL)objclass.h \ + $(INCL)optlist.h $(INCL)patchlevel.h $(INCL)pcconf.h \ + $(INCL)permonst.h $(INCL)prop.h $(INCL)rect.h \ + $(INCL)region.h $(INCL)savefile.h $(INCL)selvar.h \ + $(INCL)sfprocs.h $(INCL)sym.h $(INCL)rm.h \ + $(INCL)sp_lev.h $(INCL)spell.h $(INCL)sndprocs.h \ + $(INCL)seffects.h $(INCL)stairs.h $(INCL)sys.h \ + $(INCL)tcap.h $(INCL)timeout.h $(INCL)tradstdc.h \ + $(INCL)trap.h $(INCL)unixconf.h $(INCL)vision.h \ + $(INCL)vmsconf.h $(INCL)wintty.h $(INCL)wincurs.h \ + $(INCL)winX.h $(INCL)winprocs.h $(INCL)wintype.h \ + $(INCL)you.h $(INCL)youprop.h $(INCL)weight.h LUA_FILES = $(DAT)asmodeus.lua $(DAT)baalz.lua $(DAT)bigrm-1.lua \ $(DAT)bigrm-10.lua $(DAT)bigrm-11.lua $(DAT)bigrm-12.lua \ + $(DAT)bigrm-13.lua \ $(DAT)bigrm-2.lua $(DAT)bigrm-3.lua $(DAT)bigrm-4.lua \ $(DAT)bigrm-5.lua $(DAT)bigrm-6.lua $(DAT)bigrm-7.lua \ $(DAT)bigrm-8.lua $(DAT)bigrm-9.lua $(DAT)castle.lua \ @@ -797,14 +807,14 @@ CURSESOBJ= $(OTTY)cursdial.o $(OTTY)cursinit.o $(OTTY)cursinvt.o \ $(OTTY)cursstat.o $(OTTY)curswins.o !IF "$(CURSES_CONSOLE)" == "Y" CURSESWINCONOBJS = $(CURSESOBJ) -PDCWINCONLIB = $(LIBDIR)$(PDCDIST)-wincon-$(TARGET_CPU)-static.lib +PDCTXTLIB = $(LIBDIR)$(R_PDCDIST)-$(PDCTXTFOLDER)-$(TARGET_CPU)-static.lib CURSESCONDEF1=-D"CURSES_GRAPHICS" -DPDC_NCMOUSE #CURSESDEF2=-D"CURSES_BRIEF_INCLUDE" -DCHTYPE_32 CURSESDEF2=-DCURSES_UNICODE $(PDCURSESFLAGS) !ENDIF !IF "$(CURSES_GRAPHICAL)" == "Y" CURSESWINGUIOBJS = $(CURSESOBJ) $(OGUI)guitty.o -PDCWINGUILIB = $(LIBDIR)$(PDCDIST)-wingui-$(TARGET_CPU)-static.lib +PDCGUILIB = $(LIBDIR)$(R_PDCDIST)-wingui-$(TARGET_CPU)-static.lib CURSESGUIDEF1=-D"CURSES_GRAPHICS" -DPDC_NCMOUSE #CURSESDEF2=-D"CURSES_BRIEF_INCLUDE" -DCHTYPE_32 CURSESDEF2=-DCURSES_UNICODE $(PDCURSESFLAGS) @@ -816,8 +826,8 @@ CURSESDEF2=-DCURSES_UNICODE $(PDCURSESFLAGS) !UNDEF CURSESOBJ !UNDEF CURSESWINCONOBJS !UNDEF CURSESWINGUIOBJS -!UNDEF PDCWINCONLIB -!UNDEF PDCWINGUILIB +!UNDEF PDCTXTLIB +!UNDEF PDCGUILIB !ENDIF OUTLHACKLIBOBJS = $(OUTL)hacklib.o @@ -840,8 +850,7 @@ REGEXTTY = $(OTTY)cppregex.o LUAOBJTTY = $(OTTY)nhlua.o $(OTTY)nhlsel.o $(OTTY)nhlobj.o VVOBJTTY = $(OTTY)version.o -SOBJTTY = $(OTTY)windmain.o $(OTTY)windsys.o $(OTTY)win10.o \ - $(OTTY)safeproc.o +SOBJTTY = $(OTTY)windmain.o $(OTTY)windsys.o $(OTTY)win10.o TTYOBJTTY = $(OTTY)topl.o $(OTTY)getline.o $(OTTY)wintty.o @@ -852,30 +861,31 @@ COREOBJTTY = \ $(OTTY)dbridge.o $(OTTY)decl.o $(OTTY)detect.o $(OTTY)dig.o \ $(OTTY)display.o $(OTTY)do.o $(OTTY)do_name.o $(OTTY)do_wear.o \ $(OTTY)dog.o $(OTTY)dogmove.o $(OTTY)dokick.o $(OTTY)dothrow.o \ - $(OTTY)drawing.o $(OTTY)dungeon.o $(OTTY)eat.o $(OTTY)end.o \ - $(OTTY)engrave.o $(OTTY)exper.o $(OTTY)explode.o $(OTTY)extralev.o \ - $(OTTY)files.o $(OTTY)fountain.o $(OTTY)getpos.o $(OTTY)glyphs.o \ - $(OTTY)hack.o $(OTTY)insight.o $(OTTY)invent.o $(OTTY)isaac64.o \ - $(OTTY)light.o $(OTTY)lock.o $(OTTY)mail.o $(OTTY)makemon.o \ - $(OTTY)mcastu.o $(OTTY)mhitm.o $(OTTY)mhitu.o $(OTTY)minion.o \ - $(OTTY)mklev.o $(OTTY)mkmap.o $(OTTY)mkmaze.o $(OTTY)mkobj.o \ - $(OTTY)mkroom.o $(OTTY)mon.o $(OTTY)mondata.o $(OTTY)monmove.o \ - $(OTTY)monst.o $(OTTY)mplayer.o $(OTTY)mthrowu.o $(OTTY)muse.o \ - $(OTTY)music.o $(OTTY)o_init.o $(OTTY)objects.o $(OTTY)objnam.o \ - $(OTTY)options.o $(OTTY)pager.o $(OTTY)pickup.o $(OTTY)pline.o \ - $(OTTY)polyself.o $(OTTY)potion.o $(OTTY)pray.o $(OTTY)priest.o \ - $(OTTY)quest.o $(OTTY)questpgr.o $(OTTY)read.o $(OTTY)rect.o \ - $(OTTY)region.o $(OTTY)report.o $(OTTY)restore.o $(OTTY)rip.o \ - $(OTTY)rnd.o $(OTTY)role.o $(OTTY)rumors.o $(OTTY)save.o \ - $(OTTY)selvar.o $(OTTY)sfbase.o $(OTTY)sfstruct.o $(OTTY)shk.o \ - $(OTTY)shknam.o $(OTTY)sit.o $(OTTY)sounds.o $(OTTY)sp_lev.o \ - $(OTTY)spell.o $(OTTY)stairs.o $(OTTY)steal.o $(OTTY)steed.o \ - $(OTTY)strutil.o $(OTTY)symbols.o $(OTTY)sys.o $(OTTY)teleport.o \ - $(OTTY)timeout.o $(OTTY)topten.o $(OTTY)track.o $(OTTY)trap.o \ - $(OTTY)u_init.o $(OTTY)uhitm.o $(OTTY)utf8map.o $(OTTY)vault.o \ - $(OTTY)vision.o $(OTTY)weapon.o $(OTTY)were.o $(OTTY)wield.o \ - $(OTTY)windows.o $(OTTY)wizard.o $(OTTY)wizcmds.o $(OTTY)worm.o \ - $(OTTY)worn.o $(OTTY)write.o $(OTTY)zap.o + $(OTTY)drawing.o $(OTTY)dungeon.o $(OTTY)earlyarg.o $(OTTY)eat.o \ + $(OTTY)end.o $(OTTY)engrave.o $(OTTY)exper.o $(OTTY)explode.o \ + $(OTTY)extralev.o $(OTTY)files.o $(OTTY)fountain.o $(OTTY)getpos.o \ + $(OTTY)glyphs.o $(OTTY)iactions.o $(OTTY)hack.o $(OTTY)insight.o \ + $(OTTY)invent.o $(OTTY)isaac64.o $(OTTY)light.o $(OTTY)lock.o \ + $(OTTY)mail.o $(OTTY)makemon.o $(OTTY)mcastu.o $(OTTY)mhitm.o \ + $(OTTY)mhitu.o $(OTTY)minion.o $(OTTY)mklev.o $(OTTY)mkmap.o \ + $(OTTY)mkmaze.o $(OTTY)mkobj.o $(OTTY)mkroom.o $(OTTY)mon.o \ + $(OTTY)mondata.o $(OTTY)monmove.o $(OTTY)monst.o $(OTTY)mplayer.o \ + $(OTTY)mthrowu.o $(OTTY)muse.o $(OTTY)music.o $(OTTY)o_init.o \ + $(OTTY)objects.o $(OTTY)objnam.o $(OTTY)options.o $(OTTY)pager.o \ + $(OTTY)pickup.o $(OTTY)pline.o $(OTTY)polyself.o $(OTTY)potion.o \ + $(OTTY)pray.o $(OTTY)priest.o $(OTTY)quest.o $(OTTY)questpgr.o \ + $(OTTY)read.o $(OTTY)rect.o $(OTTY)region.o $(OTTY)report.o \ + $(OTTY)restore.o $(OTTY)rip.o $(OTTY)rnd.o $(OTTY)role.o \ + $(OTTY)rumors.o $(OTTY)save.o $(OTTY)selvar.o $(OTTY)sfbase.o \ + $(OTTY)sfstruct.o $(OTTY)shk.o $(OTTY)shknam.o $(OTTY)sit.o \ + $(OTTY)sounds.o $(OTTY)sp_lev.o $(OTTY)spell.o $(OTTY)stairs.o \ + $(OTTY)steal.o $(OTTY)steed.o $(OTTY)strutil.o $(OTTY)symbols.o \ + $(OTTY)sys.o $(OTTY)teleport.o $(OTTY)timeout.o $(OTTY)topten.o \ + $(OTTY)track.o $(OTTY)trap.o $(OTTY)u_init.o $(OTTY)uhitm.o \ + $(OTTY)utf8map.o $(OTTY)vault.o $(OTTY)vision.o $(OTTY)weapon.o \ + $(OTTY)were.o $(OTTY)wield.o $(OTTY)windows.o $(OTTY)wizard.o \ + $(OTTY)wizcmds.o $(OTTY)worm.o $(OTTY)worn.o $(OTTY)write.o \ + $(OTTY)zap.o OBJSTTY = $(MDLIBTTY) $(COREOBJTTY) $(REGEXTTY) $(RANDOMTTY) @@ -900,8 +910,7 @@ REGEXGUI = $(OGUI)cppregex.o LUAOBJGUI = $(OGUI)nhlua.o $(OGUI)nhlsel.o $(OGUI)nhlobj.o VVOBJGUI = $(OGUI)version.o -SOBJGUI = $(OGUI)windmain.o $(OGUI)windsys.o $(OGUI)win10.o \ - $(OGUI)safeproc.o +SOBJGUI = $(OGUI)windmain.o $(OGUI)windsys.o $(OGUI)win10.o GUIOBJ = $(OGUI)mhaskyn.o $(OGUI)mhdlg.o \ $(OGUI)mhfont.o $(OGUI)mhinput.o $(OGUI)mhmain.o $(OGUI)mhmap.o \ @@ -915,30 +924,31 @@ COREOBJGUI = \ $(OGUI)dbridge.o $(OGUI)decl.o $(OGUI)detect.o $(OGUI)dig.o \ $(OGUI)display.o $(OGUI)do.o $(OGUI)do_name.o $(OGUI)do_wear.o \ $(OGUI)dog.o $(OGUI)dogmove.o $(OGUI)dokick.o $(OGUI)dothrow.o \ - $(OGUI)drawing.o $(OGUI)dungeon.o $(OGUI)eat.o $(OGUI)end.o \ - $(OGUI)engrave.o $(OGUI)exper.o $(OGUI)explode.o $(OGUI)extralev.o \ - $(OGUI)files.o $(OGUI)fountain.o $(OGUI)getpos.o $(OGUI)glyphs.o \ - $(OGUI)hack.o $(OGUI)insight.o $(OGUI)invent.o $(OGUI)isaac64.o \ - $(OGUI)light.o $(OGUI)lock.o $(OGUI)mail.o $(OGUI)makemon.o \ - $(OGUI)mcastu.o $(OGUI)mhitm.o $(OGUI)mhitu.o $(OGUI)minion.o \ - $(OGUI)mklev.o $(OGUI)mkmap.o $(OGUI)mkmaze.o $(OGUI)mkobj.o \ - $(OGUI)mkroom.o $(OGUI)mon.o $(OGUI)mondata.o $(OGUI)monmove.o \ - $(OGUI)monst.o $(OGUI)mplayer.o $(OGUI)mthrowu.o $(OGUI)muse.o \ - $(OGUI)music.o $(OGUI)o_init.o $(OGUI)objects.o $(OGUI)objnam.o \ - $(OGUI)options.o $(OGUI)pager.o $(OGUI)pickup.o $(OGUI)pline.o \ - $(OGUI)polyself.o $(OGUI)potion.o $(OGUI)pray.o $(OGUI)priest.o \ - $(OGUI)quest.o $(OGUI)questpgr.o $(OGUI)read.o $(OGUI)rect.o \ - $(OGUI)region.o $(OGUI)report.o $(OGUI)restore.o $(OGUI)rip.o \ - $(OGUI)rnd.o $(OGUI)role.o $(OGUI)rumors.o $(OGUI)save.o \ - $(OGUI)selvar.o $(OGUI)sfbase.o $(OGUI)sfstruct.o $(OGUI)shk.o \ - $(OGUI)shknam.o $(OGUI)sit.o $(OGUI)sounds.o $(OGUI)sp_lev.o \ - $(OGUI)spell.o $(OGUI)stairs.o $(OGUI)steal.o $(OGUI)steed.o \ - $(OGUI)strutil.o $(OGUI)symbols.o $(OGUI)sys.o $(OGUI)teleport.o \ - $(OGUI)timeout.o $(OGUI)topten.o $(OGUI)track.o $(OGUI)trap.o \ - $(OGUI)u_init.o $(OGUI)uhitm.o $(OGUI)utf8map.o $(OGUI)vault.o \ - $(OGUI)vision.o $(OGUI)weapon.o $(OGUI)were.o $(OGUI)wield.o \ - $(OGUI)windows.o $(OGUI)wizard.o $(OGUI)wizcmds.o $(OGUI)worm.o \ - $(OGUI)worn.o $(OGUI)write.o $(OGUI)zap.o + $(OGUI)drawing.o $(OGUI)dungeon.o $(OGUI)earlyarg.o $(OGUI)eat.o \ + $(OGUI)end.o $(OGUI)engrave.o $(OGUI)exper.o $(OGUI)explode.o \ + $(OGUI)extralev.o $(OGUI)files.o $(OGUI)fountain.o $(OGUI)getpos.o \ + $(OGUI)glyphs.o $(OGUI)iactions.o $(OGUI)hack.o $(OGUI)insight.o \ + $(OGUI)invent.o $(OGUI)isaac64.o $(OGUI)light.o $(OGUI)lock.o \ + $(OGUI)mail.o $(OGUI)makemon.o $(OGUI)mcastu.o $(OGUI)mhitm.o \ + $(OGUI)mhitu.o $(OGUI)minion.o $(OGUI)mklev.o $(OGUI)mkmap.o \ + $(OGUI)mkmaze.o $(OGUI)mkobj.o $(OGUI)mkroom.o $(OGUI)mon.o \ + $(OGUI)mondata.o $(OGUI)monmove.o $(OGUI)monst.o $(OGUI)mplayer.o \ + $(OGUI)mthrowu.o $(OGUI)muse.o $(OGUI)music.o $(OGUI)o_init.o \ + $(OGUI)objects.o $(OGUI)objnam.o $(OGUI)options.o $(OGUI)pager.o \ + $(OGUI)pickup.o $(OGUI)pline.o $(OGUI)polyself.o $(OGUI)potion.o \ + $(OGUI)pray.o $(OGUI)priest.o $(OGUI)quest.o $(OGUI)questpgr.o \ + $(OGUI)read.o $(OGUI)rect.o $(OGUI)region.o $(OGUI)report.o \ + $(OGUI)restore.o $(OGUI)rip.o $(OGUI)rnd.o $(OGUI)role.o \ + $(OGUI)rumors.o $(OGUI)save.o $(OGUI)selvar.o $(OGUI)sfbase.o \ + $(OGUI)sfstruct.o $(OGUI)shk.o $(OGUI)shknam.o $(OGUI)sit.o \ + $(OGUI)sounds.o $(OGUI)sp_lev.o $(OGUI)spell.o $(OGUI)stairs.o \ + $(OGUI)steal.o $(OGUI)steed.o $(OGUI)strutil.o $(OGUI)symbols.o \ + $(OGUI)sys.o $(OGUI)teleport.o $(OGUI)timeout.o $(OGUI)topten.o \ + $(OGUI)track.o $(OGUI)trap.o $(OGUI)u_init.o $(OGUI)uhitm.o \ + $(OGUI)utf8map.o $(OGUI)vault.o $(OGUI)vision.o $(OGUI)weapon.o \ + $(OGUI)were.o $(OGUI)wield.o $(OGUI)windows.o $(OGUI)wizard.o \ + $(OGUI)wizcmds.o $(OGUI)worm.o $(OGUI)worn.o $(OGUI)write.o \ + $(OGUI)zap.o OBJSGUI = $(MDLIBGUI) $(COREOBJGUI) $(REGEXGUI) $(RANDOMGUI) @@ -1121,6 +1131,7 @@ HACK_H = $(CONFIG_H) $(INCL)align.h $(INCL)artilist.h \ $(INCL)decl.h $(INCL)defsym.h $(INCL)display.h \ $(INCL)dungeon.h $(INCL)engrave.h $(INCL)flag.h \ $(INCL)hack.h $(INCL)lint.h $(INCL)mextra.h \ + $(INCL)mcastu.h \ $(INCL)mkroom.h $(INCL)monattk.h $(INCL)mondata.h \ $(INCL)monflag.h $(INCL)monst.h $(INCL)monsters.h \ $(INCL)nhlua.h $(INCL)obj.h $(INCL)objclass.h \ @@ -1178,15 +1189,13 @@ rc=Rc.exe # is too old or untested. # # Recently tested versions: -TESTEDVS2019 = 14.29.30157.0 -TESTEDVS2022 = 14.44.35215.0 +TESTEDVS2022 = 14.44.35222.0 +TESTEDVS2026 = 14.50.35728.0 -VS2019CUR = $(TESTEDVS2019:.=) -VS2022CUR = $(TESTEDVS2022:.=) -VS2019UP1 = $(VS2019CUR) + 1 -VS2022UP1 = $(VS2022CUR) + 1 -VS20191ST = 1419300000 -VS20221ST = $(VS2019UP1) +VS20261ST = 1450000000 +VS2026CUR = $(TESTEDVS2026:.=) +VS2026DN1 = $(VS20261ST) - 1 +VS2026UP1 = $(VS2026CUR) + 1 #!MESSAGE $(MAKEFLAGS) #!MESSAGE $(MAKEDIR) @@ -1198,28 +1207,25 @@ MAKEVERSION=$(MAKEVERSION: =) #!MESSAGE $(MAKEVERSION) VSSPECIAL= -VSNEWEST=2022 +VSNEWEST=2026 !IF ($(MAKEVERSION) < 1411000000) VSVER=0000 #untested ancient version -!ELSEIF ($(MAKEVERSION) < $(VS20191ST)) -VSVER=2017 -!ELSEIF ($(MAKEVERSION) > $(VS20191ST)) && ($(MAKEVERSION) < $(VS2019UP1)) -VSVER=2019 -!ELSEIF ($(MAKEVERSION) > $(VS20221ST)) && ($(MAKEVERSION) < $(VS2022UP1)) +!ELSEIF ($(MAKEVERSION) < $(VS20261ST)) VSVER=2022 -!ELSEIF ($(MAKEVERSION) > $(VS2022CUR)) +!ELSEIF ($(MAKEVERSION) > $(VS20261ST)) && ($(MAKEVERSION) < $(VS2026UP1)) +VSVER=2026 +!ELSEIF ($(MAKEVERSION) > $(VS2026CUR)) VSVER=2999 #untested future version !ENDIF -!IF ($(VSVER) >= 2012) !IF ($(VSVER) <= $(VSNEWEST)) -!IF ($(VSVER) == 2017) -!MESSAGE Autodetected Visual Studio $(VSVER) which we stopped testing with in March 2024 +!IF ($(MAKEVERSION) < $(VS20261ST)) +!MESSAGE Autodetected Visual Studio $(VSVER) or earlier !ELSE !MESSAGE Autodetected Visual Studio $(VSVER) $(VSSPECIAL) !ENDIF !ENDIF -!ENDIF + !IF ($(VSVER) == 2999) !MESSAGE The NMAKE version of this Visual Studio $(_NMAKE_VER) is newer than the !MESSAGE most recent at the time this Makefile was crafted (Visual Studio $(VSNEWEST)). @@ -1227,25 +1233,11 @@ VSVER=2999 #untested future version !MESSAGE will still work. !ELSEIF ($(VSVER) == 0000) !MESSAGE The version of Visual Studio appears to be quite old, older -!MESSAGE than VS2010 which is the oldest supported version by this +!MESSAGE than VS2010 which is the oldest version confirmed to work with this !MESSAGE Makefile, so we'll stop now. !ERROR Untested old Visual Studio version with NMAKE $(_NMAKE_VER). !ENDIF -!IF ($(VSVER) == 2010) -# For VS2010 use "setenv /x86" or "setenv /x64" before invoking make process -# DO NOT DELETE THE FOLLOWING LINE -!include -! ENDIF - -!IF ($(VSVER) == 2010) -CL_RECENT= -!ELSE -! IF ($(VSVER) > 2010) -CL_RECENT=-sdl -! ENDIF -!ENDIF - !IF ($(VSVER) >= 2019) && ("$(WANT_ASAN)"=="Y") ASAN=/fsanitize=address !ELSE @@ -1287,6 +1279,9 @@ scall = -Gz !ELSEIF "$(TARGET_CPU)" == "x64" ctmpflags = $(ctmpflags1) -D_AMD64_=1 -DWIN64 -D_WIN64 -DWIN32 -D_WIN32 -W4 scall = +!ELSEIF "$(TARGET_CPU)" == "arm64" +ctmpflags = $(ctmpflags1) -DWIN64 -D_WIN64 -DWIN32 -D_WIN32 -W4 +scall = !ENDIF !IF ($(VSVER) >= 2012) @@ -1344,6 +1339,12 @@ DLLENTRY = MACHINE=/MACHINE:X64 !ENDIF +# declarations for use on arm64 systems +!IF "$(TARGET_CPU)" == "arm64" +DLLENTRY = +MACHINE=/MACHINE:arm64 +!ENDIF + # for Windows applications conlflags = $(lflags) -subsystem:console guilflags = $(lflags) -subsystem:windows @@ -1364,8 +1365,8 @@ INCLUSIONS= /I$(R_INCL) /I$(R_MSWSYS) $(R_LUAINCL) $(R_SOUNDLIBINCL) # Util and console builds #========================================== -CFLAGS = $(ctmpflags) $(INCLUSIONS) $(DLBDEF) -DSAFEPROCS $(SOUNDLIBDEFS) -CPPFLAGS = $(cpptmpflags) $(INCLUSIONS) $(DLBDEF) -DSAFEPROCS $(SOUNDLIBDEFS) +CFLAGS = $(ctmpflags) $(INCLUSIONS) $(DLBDEF) $(SOUNDLIBDEFS) +CPPFLAGS = $(cpptmpflags) $(INCLUSIONS) $(DLBDEF) $(SOUNDLIBDEFS) LFLAGS = $(lflags) $(conlibs) $(MACHINE) #========================================== @@ -1519,7 +1520,7 @@ DLB = #!MESSAGE {$(R_PDCSRC)}.c{$(R_OBJPDC)}.o: #!MESSAGE PDCINCL = $(PDCINCL) #!MESSAGE PDCINCLGUI = $(PDCINCLGUI) -#!MESSAGE PDCINCLCON = $(PDCINCLCON) +#!MESSAGE PDCINCLTXT = $(PDCINCLTXT) #!MESSAGE $(Q)$(CC) /wd4244 /wd4267 /wd4774 $(R_PDCINCL) $(CFLAGS:-w44774= ) $(CURSESDEF2) /wd5262 $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -FoX2 X1 # @@ -1531,15 +1532,15 @@ DLB = $(Q)$(CC) /wd4244 /wd4267 /wd4774 $(PDCINCL) $(CFLAGS:-w44774= ) $(CURSESDEF2) /wd5262 $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< !IF "$(CURSES_CONSOLE)" == "Y" -{$(R_PDCWINCON)}.c{$(R_OBJPDCC)}.o: - $(Q)$(CC) /wd4244 /wd4267 /wd4774 $(PDCINCL) $(PDCINCLCON) $(CFLAGS) $(CURSESDEF2) /wd5262 $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< -#!MESSAGE {$(R_PDCWINCON)}.c{$(R_OBJPDCC)}.o: +{$(R_PDCTXT)}.c{$(R_OBJPDCTXT)}.o: + $(Q)$(CC) /wd4244 /wd4267 /wd4774 $(PDCINCL) $(PDCINCLTXT) $(CFLAGS) $(CURSESDEF2) /wd5262 $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< +#!MESSAGE {$(R_PDCTXT)}.c{$(R_OBJPDCTXT)}.o: !ENDIF !IF "$(CURSES_GRAPHICAL)" == "Y" -{$(R_PDCWINGUI)}.c{$(R_OBJPDCG)}.o: +{$(R_PDCGUI)}.c{$(R_OBJPDCGUI)}.o: $(Q)$(CC) /wd4244 /wd4267 /wd4774 $(PDCINCL) $(PDCINCLGUI) $(CFLAGS) $(CURSESDEF2) /wd5262 $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< -#!MESSAGE {$(R_PDCWINGUI)}.c{$(R_OBJPDCG)}.o: +#!MESSAGE {$(R_PDCGUI)}.c{$(R_OBJPDCGUI)}.o: !ENDIF !ENDIF @@ -1607,8 +1608,8 @@ $(PDCURSES_TOP)curses.h: # git submodule update --remote $(SUBM)$(R_PDCDIST) !ENDIF !ELSE # GIT_AVAILABLE no -CURLLUASRC=http://www.lua.org/ftp/lua-5.4.6.tar.gz -CURLLUADST=lua-5.4.6.tar.gz +CURLLUASRC=http://www.lua.org/ftp/lua-5.4.8.tar.gz +CURLLUADST=lua-5.4.8.tar.gz #CURLPDCSRC=https://github.com/wmcbrine/PDCurses/archive/refs/tags/3.9.zip CURLPDCSRC=https://github.com/Bill-Gray/PDCursesMod/archive/refs/tags/v4.4.0.zip @@ -1680,16 +1681,16 @@ GAMEOBJ=$(GAMEOBJ:^ =^ $(GAMEDIR)NetHack.exe : gamedir.tag $(OTTY)consoletty.o \ $(ALLOBJTTY) $(CURSESWINCONOBJS) \ $(TTYSOUNDOBJS) $(OTTY)date.o $(OTTY)console.res \ - $(LUALIB) $(TTYOBJTTY) $(PDCWINCONLIB) $(PDCWINCONOBJS) $(OTTYHACKLIB) + $(LUALIB) $(TTYOBJTTY) $(PDCTXTLIB) $(PDCTXTOBJS) $(OTTYHACKLIB) @if not exist $(GAMEDIR)*.* mkdir $(R_GAMEDIR) @echo Linking $(@:\=/) $(link) $(LFLAGS) $(conlflags) /STACK:2048 /PDB:$(GAMEDIR)$(@B).PDB /MAP:$(OTTY)$(@B).MAP \ - $(LIBS) $(PDCWINCONLIB) $(LUALIB) $(TTYSOUNDLIBS) $(OTTYHACKLIB) \ + $(LIBS) $(PDCTXTLIB) $(LUALIB) $(TTYSOUNDLIBS) $(OTTYHACKLIB) \ $(conlibs) $(BCRYPT) -out:$@ @<<$(@B).lnk $(GAMEOBJTTY) $(ALLOBJTTY) $(TTYOBJTTY) - $(TTYSOUNDOBJS) $(CURSESWINCONOBJS) $(PDCWINCONOBJS) + $(TTYSOUNDOBJS) $(CURSESWINCONOBJS) $(PDCTXTOBJS) $(OTTY)consoletty.o $(OTTY)date.o $(OTTY)console.res @@ -1704,18 +1705,18 @@ $(GAMEDIR)NetHackW.exe : gamedir.tag $(OGUI)tile.o \ $(ALLOBJGUI) $(GAMEOBJGUI) $(GUIOBJ) $(GUISOUNDOBJS) \ $(CURSESWINGUIOBJS) $(OGUI)date.o \ $(OGUI)NetHackW.res \ - $(LUALIB) $(PDCWINGUILIB) $(PDCWINGUIOBJS) $(OGUIHACKLIB) + $(LUALIB) $(PDCGUILIB) $(PDCGUIOBJS) $(OGUIHACKLIB) @if not exist $(GAMEDIR)*.* mkdir $(R_GAMEDIR) @echo Linking $(@:\=/) $(link) $(LFLAGS) $(guilflags) /STACK:2048 /PDB:$(GAMEDIR)$(@B).PDB \ /MAP:$(OGUI)$(@B).MAP \ - $(LIBS) $(PDCWINGUILIB) $(LUALIB) \ + $(LIBS) $(PDCGUILIB) $(LUALIB) \ $(GUISOUNDLIBS) $(OGUIHACKLIB) \ $(guilibs) $(COMCTRL) $(BCRYPT) -out:$@ @<<$(@B).lnk $(GAMEOBJGUI) $(ALLOBJGUI) $(GUIOBJ) - $(GUISOUNDOBJS) $(CURSESWINGUIOBJS) $(PDCWINGUIOBJS) + $(GUISOUNDOBJS) $(CURSESWINGUIOBJS) $(PDCGUIOBJS) $(OGUI)tile.o $(OGUI)date.o $(OGUI)NetHackW.res @@ -1980,14 +1981,14 @@ opdcdir$(TARGET_CPU).tag: @if not exist $(OBJPDC)*.* mkdir $(R_OBJPDC) @echo directory created >$@ -opdccdir$(TARGET_CPU).tag: - @if not exist $(OBJPDCC)*.* echo creating directory $(OBJPDCC:\=/) - @if not exist $(OBJPDCC)*.* mkdir $(R_OBJPDCC) +opdctxtdir$(TARGET_CPU).tag: + @if not exist $(OBJPDCTXT)*.* echo creating directory $(OBJPDCTXT:\=/) + @if not exist $(OBJPDCTXT)*.* mkdir $(R_OBJPDCTXT) @echo directory created >$@ -opdcgdir$(TARGET_CPU).tag: - @if not exist $(OBJPDCG)*.* echo creating directory $(OBJPDCG:\=/) - @if not exist $(OBJPDCG)*.* mkdir $(R_OBJPDCG) +opdcguidir$(TARGET_CPU).tag: + @if not exist $(OBJPDCGUI)*.* echo creating directory $(OBJPDCGUI:\=/) + @if not exist $(OBJPDCGUI)*.* mkdir $(R_OBJPDCGUI) @echo directory created >$@ libdir.tag: @@ -2027,7 +2028,11 @@ cpu.tag: ! IF "$(TARGET_CPU)"=="x64" @echo Windows x64 64-bit target build ! ELSE +! IF "$(TARGET_CPU)"=="arm64" + @echo Windows arm64 64-bit target build +! ELSE @echo Windows x86 32-bit target build +! ENDIF ! ENDIF @echo cpu >$@ @@ -2041,7 +2046,7 @@ fetch-Lua: fetch-actual-Lua fetch-actual-Lua: @if not exist $(LIBDIR)*.* mkdir $(R_LIBDIR) cd $(LIBDIR) - curl -R -O http://www.lua.org/ftp/lua-$(LUAVER).tar.gz + curl -L -R -O http://www.lua.org/ftp/lua-$(LUAVER).tar.gz tar zxf lua-$(LUAVER).tar.gz if exist lua-$(LUAVER).tar.gz del lua-$(LUAVER).tar.gz if exist lua-$(LUAVER).tar del lua-$(LUAVER).tar @@ -2064,6 +2069,54 @@ fetch-pdcurses: @echo $(PDCDIST) has been fetched into $(LIBDIR)$(PDCDIST) !ENDIF +#https://github.com/universal-ctags/ctags/archive/refs/tags/v6.2.1.zip +CTAGSVER=6.2.1 +CTAGSURL=https://github.com/universal-ctags/ctags/archive/refs/tags/v$(CTAGSVER).zip + +fetch-ctags: $(LIBDIR)ctags.zip + +build-ctags: $(LIBDIR)ctags\ctags-$(CTAGSVER)\ctags.exe + +$(LIBDIR)ctags\ctags-$(CTAGSVER)\ctags.exe: $(LIBDIR)ctags\ctags-$(CTAGSVER)\mk_mvc.mak + @ECHO on + cd $(LIBDIR) + cd ctags\ctags-$(CTAGSVER) + copy win32\config_mvc.h config.h + copy win32\gnulib_h\langinfo.h gnulib + copy win32\gnulib_h\fnmatch.h gnulib + nmake -f mk_mvc.mak + copy /Y ctags.exe ..\ctags.exe + @echo ctags has been compiled into $(LIBDIR)ctags + @ECHO off + cd ..\.. + cd $(R_SRC) + +$(LIBDIR)ctags.zip: + @if not exist $(LIBDIR)*.* mkdir $(R_LIBDIR) + cd $(LIBDIR) + curl -L -R $(CTAGSURL) -o ctags.zip + cd $(R_SRC) + @echo ctags source has been fetched into $@ + +$(LIBDIR)ctags\ctags-$(CTAGSVER)\mk_mvc.mak: $(LIBDIR)ctags.zip + @if not exist $(LIBDIR)*.* mkdir $(R_LIBDIR) + cd $(LIBDIR) + powershell -command "Expand-Archive -Path $(LIBDIR)ctags.zip -DestinationPath $(LIBDIR)ctags" + cd $(R_SRC) + @echo ctags source has been expanded from zip file into $(LIBDIR)\ctags\ctags-$(CTAGSVER) + +clean-ctags: + cd $(LIBDIR) + rd $(LIBDIR)\ctags\ctags-$(CTAGSVER) /S + cd $(R_SRC) + +spotless-ctags: + cd $(LIBDIR) + @if exist ctags\ctags.exe del ctags\ctags.exe + @if exist ctags.zip del ctags.zip + cd $(R_SRC) + + #========================================== # DLB utility and nhdatNNN file creation #========================================== @@ -2273,14 +2326,14 @@ $(SRC)x11tiles: $(U)tile2x11.exe $(WSHR)monsters.txt $(WSHR)objects.txt \ #=============================================================================== !IF "$(ADD_CURSES)" == "Y" !IF "$(CURSES_CONSOLE)" == "Y" -$(PDCWINCONLIB) : $(PDCCOMMONOBJS) $(PDCWINCONOBJS) +$(PDCTXTLIB) : $(PDCCOMMONOBJS) $(PDCTXTOBJS) @echo Building library $@ from $** - @$(librarian) -nologo /out:$@ $(PDCCOMMONOBJS) $(PDCWINCONOBJS) + @$(librarian) -nologo /out:$@ $(PDCCOMMONOBJS) $(PDCTXTOBJS) !ENDIF !IF "$(CURSES_GRAPHICAL)" == "Y" -$(PDCWINGUILIB) : $(PDCCOMMONOBJS) $(PDCWINGUIOBJS) +$(PDCGUILIB) : $(PDCCOMMONOBJS) $(PDCGUIOBJS) @echo Building library $@ from $** - @$(librarian) -nologo /out:$@ $(PDCCOMMONOBJS) $(PDCWINGUIOBJS) + @$(librarian) -nologo /out:$@ $(PDCCOMMONOBJS) $(PDCGUIOBJS) !ENDIF !ENDIF $(OGUI)guitty.o: $(MSWSYS)guitty.c $(WINDHDR) $(HACK_H) $(TILE_H) @@ -2338,14 +2391,12 @@ $(OTTY)windsys.o: $(MSWSYS)windsys.c $(WINDHDR) $(HACK_H) $(OTTY)windsound.o: $(WINDSOUNDDIR)windsound.c $(HACK_H) #$(OTTY)sample.o: $(SOUNDDIR)sample.c $(HACK_H) $(OTTY)windmain.o: $(MSWSYS)windmain.c $(WINDHDR) $(HACK_H) -$(OTTY)safeproc.o: $(WSHR)safeproc.c $(WINDHDR) $(HACK_H) $(OGUI)consoletty.o: $(MSWSYS)consoletty.c $(WINDHDR) $(HACK_H) $(TILE_H) $(OGUI)win10.o: $(MSWSYS)win10.c $(WINDHDR) $(HACK_H) $(OGUI)windsys.o: $(MSWSYS)windsys.c $(WINDHDR) $(HACK_H) $(OGUI)windsound.o: $(WINDSOUNDDIR)windsound.c $(HACK_H) $(OGUI)windmain.o: $(MSWSYS)windmain.c $(WINDHDR) $(HACK_H) -$(OGUI)safeproc.o: $(WSHR)safeproc.c $(WINDHDR) $(HACK_H) #=================================================================== # win/win32 dependencies @@ -2489,6 +2540,20 @@ $(DAT)bogusmon: $(U)makedefs.exe $(DAT)bogusmon.txt #=============================================================================== # sfutil #=============================================================================== +SFCTOOLBIN = $(BinDir)sfctool.exe +!IF "$(SFCTOOL)" == "1" +SFCTOOLPKG=$(SFCTOOLBIN) +SFCTOOLBASENAM=sfctool.exe +SFCTOOLPDBNAM=sfctool.PDB +SFCTOOLPDBBIN=$(BinDir)sfctool.PDB +!ELSE +SFCTOOLPKG= +SFCTOOLBASENAM= +SFCTOOLPDBNAM= +SFCTOOLPDBBIN= +!ENDIF + +!IF "$(SFCTOOLPKG)" != "" SFCTOOLOBJS = $(OUTL)sfctool.o \ $(OUTL)sf-alloc.o $(OUTL)sf-artifact.o $(OUTL)sfdata.o \ $(OUTL)sf-date.o $(OUTL)sf-decl.o $(OUTL)sf-calendar.o \ @@ -2502,7 +2567,6 @@ SFCTOOLOBJS = $(OUTL)sfctool.o \ $(OUTL)sf-sys.o $(OUTL)sf-timeout.o $(OUTL)sf-track.o \ $(OUTL)sf-version.o $(OUTL)sf-worm.o $(OUTL)sf-windsys.o -SFCTOOLBIN = $(BinDir)sfctool.exe SFFLAGS = -DSFCTOOL -DNOPANICTRACE -DNOCRASHREPORT -DNO_CHRONICLE #CTAGSCMD = $(LIB)\ctags\ctags.exe @@ -2584,8 +2648,10 @@ $(OUTL)sf-sys.o: $(SRC)sys.c $(HACK_H) $(OUTL)sf-windsys.o: $(MSWSYS)windsys.c $(HACK_H) $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(MSWSYS)windsys.c -$(UTIL)sftags.exe: $(OUTL)sftags.o $(OUTLHACKLIB) - $(link) $(LFLAGS) /OUT:$@ $(OUTL)sftags.o $(OUTLHACKLIB) +$(UTIL)sftags.exe: $(OUTL)sftags.o $(OUTL)sf-alloc.o $(OUTL)panic.o $(OUTLHACKLIB) + $(link) $(LFLAGS) /OUT:$@ $(OUTL)sftags.o \ + $(OUTL)sf-alloc.o $(OUTL)panic.o \ + $(OUTLHACKLIB) $(OUTL)sftags.o: $(UTIL)sftags.c $(HACK_H) $(Q)$(CC) $(CFLAGS) -Fo$@ -c $(UTIL)sftags.c $(INCL)sfproto.h: $(UTIL)sftags.exe $(UTIL)sf.tags @@ -2598,7 +2664,7 @@ CTAGDEP = $(INCL)align.h $(INCL)artifact.h $(INCL)artilist.h \ $(INCL)attrib.h $(INCL)context.h $(INCL)coord.h \ $(INCL)decl.h $(INCL)dungeon.h $(INCL)engrave.h \ $(INCL)flag.h $(INCL)func_tab.h $(INCL)global.h \ - $(INCL)hack.h $(INCL)mextra.h \ + $(INCL)hack.h $(INCL)mextra.h $(INCL)mcastu.h \ $(INCL)mkroom.h $(INCL)monst.h \ $(INCL)obj.h $(INCL)objclass.h $(INCL)prop.h \ $(INCL)quest.h $(INCL)rect.h $(INCL)region.h \ @@ -2609,7 +2675,7 @@ CTAGDEP = $(INCL)align.h $(INCL)artifact.h $(INCL)artilist.h \ # $(INCL)permonst.h CTAGSOPT = --language-force=c --sort=no -D"Bitfield(x,n)=unsigned x : n" --excmd=pattern # -$(UTIL)sf.tags: $(CTAGDEP) +$(UTIL)sf.tags: $(CTAGSCMD) $(CTAGDEP) $(CTAGSCMD) $(CTAGSOPT) -f $@ $(INCL)align.h $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)artifact.h $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(SRC)artifact.c @@ -2628,6 +2694,7 @@ $(UTIL)sf.tags: $(CTAGDEP) $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)global.h $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)hack.h $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)mextra.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)mcastu.h $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)mkroom.h $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)monst.h $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)defsym.h @@ -2648,6 +2715,7 @@ $(UTIL)sf.tags: $(CTAGDEP) $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)you.h $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)onames.h $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)wintype.h +!ENDIF #=============================================================================== # Integrated sound files @@ -2719,15 +2787,15 @@ $(SndWavDir)sa2_xplevelup.wav: $(SndWavDir)sa2_xplevelup.uu $(U)uudecode.exe #=============================================================================== PKGFILES = nethackrc.template Guidebook.txt license NetHack.exe NetHack.txt \ - NetHackW.exe opthelp nhdat370 record symbols sysconf.template \ - sfctool.exe + NetHackW.exe opthelp nhdat370 record symbols \ + sysconf.template $(SFCTOOLBASENAM) FILESTOZIP = $(BinDir)nethackrc.template $(BinDir)Guidebook.txt $(BinDir)license \ $(BinDir)NetHack.exe $(BinDir)NetHack.txt $(BinDir)NetHackW.exe \ $(BinDir)opthelp $(BinDir)nhdat370 $(BinDir)record \ - $(BinDir)symbols $(BinDir)sysconf.template $(BinDir)sfctool.exe + $(BinDir)symbols $(BinDir)sysconf.template $(SFCTOOLPKG) -DBGSYMS = NetHack.PDB NetHackW.PDB sfctool.PDB -PDBTOZIP = $(BinDir)NetHack.PDB $(BinDir)NetHackW.PDB $(BinDir)sfctool.PDB +DBGSYMS = NetHack.PDB NetHackW.PDB $(SFCTOOLPDBNAM) +PDBTOZIP = $(BinDir)NetHack.PDB $(BinDir)NetHackW.PDB $(SFCTOOLPDBBIN) MAINZIP = $(PkgDir)nethack-$(NHV)-win-$(TARGET_CPU).zip DBGSYMZIP = $(PkgDir)nethack-$(NHV)-win-$(TARGET_CPU)-debugsymbols.zip @@ -2743,12 +2811,12 @@ $(DBGSYMZIP): $(PDBTOZIP) binary: envchk.tag libdir.tag ottydir$(TARGET_CPU).tag \ outldir$(TARGET_CPU).tag oguidir$(TARGET_CPU).tag \ oluadir$(TARGET_CPU).tag opdcdir$(TARGET_CPU).tag \ - opdccdir$(TARGET_CPU).tag opdcgdir$(TARGET_CPU).tag \ + opdctxtdir$(TARGET_CPU).tag opdcguidir$(TARGET_CPU).tag \ $(LUASRC)lua.h $(PDCDEP) \ $(INCL)nhlua.h $(OUTL)utility.tag \ $(DAT)data $(DAT)rumors $(DAT)oracles $(DAT)engrave \ $(DAT)epitaph $(DAT)bogusmon $(GAMEDIR)NetHack.exe \ - $(GAMEDIR)NetHackW.exe $(GAMEDIR)sfctool.exe $(GAMEDIRDLLS) binary.tag + $(GAMEDIR)NetHackW.exe $(SFCTOOLPKG) $(GAMEDIRDLLS) binary.tag @echo NetHack is up to date. #=============================================================================== @@ -2756,54 +2824,61 @@ binary: envchk.tag libdir.tag ottydir$(TARGET_CPU).tag \ #=============================================================================== spotless: clean - if exist $(GAMEDIR)NetHack.exe del $(GAMEDIR)NetHack.exe + if exist $(GAMEDIR)NetHack.exe del $(GAMEDIR)NetHack.exe if exist $(GAMEDIR)NetHackW.exe del $(GAMEDIR)NetHackW.exe - if exist $(GAMEDIR)NetHack.pdb del $(GAMEDIR)NetHack.pdb - if exist $(GAMEDIR)nhdat$(NHV) del $(GAMEDIR)nhdat$(NHV) - if exist $(INCL)date.h del $(INCL)date.h - if exist $(INCL)onames.h del $(INCL)onames.h - if exist $(INCL)pm.h del $(INCL)pm.h + if exist $(GAMEDIR)NetHack.pdb del $(GAMEDIR)NetHack.pdb + if exist $(GAMEDIR)nhdat$(NHV) del $(GAMEDIR)nhdat$(NHV) + if exist $(INCL)date.h del $(INCL)date.h + if exist $(INCL)onames.h del $(INCL)onames.h + if exist $(INCL)pm.h del $(INCL)pm.h if exist $(U)*.lnk del $(U)*.lnk if exist $(U)*.map del $(U)*.map - if exist $(DAT)data del $(DAT)data - if exist $(DAT)rumors del $(DAT)rumors - if exist $(DAT)engrave del $(DAT)engrave - if exist $(DAT)epitaph del $(DAT)epitaph - if exist $(DAT)bogusmon del $(DAT)bogusmon - if exist $(DAT)porthelp del $(DAT)porthelp + if exist $(DAT)data del $(DAT)data + if exist $(DAT)rumors del $(DAT)rumors + if exist $(DAT)engrave del $(DAT)engrave + if exist $(DAT)epitaph del $(DAT)epitaph + if exist $(DAT)bogusmon del $(DAT)bogusmon + if exist $(DAT)porthelp del $(DAT)porthelp if exist nhdat$(NHV). del nhdat$(NHV). if exist outldirx86.tag del outldirx86.tag if exist ottydirx86.tag del ottydirx86.tag if exist oguidirx86.tag del oguidirx86.tag if exist oluadirx86.tag del oluadirx86.tag if exist opdcdirx86.tag del opdcdirx86.tag - if exist opdccdirx86.tag del opdccdirx86.tag - if exist opdcgdirx86.tag del opdcgdirx86.tag + if exist opdctxtdirx86.tag del opdctxtdirx86.tag + if exist opdcguidirx86.tag del opdcguidirx86.tag if exist outldirx64.tag del outldirx64.tag if exist ottydirx64.tag del ottydirx64.tag if exist oguidirx64.tag del oguidirx64.tag if exist oluadirx64.tag del oluadirx64.tag if exist opdcdirx64.tag del opdcdirx64.tag - if exist opdccdirx64.tag del opdccdirx64.tag - if exist opdcgdirx64.tag del opdcgdirx64.tag + if exist opdctxtdirx64.tag del opdctxtdirx64.tag + if exist opdcguidirx64.tag del opdcguidirx64.tag + if exist outldirarm64.tag del outldirarm64.tag + if exist ottydirarm64.tag del ottydirarm64.tag + if exist oguidirarm64.tag del oguidirarm64.tag + if exist oluadirarm64.tag del oluadirarm64.tag + if exist opdcdirarm64.tag del opdcdirarm64.tag + if exist opdctxtdirarm64.tag del opdctxtdirarm64.tag + if exist opdcguidirarm64.tag del opdcguidirarm64.tag if exist libdir.tag del libdir.tag if exist gamedir.tag del gamedir.tag - if exist $(MSWIN)mnsel.bmp del $(MSWIN)mnsel.bmp - if exist $(MSWIN)mnselcnt.bmp del $(MSWIN)mnselcnt.bmp - if exist $(MSWIN)mnunsel.bmp del $(MSWIN)mnunsel.bmp - if exist $(MSWIN)petmark.bmp del $(MSWIN)petmark.bmp - if exist $(MSWIN)pilemark.bmp del $(MSWIN)pilemark.bmp - if exist $(MSWIN)rip.bmp del $(MSWIN)rip.bmp - if exist $(MSWIN)splash.bmp del $(MSWIN)splash.bmp - if exist $(MSWIN)nethack.ico del $(MSWIN)nethack.ico - if exist $(MSWSYS)nethack.ico del $(MSWSYS)nethack.ico + if exist $(MSWIN)mnsel.bmp del $(MSWIN)mnsel.bmp + if exist $(MSWIN)mnselcnt.bmp del $(MSWIN)mnselcnt.bmp + if exist $(MSWIN)mnunsel.bmp del $(MSWIN)mnunsel.bmp + if exist $(MSWIN)petmark.bmp del $(MSWIN)petmark.bmp + if exist $(MSWIN)pilemark.bmp del $(MSWIN)pilemark.bmp + if exist $(MSWIN)rip.bmp del $(MSWIN)rip.bmp + if exist $(MSWIN)splash.bmp del $(MSWIN)splash.bmp + if exist $(MSWIN)nethack.ico del $(MSWIN)nethack.ico + if exist $(MSWSYS)nethack.ico del $(MSWSYS)nethack.ico if exist $(U)recover.exe del $(U)recover.exe if exist $(U)tile2bmp.exe del $(U)tile2bmp.exe if exist $(U)tilemap.exe del $(U)tilemap.exe if exist $(U)uudecode.exe del $(U)uudecode.exe if exist $(U)dlb.exe del $(U)dlb.exe !IF "$(ADD_CURSES)" == "Y" - if exist $(LIBDIR)$(PDCDIST)-wincon-$(TARGET_CPU)-static.lib del $(LIBDIR)$(PDCDIST)-wincon-$(TARGET_CPU)-static.lib + if exist $(LIBDIR)$(PDCDIST)-$(PDCTXTFOLDER)-$(TARGET_CPU)-static.lib del $(LIBDIR)$(PDCDIST)-$(PDCTXTFOLDER)-$(TARGET_CPU)-static.lib if exist $(LIBDIR)$(PDCDIST)-wingui-$(TARGET_CPU)-static.lib del $(LIBDIR)$(PDCDIST)-wingui-$(TARGET_CPU)-static.lib !ENDIF if exist $(LUALIB) del $(LUALIB) @@ -2814,23 +2889,23 @@ spotless: clean if exist $(DAT)guioptions del $(DAT)guioptions if exist $(DAT)data del $(DAT)data if exist tilemappings.lst del tilemappings.lst - if exist $(SndWavDir)*.wav del $(SndWavDir)*.wav + if exist $(SndWavDir)*.wav del $(SndWavDir)*.wav if exist $(MAINZIP) del $(MAINZIP) if exist $(DBGSYMZIP) del $(DBGSYMZIP) - if exist $(OBJTTY)* rmdir $(OBJTTY) /s /Q - if exist $(OBJGUI)* rmdir $(OBJGUI) /s /Q - if exist $(OBJUTIL)* rmdir $(OBJUTIL) /s /Q - if exist $(OBJLUA)* rmdir $(OBJLUA) /s /Q - if exist $(OBJPDC)* rmdir $(OBJPDC) /s /Q - if exist $(OBJPDCC)* rmdir $(OBJPDCC) /s /Q - if exist $(OBJPDCG)* rmdir $(OBJPDCG) /s /Q - if exist $(OBJTTY_B)* rmdir $(OBJTTY_B) /s /Q - if exist $(OBJGUI_B)* rmdir $(OBJGUI_B) /s /Q - if exist $(OBJUTIL_B)* rmdir $(OBJUTIL_B) /s /Q - if exist $(OBJLUA_B)* rmdir $(OBJLUA_B) /s /Q - if exist $(OBJPDC_B)* rmdir $(OBJPDC_B) /s /Q - if exist $(OBJPDCC_B)* rmdir $(OBJPDCC_B) /s /Q - if exist $(OBJPDCG_B)* rmdir $(OBJPDCG_B) /s /Q + if exist $(OBJTTY)* rmdir $(OBJTTY) /s /Q + if exist $(OBJGUI)* rmdir $(OBJGUI) /s /Q + if exist $(OBJUTIL)* rmdir $(OBJUTIL) /s /Q + if exist $(OBJLUA)* rmdir $(OBJLUA) /s /Q + if exist $(OBJPDC)* rmdir $(OBJPDC) /s /Q + if exist $(OBJPDCTXT)* rmdir $(OBJPDCTXT) /s /Q + if exist $(OBJPDCGUI)* rmdir $(OBJPDCGUI) /s /Q + if exist $(OBJTTY_B)* rmdir $(OBJTTY_B) /s /Q + if exist $(OBJGUI_B)* rmdir $(OBJGUI_B) /s /Q + if exist $(OBJUTIL_B)* rmdir $(OBJUTIL_B) /s /Q + if exist $(OBJLUA_B)* rmdir $(OBJLUA_B) /s /Q + if exist $(OBJPDC_B)* rmdir $(OBJPDC_B) /s /Q + if exist $(OBJPDCTXT_B)* rmdir $(OBJPDCTXT_B) /s /Q + if exist $(OBJPDCGUI_B)* rmdir $(OBJPDCGUI_B) /s /Q clean: if exist binary.tag del binary.tag @@ -2842,28 +2917,28 @@ clean: if exist $(OUTL)utility.tag del $(OUTL)utility.tag if exist $(OTTY)sp_lev.tag del $(OTTY)sp_lev.tag if exist $(OGUI)sp_lev.tag del $(OGUI)sp_lev.tag - if exist $(SRC)tile.c del $(SRC)tile.c - if exist $(INCL)nhlua.h del $(INCL)nhlua.h + if exist $(SRC)tile.c del $(SRC)tile.c + if exist $(INCL)nhlua.h del $(INCL)nhlua.h if exist $(U)makedefs.exe del $(U)makedefs.exe if exist $(U)dlb_main.exe del $(U)dlb_main.exe if exist $(U)tile2bmp.exe del $(U)tile2bmp.exe if exist $(U)tilemap.exe del $(U)tilemap.exe - if exist $(SRC)*.lnk del $(SRC)*.lnk - if exist $(DAT)dlb.lst del $(DAT)dlb.lst + if exist $(SRC)*.lnk del $(SRC)*.lnk + if exist $(DAT)dlb.lst del $(DAT)dlb.lst if exist $(OUTL)*.o del $(OUTL)*.o if exist $(OTTY)*.o del $(OTTY)*.o if exist $(OGUI)*.o del $(OGUI)*.o if exist $(OLUA)*.o del $(OLUA)*.o if exist $(OPDC)*.o del $(OPDC)*.o - if exist $(OPDCC)*.o del $(OPDCC)*.o - if exist $(OPDCG)*.o del $(OPDCG)*.o + if exist $(OPDCTXT)*.o del $(OPDCTXT)*.o + if exist $(OPDCGUI)*.o del $(OPDCGUI)*.o if exist $(OUTL)*.PDB del $(OUTL)*.PDB if exist $(OTTY)*.PDB del $(OTTY)*.PDB if exist $(OGUI)*.PDB del $(OGUI)*.PDB if exist $(OLUA)*.PDB del $(OLUA)*.PDB if exist $(OPDC)*.PDB del $(OPDC)*.PDB - if exist $(OPDCC)*.PDB del $(OPDCC)*.PDB - if exist $(OPDCG)*.PDB del $(OPDCG)*.PDB + if exist $(OPDCTXT)*.PDB del $(OPDCTXT)*.PDB + if exist $(OPDCGUI)*.PDB del $(OPDCGUI)*.PDB if exist $(OUTL)*.MAP del $(OUTL)*.MAP if exist $(OTTY)*.MAP del $(OTTY)*.MAP if exist $(OGUI)*.MAP del $(OGUI)*.MAP @@ -2874,16 +2949,16 @@ clean: if exist $(OGUI)*.LIB del $(OGUI)*.LIB if exist $(OLUA)*.LIB del $(OLUA)*.LIB if exist $(OPDC)*.LIB del $(OPDC)*.LIB - if exist $(OPDCC)*.LIB del $(OPDCC)*.LIB - if exist $(OPDCG)*.LIB del $(OPDCG)*.LIB + if exist $(OPDCTXT)*.LIB del $(OPDCTXT)*.LIB + if exist $(OPDCGUI)*.LIB del $(OPDCGUI)*.LIB if exist $(OUTL)*.EXP del $(OUTL)*.EXP if exist $(OTTY)*.EXP del $(OTTY)*.EXP if exist $(OGUI)*.EXP del $(OGUI)*.EXP if exist $(OLUA)*.EXP del $(OLUA)*.EXP if exist $(OPDC)*.EXP del $(OPDC)*.EXP - if exist $(OPDCC)*.EXP del $(OPDCC)*.EXP - if exist $(OPDCG)*.EXP del $(OPDCG)*.EXP - if exist $(SRC)tiles.bmp del $(SRC)tiles.bmp + if exist $(OPDCTXT)*.EXP del $(OPDCTXT)*.EXP + if exist $(OPDCGUI)*.EXP del $(OPDCGUI)*.EXP + if exist $(SRC)tiles.bmp del $(SRC)tiles.bmp if exist $(OTTY)console.res del $(OTTY)console.res if exist $(OTTY)NetHack.res del $(OTTY)NetHack.res if exist $(OGUI)NetHackW.res del $(OGUI)NetHackW.res @@ -3174,6 +3249,7 @@ $(OTTY)drawing.o: drawing.c $(CONFIG_H) $(INCL)defsym.h \ $(INCL)sym.h $(INCL)wintype.h $(OTTY)dungeon.o: dungeon.c $(HACK_H) $(INCL)dgn_file.h \ $(INCL)dlb.h +$(OTTY)earlyarg.o: earlyarg.c $(HACK_H) $(OTTY)eat.o: eat.c $(HACK_H) $(OTTY)end.o: end.c $(HACK_H) $(INCL)dlb.h $(OTTY)engrave.o: engrave.c $(HACK_H) @@ -3187,6 +3263,7 @@ $(OTTY)getpos.o: getpos.c $(HACK_H) $(OTTY)glyphs.o: glyphs.c $(HACK_H) $(OTTY)hack.o: hack.c $(HACK_H) $(OTTY)hacklib.o: hacklib.c $(HACK_H) +$(OTTY)iactions.o: iactions.c $(HACK_H) $(OTTY)insight.o: insight.c $(HACK_H) $(OTTY)invent.o: invent.c $(HACK_H) $(OTTY)isaac64.o: isaac64.c $(CONFIG_H) $(INCL)isaac64.h @@ -3194,7 +3271,7 @@ $(OTTY)light.o: light.c $(HACK_H) $(OTTY)lock.o: lock.c $(HACK_H) $(OTTY)mail.o: mail.c $(HACK_H) $(INCL)mail.h $(OTTY)makemon.o: makemon.c $(HACK_H) -$(OTTY)mcastu.o: mcastu.c $(HACK_H) +$(OTTY)mcastu.o: mcastu.c $(HACK_H) $(INCL)mcastu.h $(OTTY)mdlib.o: mdlib.c $(CONFIG_H) $(INCL)align.h \ $(INCL)artilist.h $(INCL)attrib.h \ $(INCL)context.h $(INCL)defsym.h $(INCL)dlb.h \ @@ -3553,6 +3630,7 @@ $(OGUI)drawing.o: drawing.c $(CONFIG_H) $(INCL)defsym.h \ $(INCL)sym.h $(INCL)wintype.h $(OGUI)dungeon.o: dungeon.c $(HACK_H) $(INCL)dgn_file.h \ $(INCL)dlb.h +$(OGUI)earlyarg.o: earlyarg.c $(HACK_H) $(OGUI)eat.o: eat.c $(HACK_H) $(OGUI)end.o: end.c $(HACK_H) $(INCL)dlb.h $(OGUI)engrave.o: engrave.c $(HACK_H) @@ -3566,6 +3644,7 @@ $(OGUI)getpos.o: getpos.c $(HACK_H) $(OGUI)glyphs.o: glyphs.c $(HACK_H) $(OGUI)hack.o: hack.c $(HACK_H) $(OGUI)hacklib.o: hacklib.c $(HACK_H) +$(OGUI)iactions.o: iactions.c $(HACK_H) $(OGUI)insight.o: insight.c $(HACK_H) $(OGUI)invent.o: invent.c $(HACK_H) $(OGUI)isaac64.o: isaac64.c $(CONFIG_H) $(INCL)isaac64.h diff --git a/sys/windows/build-msys2.txt b/sys/windows/build-msys2.txt index 1258f7be9..06938bfaa 100644 --- a/sys/windows/build-msys2.txt +++ b/sys/windows/build-msys2.txt @@ -30,7 +30,7 @@ Prerequisite Requirements: +----------+ +------+ +----+ +----+ | | | | | | | | | | | | | | | | - lua-5.4.6 pdcursesmod share windows tty win32 lua pdcursesmod + lua-5.4.8 pdcursesmod share windows tty win32 lua pdcursesmod | vs | diff --git a/sys/windows/build-nmake.txt b/sys/windows/build-nmake.txt index 4735b480c..35c339e7e 100644 --- a/sys/windows/build-nmake.txt +++ b/sys/windows/build-nmake.txt @@ -2,8 +2,8 @@ Building NetHack using the Visual Studio nmake from the command line Prerequisite Requirements: - o Visual Studio Community Edition - A copy of Microsoft Visual Studio Community Edition needs to + o Visual Studio Community + A copy of Microsoft Visual Studio Community needs to be installed on your machine. See: https://visualstudio.microsoft.com/vs/community/ o Lua @@ -43,7 +43,7 @@ You can use one of the following build environments: +----------+ +------+ +----+ +----+ | | | | | | | | | | | | | | | | - lua-5.4.6 pdcursesmod share windows tty win32 lua pdcursesmod + lua-5.4.8 pdcursesmod share windows tty win32 lua pdcursesmod | vs | @@ -54,7 +54,7 @@ You can use one of the following build environments: /-----------------------------------------------------------\ | Building From the Command Line Using nmake from one of the | -| Visual Studio Community Editions | +| Visual Studio Community versions | \-----------------------------------------------------------/ Building @@ -62,7 +62,7 @@ Building Two different versions of NetHack will be built for Windows from the command line using the Makefile approach: A tty port utilizing the Win32 Console I/O subsystem Console, - and a curses interface in an executabled called NetHack.exe. + and a curses interface in an executable called NetHack.exe. NetHack A Win32 native port built on the Windows API Graphical NetHack, and graphical curses in an executable called NetHackW.exe. diff --git a/sys/windows/build-vs.txt b/sys/windows/build-vs.txt index a144470df..bd939a81b 100644 --- a/sys/windows/build-vs.txt +++ b/sys/windows/build-vs.txt @@ -2,9 +2,33 @@ Building NetHack using the Visual Studio IDE Prerequisite Requirements: - o Visual Studio Community Edition - A copy of Microsoft Visual Studio Community Edition needs to - be installed on your machine. See: + o Visual Studio Community + A copy of a version of Microsoft Visual Studio Community needs to + be installed on your machine. Visual Studio Community can be either + the version for Windows 11 x64 for use on machines with + Intel/AMD64 processors, or the version for Windows 11 ARM64 for + use on machines with ARM64 processors (Snapdragon). + + You can opt to build for a target x64, or for a target ARM64 + on either the x64 version of Visual Studio, or on the ARM64 + version. Versions of Visual Studio, since Visual Studio 26, + are able to compile for the native host platform and + cross-compile to the other target platform. + + If you want to selectively cross-compile for both x64 and ARM64 + target types, ensure that the necessary Individual Components for + both types of target builds are selected (checked-off) in the + Visual Studio Installer Individual Components dialog: + + Under Compilers, build tools, and runtimes: + o MSVC Build Tools for ARM64/ARM64EC (Latest) + o MSVC Build Tools for x64/x86 (Latest) + + You can use the Visual Studio Installer "Modify" button + to add missing Individual Components after installation, + if needed. + + See: https://visualstudio.microsoft.com/vs/community/ o Lua NetHack 3.7 for Windows requires 3rd party Lua source that is not part @@ -44,7 +68,7 @@ versions: +----------+ +------+ +----+ +----+ | | | | | | | | | | | | | | | | - lua-5.4.6 pdcursesmod share windows tty win32 lua pdcursesmod + lua-5.4.8 pdcursesmod share windows tty win32 lua pdcursesmod | vs | @@ -54,10 +78,10 @@ versions: /-----------------------------------------------------------\ -| Building And Running Using Visual Studio 2019, 2022 | +| Building And Running Using Visual Studio Community | \-----------------------------------------------------------/ -When using Visual Studio Community Edition, load the provided solution +When using a version of Visual Studio Community, load the provided solution file within the IDE, build the solution. The Visual Studio NetHack solution file can be found here: @@ -66,7 +90,15 @@ The Visual Studio NetHack solution file can be found here: The steps are: 1. Launch the IDE. 2. Open the appropriate solution file in sys\windows\vs\NetHack.sln. - 3. Select the build configuration you wish to use (Release, Debug, etc.). + 3. Select the build platform and configuration that you wish + to target (x64 or ARM64, and Release or Debug). + (Note: If you want to target an ARM64 build from an x64 machine, + or target an x64 build from an ARM64 machine, you need to + first complete the build for the native machine type target. + That's necessary to ensure that host-native versions of + uudecode.exe, makedefs.exe, tilemap.exe, tile2bmp.exe and + dlb.exe tools are available for execution during the build + steps that rely on those tools.) 4. From the build menu, select build solution. 5. Type F5 to start debugging. diff --git a/sys/windows/consoletty.c b/sys/windows/consoletty.c index 7754b3d1f..988e7663a 100644 --- a/sys/windows/consoletty.c +++ b/sys/windows/consoletty.c @@ -98,22 +98,22 @@ cell_t undefined_cell = { CONSOLE_UNDEFINED_CHARACTER, CONSOLE_UNDEFINED_ATTRIBUTE }; #else /* VIRTUAL_TERMINAL_SEQUENCES */ cell_t clear_cell = { - { CONSOLE_CLEAR_CHARACTER, 0, 0, 0, 0, 0, 0 }, + { CONSOLE_CLEAR_CHARACTER, 0, 0, 0, 0, 0, 0 }, CONSOLE_CLEAR_CHARACTER, /* wcharacter */ - 0, /* attr */ - 0L, /* color24 */ - 0, /* color256idx */ - "\x1b[0m", /* bkcolorseq */ - 0 /* colorseq */ + 0, /* attr */ + 0L, /* color24 */ + 0, /* color256idx */ + "\x1b[0m", /* bkcolorseq */ + 0 /* colorseq */ }; cell_t undefined_cell = { - { CONSOLE_UNDEFINED_CHARACTER, 0, 0, 0, 0, 0, 0 }, + { CONSOLE_UNDEFINED_CHARACTER, 0, 0, 0, 0, 0, 0 }, CONSOLE_UNDEFINED_CHARACTER, /* wcharacter */ - 0, /* attr */ - 0L, /* color24 */ - 0, /* color256idx */ - (const char *) 0, /* bkcolorseq */ - (const char *) 0 /* colorseq */ + 0, /* attr */ + 0L, /* color24 */ + 0, /* color256idx */ + (const char *) 0, /* bkcolorseq */ + (const char *) 0 /* colorseq */ }; #if 0 static const uint8 empty_utf8str[MAX_UTF8_SEQUENCE] = { 0 }; @@ -156,7 +156,6 @@ static boolean check_font_widths(void); #endif static void set_known_good_console_font(void); static void restore_original_console_font(void); -extern void safe_routines(void); void tty_ibmgraphics_fixup(void); #ifdef VIRTUAL_TERMINAL_SEQUENCES extern void (*ibmgraphics_mode_callback)(void); /* symbols.c */ @@ -763,7 +762,7 @@ emit_stop_inverse(void) #define tcfmtstr256 "\x1b[38;5;%ldm" #else #define tcfmtstr24bit "\x1b[38:2:%ld:%ld:%ldm" -#define tcfmtstr256 "\x1b[38:5:%dm" +#define tcfmtstr256 "\x1b[38:5:%ldm" #endif void @@ -771,7 +770,8 @@ emit_start_256color(int u256coloridx) { DWORD unused; static char tcolorbuf[QBUFSZ]; - Snprintf(tcolorbuf, sizeof tcolorbuf, tcfmtstr256, u256coloridx); + Snprintf(tcolorbuf, sizeof tcolorbuf, tcfmtstr256, + (long) u256coloridx); WriteConsoleA(console.hConOut, (LPCSTR) tcolorbuf, (int) strlen(tcolorbuf), &unused, NULL); } @@ -783,9 +783,9 @@ emit_start_24bitcolor(long color24bit) static char tcolorbuf[QBUFSZ]; uint32 mcolor = COLORVAL(color24bit); Snprintf(tcolorbuf, sizeof tcolorbuf, tcfmtstr24bit, - ((mcolor >> 16) & 0xFF), /* red */ - ((mcolor >> 8) & 0xFF), /* green */ - ((mcolor >> 0) & 0xFF)); /* blue */ + (long) ((mcolor >> 16) & 0xFF), /* red */ + (long) ((mcolor >> 8) & 0xFF), /* green */ + (long) ((mcolor >> 0) & 0xFF)); /* blue */ WriteConsoleA(console.hConOut, (LPCSTR) tcolorbuf, (int) strlen(tcolorbuf), &unused, NULL); } @@ -870,7 +870,8 @@ back_buffer_flip(void) do_anything |= do_wide_content; } else { #endif - if (strcmp((const char *) back->utf8str, + if (back->utf8str && front->utf8str + && strcmp((const char *) back->utf8str, (const char *) front->utf8str)) do_anything |= do_utf8_content; #ifdef UTF8_FROM_CORE @@ -1145,7 +1146,7 @@ CtrlHandler(DWORD ctrltype) case CTRL_BREAK_EVENT: term_clear_screen(); FALLTHROUGH; - /* FALLTHRU */ + /* FALLTHRU */ case CTRL_CLOSE_EVENT: case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: @@ -1184,17 +1185,18 @@ consoletty_open(int mode UNUSED) really_move_cursor(); nhUse(debugvar); } +extern void set_emergency_io(void); void consoletty_exit(void) { - /* go back to using the safe routines */ - safe_routines(); free_custom_colors(); free((genericptr_t) console.front_buffer); free((genericptr_t) console.back_buffer); + console.front_buffer = console.back_buffer = 0; free((genericptr_t) console.localestr); free((genericptr_t) console.orig_localestr); + set_emergency_io(); } int @@ -1307,8 +1309,8 @@ really_move_cursor(void) oldtitle[39] = '\0'; } Snprintf(newtitle, sizeof newtitle, - "%-55s tty=(%02d,%02d) consoletty=(%02d,%02d)", - oldtitle, + "%-55s tty=(%02d,%02d) consoletty=(%02d,%02d)", + oldtitle, ttyDisplay->curx, ttyDisplay->cury, console.cursor.X, console.cursor.Y); (void) SetConsoleTitle(newtitle); @@ -1493,7 +1495,7 @@ console_g_putch(int in_ch) #else /* VIRTUAL_TERMINAL_SEQUENCES */ ccount = 0; WCHAR wch[2]; - boolean usemap = (ch >= 0 && ch < SIZE(console.cpMap)); + boolean usemap = (ch >= 0 && ((int) ch < SIZE(console.cpMap))); #endif /* VIRTUAL_TERMINAL_SEQUENCES */ set_console_cursor(ttyDisplay->curx, ttyDisplay->cury); @@ -2543,11 +2545,18 @@ void early_raw_print(const char *s) * */ + +DISABLE_WARNING_CONDEXPR_IS_CONSTANT + void nethack_enter_consoletty(void) { + int width; #ifdef VIRTUAL_TERMINAL_SEQUENCES char buf[BUFSZ], *bp, *localestr; BOOL apisuccess; +// DWORD unused; +// int i = 0; + #endif /* VIRTUAL_TERMINAL_SEQUENCES */ #if 0 /* set up state needed by early_raw_print() */ @@ -2561,13 +2570,41 @@ void nethack_enter_consoletty(void) GetWindowLong(console.hWnd, GWL_STYLE) & ~WS_MAXIMIZEBOX & ~WS_SIZEBOX); #endif + + CONSOLE_SCREEN_BUFFER_INFO csbi; + if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) { + /* srWindow identifies the visible area; dwSize identifies the buffer + */ + width = csbi.srWindow.Right - csbi.srWindow.Left + 1; +#ifdef DEBUG + if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) + fprintf(stdout, "width = %d\n", width); +#endif + } + console.hConOut = GetStdHandle(STD_OUTPUT_HANDLE); nhassert(console.hConOut != NULL); // NOTE: this assert will not print GetConsoleScreenBufferInfo(console.hConOut, &console.orig_csbi); + //COORD screencheck = GetLargestConsoleWindowSize(console.hConOut); + + GetConsoleMode(console.hConOut, &console.orig_out_cmode); + console.out_cmode = console.orig_out_cmode; + console.out_cmode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + SetConsoleMode(console.hConOut, console.out_cmode); +#if 0 + /* tests */ + WriteConsoleA(console.hConOut, "\033[8;;133t",9, &unused, NULL); + for (i = 0; i < 13; ++i) { + WriteConsoleA(console.hConOut, "0123456789", 10, &unused, NULL); + } + WriteConsoleA(console.hConOut, "\033[3;133ftest", 12, &unused, NULL); + GetConsoleScreenBufferInfo(console.hConOut, &console.orig_csbi); /* Testing of widths != COLNO has not turned up any problems. Need * to do a bit more testing and then we are likely to enable having * console width match window width. */ +#endif + #if 0 console.width = console.orig_csbi.srWindow.Right - console.orig_csbi.srWindow.Left + 1; @@ -2589,19 +2626,19 @@ void nethack_enter_consoletty(void) /* clear the entire console buffer */ - int size = console.orig_csbi.dwSize.X * console.orig_csbi.dwSize.Y; - DWORD unused; - set_console_cursor(0, 0); - FillConsoleOutputAttribute( - console.hConOut, CONSOLE_CLEAR_ATTRIBUTE, - size, console.cursor, &unused); + //int size = console.orig_csbi.dwSize.X * console.orig_csbi.dwSize.Y; + //DWORD unused; + //set_console_cursor(0, 0); + // FillConsoleOutputAttribute( + // console.hConOut, CONSOLE_CLEAR_ATTRIBUTE, + // size, console.cursor, &unused); - FillConsoleOutputCharacter( - console.hConOut, CONSOLE_CLEAR_CHARACTER, - size, console.cursor, &unused); + // FillConsoleOutputCharacter( + // console.hConOut, CONSOLE_CLEAR_CHARACTER, + // size, console.cursor, &unused); - set_console_cursor(1, 0); - SetConsoleCursorPosition(console.hConOut, console.cursor); + //set_console_cursor(1, 0); + //SetConsoleCursorPosition(console.hConOut, console.cursor); /* At this point early_raw_print will work */ @@ -2744,6 +2781,9 @@ void nethack_enter_consoletty(void) console.is_ready = TRUE; nhUse(apisuccess); } + +RESTORE_WARNING_CONDEXPR_IS_CONSTANT + #endif /* TTY_GRAPHICS */ /* this is used as a printf() replacement when the window @@ -2756,7 +2796,7 @@ VA_DECL(const char *, fmt) VA_START(fmt); VA_INIT(fmt, const char *); (void) vsnprintf(buf, sizeof buf, fmt, VA_ARGS); - if (redirect_stdout) + if (redirect_stdout || program_state.early_options) fprintf(stdout, "%s", buf); else { #ifdef TTY_GRAPHICS diff --git a/sys/windows/fetch.cmd b/sys/windows/fetch.cmd index c484f9b13..a508ad8dc 100644 --- a/sys/windows/fetch.cmd +++ b/sys/windows/fetch.cmd @@ -2,7 +2,7 @@ if not exist lib\* mkdir lib if [%1] == [lua] ( - set LUA_VERSION=5.4.6 + set LUA_VERSION=5.4.8 set LUASRC=../lib/lua set CURLLUASRC=http://www.lua.org/ftp/lua-%LUA_VERSION%.tar.gz set CURLLUADST=lua-%LUA_VERSION%.tar.gz diff --git a/sys/windows/fetch.sh b/sys/windows/fetch.sh index 4d9b36202..e04dd31d3 100644 --- a/sys/windows/fetch.sh +++ b/sys/windows/fetch.sh @@ -6,12 +6,12 @@ fi if [ $1 == "lua" ]; then if [ -z "$LUA_VERSION" ]; then - export LUA_VERSION=5.4.6 + export LUA_VERSION=5.4.8 export LUASRC=../lib/lua fi - export CURLLUASRC=http://www.lua.org/ftp/lua-5.4.6.tar.gz - export CURLLUADST=lua-5.4.6.tar.gz + export CURLLUASRC=http://www.lua.org/ftp/lua-5.4.8.tar.gz + export CURLLUADST=lua-5.4.8.tar.gz if [ ! -f lib/lua.h ] ;then cd lib diff --git a/sys/windows/nethackrc.template b/sys/windows/nethackrc.template index 60db0ba77..6529b1505 100644 --- a/sys/windows/nethackrc.template +++ b/sys/windows/nethackrc.template @@ -57,6 +57,12 @@ OPTIONS=!autopickup # Set the default boulder symbol to '0' OPTIONS=boulder:0 +# Disable the prompt for a tutorial +#OPTIONS=!tutorial + +# Disable game tips +#OPTIONS=!tips + # Highlight menu lines with different colors. You need to define the colors # with MENUCOLOR lines. # Toggle menucolor use on or off diff --git a/sys/windows/vs/.gitattributes b/sys/windows/vs/.gitattributes index 852f61477..508769ffa 100644 --- a/sys/windows/vs/.gitattributes +++ b/sys/windows/vs/.gitattributes @@ -1,2 +1,2 @@ -* NH_filestag=(file%s_for_Visual_Studio_2019_or_2022_Community_Edition_builds) +* NH_filestag=(file%s_for_Visual_Studio_Community_builds) ..files !NH_filegenerated diff --git a/sys/windows/vs/FetchPrereq/fetchprereq.nmake b/sys/windows/vs/FetchPrereq/fetchprereq.nmake index 07c62dbb6..2d4f69913 100644 --- a/sys/windows/vs/FetchPrereq/fetchprereq.nmake +++ b/sys/windows/vs/FetchPrereq/fetchprereq.nmake @@ -9,7 +9,7 @@ NHV=$(NETHACK_VERSION:.=) NHV=$(NHV:"=) # The version of Lua we want -LUA_VERSION=5.4.6 +LUA_VERSION=5.4.8 CURLLUASRC=https://www.lua.org/ftp/lua-$(LUA_VERSION).tar.gz CURLLUADST=lua-$(LUA_VERSION).tar.gz @@ -33,7 +33,7 @@ SUBMDIR=$(ROOTDIR)\$(SUBMSDIR) default: fetchall -fetchall: libdir fetch-Lua fetch-pdcurses ..\..\..\..\include\nhlua.h +fetchall: libdir fetch-Lua fetch-pdcurses fetch-lua: fetch-actual-Lua @@ -56,14 +56,6 @@ fetch-pdcurses: cd ..\sys\windows\vs\fetchprereq @echo $(PDCDIST) has been fetched into $(LIBDIR)\$(PDCDIST) -..\..\..\..\include\nhlua.h: - @echo /* nhlua.h - generated by Makefile from fetchprereq.nmake */ > $@ - @echo #include "lua.h" >> $@ - @echo ATTRNORETURN LUA_API int (lua_error) (lua_State *L) NORETURN; >> $@ - @echo #include "lualib.h" >> $@ - @echo #include "lauxlib.h" >> $@ - @echo /*nhlua.h*/ >> $@ - libdir: @if not exist $(LIBDIR)\*.* echo creating directory $(LIB:\=/) @if not exist $(LIBDIR)\*.* mkdir $(LIBDIR) diff --git a/sys/windows/vs/FetchPrereq/fetchprereq.vcxproj b/sys/windows/vs/FetchPrereq/fetchprereq.vcxproj index 650e8ed87..1aab0cbbc 100644 --- a/sys/windows/vs/FetchPrereq/fetchprereq.vcxproj +++ b/sys/windows/vs/FetchPrereq/fetchprereq.vcxproj @@ -1,10 +1,18 @@ + + Debug + ARM64 + Debug Win32 + + Release + ARM64 + Release Win32 @@ -39,10 +47,18 @@ Makefile true + + Makefile + true + Makefile false + + Makefile + false + diff --git a/sys/windows/vs/NetHack.sln b/sys/windows/vs/NetHack.sln index 3bb69f828..2c70f0491 100644 --- a/sys/windows/vs/NetHack.sln +++ b/sys/windows/vs/NetHack.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.31903.59 +# Visual Studio Version 18 +VisualStudioVersion = 18.0.11205.157 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NetHackW", "NetHackW\NetHackW.vcxproj", "{CEC5D360-8804-454F-8591-002184C23499}" ProjectSection(ProjectDependencies) = postProject @@ -12,6 +12,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NetHackW", "NetHackW\NetHac {63F9B82B-F589-4082-ABE5-D4F0682050AB} = {63F9B82B-F589-4082-ABE5-D4F0682050AB} {642BC75D-ABAF-403E-8224-7C725FD4CB42} = {642BC75D-ABAF-403E-8224-7C725FD4CB42} {93F10526-209E-41D7-BBEA-775787876895} = {93F10526-209E-41D7-BBEA-775787876895} + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} = {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} = {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} EndProjectSection EndProject @@ -26,7 +27,7 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "makedefs", "makedefs\makedefs.vcxproj", "{BA3DD34C-04B7-40D0-B373-9329AA9E8945}" ProjectSection(ProjectDependencies) = postProject {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} = {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} - {503AE687-C33A-45ED-93AA-83967E176D67} = {503AE687-C33A-45ED-93AA-83967E176D67} + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} = {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "recover", "recover\recover.vcxproj", "{2F35F228-6733-4FE5-9B46-B3AA10D4BC2E}" @@ -41,26 +42,28 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tile2bmp", "tile2bmp\tile2b ProjectSection(ProjectDependencies) = postProject {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} = {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} {503AE687-C33A-45ED-93AA-83967E176D67} = {503AE687-C33A-45ED-93AA-83967E176D67} + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} = {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tilemap", "tilemap\tilemap.vcxproj", "{93F10526-209E-41D7-BBEA-775787876895}" ProjectSection(ProjectDependencies) = postProject {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} = {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} {503AE687-C33A-45ED-93AA-83967E176D67} = {503AE687-C33A-45ED-93AA-83967E176D67} + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} = {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "uudecode", "uudecode\uudecode.vcxproj", "{63F9B82B-F589-4082-ABE5-D4F0682050AB}" ProjectSection(ProjectDependencies) = postProject - {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} = {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} {503AE687-C33A-45ED-93AA-83967E176D67} = {503AE687-C33A-45ED-93AA-83967E176D67} + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} = {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NetHack", "NetHack\NetHack.vcxproj", "{609BC774-C6F8-4B2B-AA7D-5B3D0EA95751}" ProjectSection(ProjectDependencies) = postProject {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC} = {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC} {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} = {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} - {503AE687-C33A-45ED-93AA-83967E176D67} = {503AE687-C33A-45ED-93AA-83967E176D67} {63F9B82B-F589-4082-ABE5-D4F0682050AB} = {63F9B82B-F589-4082-ABE5-D4F0682050AB} + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} = {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} = {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} {BAA70D0F-3EC7-4D10-91F0-974F1F49308B} = {BAA70D0F-3EC7-4D10-91F0-974F1F49308B} EndProjectSection @@ -83,6 +86,8 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hacklib", "hacklib\hacklib.vcxproj", "{096FD6BB-256A-4E68-9B09-2ACA7C606FF3}" ProjectSection(ProjectDependencies) = postProject {503AE687-C33A-45ED-93AA-83967E176D67} = {503AE687-C33A-45ED-93AA-83967E176D67} + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} = {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} + {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} = {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fetchprereq", "FetchPrereq\fetchprereq.vcxproj", "{503AE687-C33A-45ED-93AA-83967E176D67}" @@ -98,6 +103,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "package\package. {63F9B82B-F589-4082-ABE5-D4F0682050AB} = {63F9B82B-F589-4082-ABE5-D4F0682050AB} {642BC75D-ABAF-403E-8224-7C725FD4CB42} = {642BC75D-ABAF-403E-8224-7C725FD4CB42} {93F10526-209E-41D7-BBEA-775787876895} = {93F10526-209E-41D7-BBEA-775787876895} + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} = {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} + {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} = {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} {BA3DD34C-04B7-40D0-B373-9329AA9E8945} = {BA3DD34C-04B7-40D0-B373-9329AA9E8945} {BAA70D0F-3EC7-4D10-91F0-974F1F49308B} = {BAA70D0F-3EC7-4D10-91F0-974F1F49308B} {CEC5D360-8804-454F-8591-002184C23499} = {CEC5D360-8804-454F-8591-002184C23499} @@ -108,126 +115,202 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lualib", "lualib\lualib.vcx {503AE687-C33A-45ED-93AA-83967E176D67} = {503AE687-C33A-45ED-93AA-83967E176D67} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nhlua_h", "nhlua_h\nhlua_h.vcxproj", "{A3B1A65E-4B65-4B67-ADF1-0E38567013A5}" + ProjectSection(ProjectDependencies) = postProject + {503AE687-C33A-45ED-93AA-83967E176D67} = {503AE687-C33A-45ED-93AA-83967E176D67} + {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} = {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 + Release|ARM64 = Release|ARM64 Release|Win32 = Release|Win32 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CEC5D360-8804-454F-8591-002184C23499}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {CEC5D360-8804-454F-8591-002184C23499}.Debug|ARM64.Build.0 = Debug|ARM64 {CEC5D360-8804-454F-8591-002184C23499}.Debug|Win32.ActiveCfg = Debug|Win32 {CEC5D360-8804-454F-8591-002184C23499}.Debug|Win32.Build.0 = Debug|Win32 {CEC5D360-8804-454F-8591-002184C23499}.Debug|x64.ActiveCfg = Debug|x64 {CEC5D360-8804-454F-8591-002184C23499}.Debug|x64.Build.0 = Debug|x64 + {CEC5D360-8804-454F-8591-002184C23499}.Release|ARM64.ActiveCfg = Release|ARM64 + {CEC5D360-8804-454F-8591-002184C23499}.Release|ARM64.Build.0 = Release|ARM64 {CEC5D360-8804-454F-8591-002184C23499}.Release|Win32.ActiveCfg = Release|Win32 {CEC5D360-8804-454F-8591-002184C23499}.Release|Win32.Build.0 = Release|Win32 {CEC5D360-8804-454F-8591-002184C23499}.Release|x64.ActiveCfg = Release|x64 {CEC5D360-8804-454F-8591-002184C23499}.Release|x64.Build.0 = Release|x64 + {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC}.Debug|ARM64.Build.0 = Debug|ARM64 {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC}.Debug|Win32.ActiveCfg = Debug|Win32 {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC}.Debug|Win32.Build.0 = Debug|Win32 {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC}.Debug|x64.ActiveCfg = Debug|x64 {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC}.Debug|x64.Build.0 = Debug|x64 + {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC}.Release|ARM64.ActiveCfg = Release|ARM64 + {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC}.Release|ARM64.Build.0 = Release|ARM64 {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC}.Release|Win32.ActiveCfg = Release|Win32 {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC}.Release|Win32.Build.0 = Release|Win32 {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC}.Release|x64.ActiveCfg = Release|x64 {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC}.Release|x64.Build.0 = Release|x64 + {BA3DD34C-04B7-40D0-B373-9329AA9E8945}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {BA3DD34C-04B7-40D0-B373-9329AA9E8945}.Debug|ARM64.Build.0 = Debug|ARM64 {BA3DD34C-04B7-40D0-B373-9329AA9E8945}.Debug|Win32.ActiveCfg = Debug|Win32 {BA3DD34C-04B7-40D0-B373-9329AA9E8945}.Debug|Win32.Build.0 = Debug|Win32 {BA3DD34C-04B7-40D0-B373-9329AA9E8945}.Debug|x64.ActiveCfg = Debug|x64 {BA3DD34C-04B7-40D0-B373-9329AA9E8945}.Debug|x64.Build.0 = Debug|x64 + {BA3DD34C-04B7-40D0-B373-9329AA9E8945}.Release|ARM64.ActiveCfg = Release|ARM64 + {BA3DD34C-04B7-40D0-B373-9329AA9E8945}.Release|ARM64.Build.0 = Release|ARM64 {BA3DD34C-04B7-40D0-B373-9329AA9E8945}.Release|Win32.ActiveCfg = Release|Win32 {BA3DD34C-04B7-40D0-B373-9329AA9E8945}.Release|Win32.Build.0 = Release|Win32 {BA3DD34C-04B7-40D0-B373-9329AA9E8945}.Release|x64.ActiveCfg = Release|x64 {BA3DD34C-04B7-40D0-B373-9329AA9E8945}.Release|x64.Build.0 = Release|x64 + {2F35F228-6733-4FE5-9B46-B3AA10D4BC2E}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {2F35F228-6733-4FE5-9B46-B3AA10D4BC2E}.Debug|ARM64.Build.0 = Debug|ARM64 {2F35F228-6733-4FE5-9B46-B3AA10D4BC2E}.Debug|Win32.ActiveCfg = Debug|Win32 {2F35F228-6733-4FE5-9B46-B3AA10D4BC2E}.Debug|Win32.Build.0 = Debug|Win32 {2F35F228-6733-4FE5-9B46-B3AA10D4BC2E}.Debug|x64.ActiveCfg = Debug|x64 {2F35F228-6733-4FE5-9B46-B3AA10D4BC2E}.Debug|x64.Build.0 = Debug|x64 + {2F35F228-6733-4FE5-9B46-B3AA10D4BC2E}.Release|ARM64.ActiveCfg = Release|ARM64 + {2F35F228-6733-4FE5-9B46-B3AA10D4BC2E}.Release|ARM64.Build.0 = Release|ARM64 {2F35F228-6733-4FE5-9B46-B3AA10D4BC2E}.Release|Win32.ActiveCfg = Release|Win32 {2F35F228-6733-4FE5-9B46-B3AA10D4BC2E}.Release|Win32.Build.0 = Release|Win32 {2F35F228-6733-4FE5-9B46-B3AA10D4BC2E}.Release|x64.ActiveCfg = Release|x64 {2F35F228-6733-4FE5-9B46-B3AA10D4BC2E}.Release|x64.Build.0 = Release|x64 + {642BC75D-ABAF-403E-8224-7C725FD4CB42}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {642BC75D-ABAF-403E-8224-7C725FD4CB42}.Debug|ARM64.Build.0 = Debug|ARM64 {642BC75D-ABAF-403E-8224-7C725FD4CB42}.Debug|Win32.ActiveCfg = Debug|Win32 {642BC75D-ABAF-403E-8224-7C725FD4CB42}.Debug|Win32.Build.0 = Debug|Win32 {642BC75D-ABAF-403E-8224-7C725FD4CB42}.Debug|x64.ActiveCfg = Debug|x64 {642BC75D-ABAF-403E-8224-7C725FD4CB42}.Debug|x64.Build.0 = Debug|x64 + {642BC75D-ABAF-403E-8224-7C725FD4CB42}.Release|ARM64.ActiveCfg = Release|ARM64 + {642BC75D-ABAF-403E-8224-7C725FD4CB42}.Release|ARM64.Build.0 = Release|ARM64 {642BC75D-ABAF-403E-8224-7C725FD4CB42}.Release|Win32.ActiveCfg = Release|Win32 {642BC75D-ABAF-403E-8224-7C725FD4CB42}.Release|Win32.Build.0 = Release|Win32 {642BC75D-ABAF-403E-8224-7C725FD4CB42}.Release|x64.ActiveCfg = Release|x64 {642BC75D-ABAF-403E-8224-7C725FD4CB42}.Release|x64.Build.0 = Release|x64 + {93F10526-209E-41D7-BBEA-775787876895}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {93F10526-209E-41D7-BBEA-775787876895}.Debug|ARM64.Build.0 = Debug|ARM64 {93F10526-209E-41D7-BBEA-775787876895}.Debug|Win32.ActiveCfg = Debug|Win32 {93F10526-209E-41D7-BBEA-775787876895}.Debug|Win32.Build.0 = Debug|Win32 {93F10526-209E-41D7-BBEA-775787876895}.Debug|x64.ActiveCfg = Debug|x64 {93F10526-209E-41D7-BBEA-775787876895}.Debug|x64.Build.0 = Debug|x64 + {93F10526-209E-41D7-BBEA-775787876895}.Release|ARM64.ActiveCfg = Release|ARM64 + {93F10526-209E-41D7-BBEA-775787876895}.Release|ARM64.Build.0 = Release|ARM64 {93F10526-209E-41D7-BBEA-775787876895}.Release|Win32.ActiveCfg = Release|Win32 {93F10526-209E-41D7-BBEA-775787876895}.Release|Win32.Build.0 = Release|Win32 {93F10526-209E-41D7-BBEA-775787876895}.Release|x64.ActiveCfg = Release|x64 {93F10526-209E-41D7-BBEA-775787876895}.Release|x64.Build.0 = Release|x64 + {63F9B82B-F589-4082-ABE5-D4F0682050AB}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {63F9B82B-F589-4082-ABE5-D4F0682050AB}.Debug|ARM64.Build.0 = Debug|ARM64 {63F9B82B-F589-4082-ABE5-D4F0682050AB}.Debug|Win32.ActiveCfg = Debug|Win32 {63F9B82B-F589-4082-ABE5-D4F0682050AB}.Debug|Win32.Build.0 = Debug|Win32 {63F9B82B-F589-4082-ABE5-D4F0682050AB}.Debug|x64.ActiveCfg = Debug|x64 {63F9B82B-F589-4082-ABE5-D4F0682050AB}.Debug|x64.Build.0 = Debug|x64 + {63F9B82B-F589-4082-ABE5-D4F0682050AB}.Release|ARM64.ActiveCfg = Release|ARM64 + {63F9B82B-F589-4082-ABE5-D4F0682050AB}.Release|ARM64.Build.0 = Release|ARM64 {63F9B82B-F589-4082-ABE5-D4F0682050AB}.Release|Win32.ActiveCfg = Release|Win32 {63F9B82B-F589-4082-ABE5-D4F0682050AB}.Release|Win32.Build.0 = Release|Win32 {63F9B82B-F589-4082-ABE5-D4F0682050AB}.Release|x64.ActiveCfg = Release|x64 {63F9B82B-F589-4082-ABE5-D4F0682050AB}.Release|x64.Build.0 = Release|x64 + {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751}.Debug|ARM64.Build.0 = Debug|ARM64 {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751}.Debug|Win32.ActiveCfg = Debug|Win32 {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751}.Debug|Win32.Build.0 = Debug|Win32 {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751}.Debug|x64.ActiveCfg = Debug|x64 {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751}.Debug|x64.Build.0 = Debug|x64 + {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751}.Release|ARM64.ActiveCfg = Release|ARM64 + {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751}.Release|ARM64.Build.0 = Release|ARM64 {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751}.Release|Win32.ActiveCfg = Release|Win32 {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751}.Release|Win32.Build.0 = Release|Win32 {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751}.Release|x64.ActiveCfg = Release|x64 {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751}.Release|x64.Build.0 = Release|x64 + {BAA70D0F-3EC7-4D10-91F0-974F1F49308B}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {BAA70D0F-3EC7-4D10-91F0-974F1F49308B}.Debug|ARM64.Build.0 = Debug|ARM64 {BAA70D0F-3EC7-4D10-91F0-974F1F49308B}.Debug|Win32.ActiveCfg = Debug|Win32 {BAA70D0F-3EC7-4D10-91F0-974F1F49308B}.Debug|Win32.Build.0 = Debug|Win32 {BAA70D0F-3EC7-4D10-91F0-974F1F49308B}.Debug|x64.ActiveCfg = Debug|x64 {BAA70D0F-3EC7-4D10-91F0-974F1F49308B}.Debug|x64.Build.0 = Debug|x64 + {BAA70D0F-3EC7-4D10-91F0-974F1F49308B}.Release|ARM64.ActiveCfg = Release|ARM64 + {BAA70D0F-3EC7-4D10-91F0-974F1F49308B}.Release|ARM64.Build.0 = Release|ARM64 {BAA70D0F-3EC7-4D10-91F0-974F1F49308B}.Release|Win32.ActiveCfg = Release|Win32 {BAA70D0F-3EC7-4D10-91F0-974F1F49308B}.Release|Win32.Build.0 = Release|Win32 {BAA70D0F-3EC7-4D10-91F0-974F1F49308B}.Release|x64.ActiveCfg = Release|x64 {BAA70D0F-3EC7-4D10-91F0-974F1F49308B}.Release|x64.Build.0 = Release|x64 + {1693F852-A207-4348-8223-222C2A7FEEEB}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {1693F852-A207-4348-8223-222C2A7FEEEB}.Debug|ARM64.Build.0 = Debug|ARM64 {1693F852-A207-4348-8223-222C2A7FEEEB}.Debug|Win32.ActiveCfg = Debug|Win32 {1693F852-A207-4348-8223-222C2A7FEEEB}.Debug|Win32.Build.0 = Debug|Win32 {1693F852-A207-4348-8223-222C2A7FEEEB}.Debug|x64.ActiveCfg = Debug|x64 {1693F852-A207-4348-8223-222C2A7FEEEB}.Debug|x64.Build.0 = Debug|x64 + {1693F852-A207-4348-8223-222C2A7FEEEB}.Release|ARM64.ActiveCfg = Release|ARM64 + {1693F852-A207-4348-8223-222C2A7FEEEB}.Release|ARM64.Build.0 = Release|ARM64 {1693F852-A207-4348-8223-222C2A7FEEEB}.Release|Win32.ActiveCfg = Release|Win32 {1693F852-A207-4348-8223-222C2A7FEEEB}.Release|Win32.Build.0 = Release|Win32 {1693F852-A207-4348-8223-222C2A7FEEEB}.Release|x64.ActiveCfg = Release|x64 {1693F852-A207-4348-8223-222C2A7FEEEB}.Release|x64.Build.0 = Release|x64 + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Debug|ARM64.Build.0 = Debug|ARM64 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Debug|Win32.ActiveCfg = Debug|Win32 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Debug|Win32.Build.0 = Debug|Win32 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Debug|x64.ActiveCfg = Debug|x64 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Debug|x64.Build.0 = Debug|x64 + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Release|ARM64.ActiveCfg = Release|ARM64 + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Release|ARM64.Build.0 = Release|ARM64 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Release|Win32.ActiveCfg = Release|Win32 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Release|Win32.Build.0 = Release|Win32 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Release|x64.ActiveCfg = Release|x64 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Release|x64.Build.0 = Release|x64 + {503AE687-C33A-45ED-93AA-83967E176D67}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {503AE687-C33A-45ED-93AA-83967E176D67}.Debug|ARM64.Build.0 = Debug|ARM64 {503AE687-C33A-45ED-93AA-83967E176D67}.Debug|Win32.ActiveCfg = Debug|Win32 {503AE687-C33A-45ED-93AA-83967E176D67}.Debug|Win32.Build.0 = Debug|Win32 {503AE687-C33A-45ED-93AA-83967E176D67}.Debug|x64.ActiveCfg = Debug|x64 {503AE687-C33A-45ED-93AA-83967E176D67}.Debug|x64.Build.0 = Debug|x64 + {503AE687-C33A-45ED-93AA-83967E176D67}.Release|ARM64.ActiveCfg = Release|ARM64 + {503AE687-C33A-45ED-93AA-83967E176D67}.Release|ARM64.Build.0 = Release|ARM64 {503AE687-C33A-45ED-93AA-83967E176D67}.Release|Win32.ActiveCfg = Release|Win32 {503AE687-C33A-45ED-93AA-83967E176D67}.Release|Win32.Build.0 = Release|Win32 {503AE687-C33A-45ED-93AA-83967E176D67}.Release|x64.ActiveCfg = Release|x64 {503AE687-C33A-45ED-93AA-83967E176D67}.Release|x64.Build.0 = Release|x64 + {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Debug|ARM64.Build.0 = Debug|ARM64 {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Debug|Win32.ActiveCfg = Debug|Win32 {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Debug|Win32.Build.0 = Debug|Win32 {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Debug|x64.ActiveCfg = Debug|x64 {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Debug|x64.Build.0 = Debug|x64 + {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Release|ARM64.ActiveCfg = Release|ARM64 + {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Release|ARM64.Build.0 = Release|ARM64 {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Release|Win32.ActiveCfg = Release|Win32 {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Release|Win32.Build.0 = Release|Win32 {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Release|x64.ActiveCfg = Release|x64 {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Release|x64.Build.0 = Release|x64 + {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037}.Debug|ARM64.Build.0 = Debug|ARM64 {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037}.Debug|Win32.ActiveCfg = Debug|Win32 {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037}.Debug|Win32.Build.0 = Debug|Win32 {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037}.Debug|x64.ActiveCfg = Debug|x64 {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037}.Debug|x64.Build.0 = Debug|x64 + {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037}.Release|ARM64.ActiveCfg = Release|ARM64 + {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037}.Release|ARM64.Build.0 = Release|ARM64 {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037}.Release|Win32.ActiveCfg = Release|Win32 {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037}.Release|Win32.Build.0 = Release|Win32 {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037}.Release|x64.ActiveCfg = Release|x64 {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037}.Release|x64.Build.0 = Release|x64 + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5}.Debug|ARM64.Build.0 = Debug|ARM64 + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5}.Debug|Win32.ActiveCfg = Debug|Win32 + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5}.Debug|Win32.Build.0 = Debug|Win32 + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5}.Debug|x64.ActiveCfg = Debug|x64 + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5}.Debug|x64.Build.0 = Debug|x64 + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5}.Release|ARM64.ActiveCfg = Release|ARM64 + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5}.Release|ARM64.Build.0 = Release|ARM64 + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5}.Release|Win32.ActiveCfg = Release|Win32 + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5}.Release|Win32.Build.0 = Release|Win32 + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5}.Release|x64.ActiveCfg = Release|x64 + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/sys/windows/vs/NetHack/NetHack.vcxproj b/sys/windows/vs/NetHack/NetHack.vcxproj index 7b28b0837..c6f2df87c 100644 --- a/sys/windows/vs/NetHack/NetHack.vcxproj +++ b/sys/windows/vs/NetHack/NetHack.vcxproj @@ -1,13 +1,7 @@ - + - - {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751} - Win32Proj - NetHack - 10.0 - @@ -15,6 +9,38 @@ + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751} + Win32Proj + NetHack + 10.0 + $(BinDir) @@ -41,20 +67,21 @@ /Gs /Oi- /w44774 /w45262 %(AdditionalOptions) - 4100;4244;4245;4310;4706;4820;4324 + 4100;4244;4245;4310;4706;4820;4324 + 4244;4245;4310;4706;4820;4324 Disabled Default Speed true $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);%(AdditionalIncludeDirectories) - WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;%(PreprocessorDefinitions) + WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;SND_SOUNDEFFECTS_AUTOMAP;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) stdclatest hacklib.lib;lualib.lib;kernel32.lib;dbghelp.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;winmm.lib;Winmm.lib;UserEnv.lib;bcrypt.lib;%(AdditionalDependencies) - $(SndWavDir);%(AdditionalIncludeDirectories) + $(SndWavDir);$(SysWindDir);%(AdditionalIncludeDirectories) @@ -86,6 +113,7 @@ + @@ -98,6 +126,7 @@ + @@ -189,7 +218,6 @@ - @@ -231,6 +259,7 @@ + diff --git a/sys/windows/vs/NetHackProperties.props b/sys/windows/vs/NetHackProperties.props index 0e5f10889..164cc760e 100644 --- a/sys/windows/vs/NetHackProperties.props +++ b/sys/windows/vs/NetHackProperties.props @@ -1,5 +1,5 @@ - + 3 @@ -8,7 +8,7 @@ $(VERSION_MAJOR).$(VERSION_MINOR).$(PATCHLEVEL) 5 4 - 6> + 8> $(LUA_MAJOR_VERSION).$(LUA_MINOR_VERSION).$(LUA_PATCH_LEVEL) $(ROOTDIR)lib\fmod\api\core\ fmod_vc.lib diff --git a/sys/windows/vs/NetHackW/NetHackW.vcxproj b/sys/windows/vs/NetHackW/NetHackW.vcxproj index 11e6baa99..cc3ec2563 100644 --- a/sys/windows/vs/NetHackW/NetHackW.vcxproj +++ b/sys/windows/vs/NetHackW/NetHackW.vcxproj @@ -1,6 +1,32 @@ + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + {CEC5D360-8804-454F-8591-002184C23499} NetHackW @@ -42,18 +68,26 @@ false + + false + false false + + false + false + /Gs /Oi- /w44774 /w45262 %(AdditionalOptions) - 4100;4244;4245;4310;4706;4820;4324 + 4100;4244;4245;4310;4706;4820;4324 + 4244;4245;4310;4706;4820;4324 Disabled true $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);%(AdditionalIncludeDirectories) - TILES;_WINDOWS;DLB;MSWIN_GRAPHICS;SAFEPROCS;NOTTYGRAPHICS;SND_LIB_WINDSOUND;USER_SOUNDS;HAS_STDINT_H;PDC_WIDE;%(PreprocessorDefinitions) + TILES;_WINDOWS;DLB;MSWIN_GRAPHICS;NOTTYGRAPHICS;SND_LIB_WINDSOUND;USER_SOUNDS;SND_SOUNDEFFECTS_AUTOMAP;HAS_STDINT_H;HAS_INTTYPES_H;PDC_WIDE;%(PreprocessorDefinitions) stdclatest stdclatest stdclatest @@ -62,10 +96,7 @@ NDEBUG;%(PreprocessorDefinitions) 0x0409 - $(SndWavDir);%(AdditionalIncludeDirectories) - $(SndWavDir);%(AdditionalIncludeDirectories) - $(SndWavDir);%(AdditionalIncludeDirectories) - $(SndWavDir);%(AdditionalIncludeDirectories) + $(SndWavDir);$(SysWindDir);%(AdditionalIncludeDirectories) Windows @@ -104,6 +135,7 @@ + @@ -115,6 +147,7 @@ + @@ -144,7 +177,9 @@ 4100;4244;4245;4310;4706;4820;4324 + 4100;4244;4245;4310;4706;4820;4324 4100;4244;4245;4310;4706;4820;4324 + 4100;4244;4245;4310;4706;4820;4324 @@ -215,7 +250,9 @@ 4100;4201;4244;4245;4310;4706;4820;4324 + 4100;4201;4244;4245;4310;4706;4820;4324 4100;4201;4244;4245;4310;4706;4820;4324 + 4100;4201;4244;4245;4310;4706;4820;4324 4100;4201;4244;4245;4310;4706;4820;4324 4100;4201;4244;4245;4310;4706;4820 @@ -226,7 +263,6 @@ - @@ -291,6 +327,7 @@ + diff --git a/sys/windows/vs/PDCurses/PDCurses.vcxproj b/sys/windows/vs/PDCurses/PDCurses.vcxproj index ab75f18db..10f9712ec 100644 --- a/sys/windows/vs/PDCurses/PDCurses.vcxproj +++ b/sys/windows/vs/PDCurses/PDCurses.vcxproj @@ -1,7 +1,33 @@ - + + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + {BAA70D0F-3EC7-4D10-91F0-974F1F49308B} Win32Proj @@ -34,7 +60,9 @@ 4996;4244;4267;%(DisableSpecificWarnings) + 4996;4244;4267;%(DisableSpecificWarnings) 4996;4244;4267;%(DisableSpecificWarnings) + 4996;4244;4267;%(DisableSpecificWarnings) @@ -56,11 +84,15 @@ 4996;4244;4267;%(DisableSpecificWarnings) + 4996;4244;4267;%(DisableSpecificWarnings) 4996;4244;4267;%(DisableSpecificWarnings) + 4996;4244;4267;%(DisableSpecificWarnings) 4996;4244;4267;%(DisableSpecificWarnings) + 4996;4244;4267;%(DisableSpecificWarnings) 4996;4244;4267;%(DisableSpecificWarnings) + 4996;4244;4267;%(DisableSpecificWarnings) @@ -100,6 +132,12 @@ _DEBUG;_LIB;PDC_RGB;%(PreprocessorDefinitions) + + + Disabled + _DEBUG;_LIB;PDC_RGB;%(PreprocessorDefinitions) + + MaxSpeed @@ -124,5 +162,17 @@ true + + + MaxSpeed + true + true + NDEBUG;_LIB;PDC_RGB;%(PreprocessorDefinitions) + + + true + true + + \ No newline at end of file diff --git a/sys/windows/vs/PDCursesGui/pdcursesgui.vcxproj b/sys/windows/vs/PDCursesGui/pdcursesgui.vcxproj index 1c62705f8..e709ade6d 100644 --- a/sys/windows/vs/PDCursesGui/pdcursesgui.vcxproj +++ b/sys/windows/vs/PDCursesGui/pdcursesgui.vcxproj @@ -2,6 +2,32 @@ + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + {1693F852-A207-4348-8223-222C2A7FEEEB} Win32Proj @@ -34,7 +60,9 @@ 4996;4244;4267;%(DisableSpecificWarnings) + 4996;4244;4267;%(DisableSpecificWarnings) 4996;4244;4267;%(DisableSpecificWarnings) + 4996;4244;4267;%(DisableSpecificWarnings) @@ -94,6 +122,12 @@ _DEBUG;_LIB;PDC_WIDE;PDC_RGB;%(PreprocessorDefinitions) + + + Disabled + _DEBUG;_LIB;PDC_WIDE;PDC_RGB;%(PreprocessorDefinitions) + + MaxSpeed @@ -118,5 +152,17 @@ true + + + MaxSpeed + true + true + NDEBUG;_LIB;PDC_WIDE;PDC_RGB;%(PreprocessorDefinitions) + + + true + true + + \ No newline at end of file diff --git a/sys/windows/vs/dirs.props b/sys/windows/vs/dirs.props index 8f1f17062..82d8dc1b1 100644 --- a/sys/windows/vs/dirs.props +++ b/sys/windows/vs/dirs.props @@ -1,7 +1,7 @@ - 5.4.6 + 5.4.8 $(MSBuildProjectDirectory)\..\..\..\..\ $(RootDir)binary\$(Configuration)\$(Platform)\ $(ProjectDir)obj\$(Configuration)\$(Platform)\$(TargetName)\ @@ -31,6 +31,7 @@ $(LibDir)lua-$(LUA_VERSION)\src\ $(RootDir)\sys\windows\vs\sftags\ $(RootDir)\sys\windows\vs\sfctool\ + $(RootDir)binary\$(Configuration)\$(Platform) $(LibDir)pdcursesmod\ @@ -40,4 +41,32 @@ $(SubmodulesDir)pdcursesmod\ $(PDCURSESMOD) + + + $(Platform) + + + ..\ARM64\ + arm64 + + + ..\ARM64\ + arm64 + + + ..\ARM64\ + arm64 + + + ..\x64\ + x64 + + + ..\x64\ + x64 + + + ..\x64\ + x64 + diff --git a/sys/windows/vs/dlb/afterdlb.proj b/sys/windows/vs/dlb/afterdlb.proj index 515473a3a..44a663187 100644 --- a/sys/windows/vs/dlb/afterdlb.proj +++ b/sys/windows/vs/dlb/afterdlb.proj @@ -2,12 +2,12 @@ - + diff --git a/sys/windows/vs/dlb/dlb.vcxproj b/sys/windows/vs/dlb/dlb.vcxproj index 71b96d823..44a1dad6a 100644 --- a/sys/windows/vs/dlb/dlb.vcxproj +++ b/sys/windows/vs/dlb/dlb.vcxproj @@ -2,6 +2,32 @@ + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC} 10.0 @@ -15,19 +41,25 @@ + + + + + + $(IncDir);$(SysWindDir);$(SysShareDir);$(LuaDir);%(AdditionalIncludeDirectories) - WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;%(PreprocessorDefinitions) + WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) stdclatest stdclatest stdclatest @@ -35,7 +67,9 @@ $(ToolsDir);%(AdditionalLibraryDirectories) + $(ToolsDir);%(AdditionalLibraryDirectories) hacklib.lib;%(AdditionalDependencies) + hacklib.lib;%(AdditionalDependencies) $(ToolsDir);%(AdditionalLibraryDirectories) @@ -47,7 +81,9 @@ $(ToolsDir);%(AdditionalLibraryDirectories) + $(ToolsDir);%(AdditionalLibraryDirectories) hacklib.lib;%(AdditionalDependencies) + hacklib.lib;%(AdditionalDependencies) @@ -68,4 +104,4 @@ - + \ No newline at end of file diff --git a/sys/windows/vs/fetchctags/fetchctags.vcxproj b/sys/windows/vs/fetchctags/fetchctags.vcxproj index 58df9ec83..8bc71cdb4 100644 --- a/sys/windows/vs/fetchctags/fetchctags.vcxproj +++ b/sys/windows/vs/fetchctags/fetchctags.vcxproj @@ -1,10 +1,18 @@ + + Debug + ARM64 + Debug Win32 + + Release + ARM64 + Release Win32 @@ -39,10 +47,18 @@ Makefile true + + Makefile + true + Makefile false + + Makefile + false + @@ -67,5 +83,4 @@ - - + \ No newline at end of file diff --git a/sys/windows/vs/files.props b/sys/windows/vs/files.props index 16a62721e..07f3c7421 100644 --- a/sys/windows/vs/files.props +++ b/sys/windows/vs/files.props @@ -33,6 +33,7 @@ + diff --git a/sys/windows/vs/hacklib/hacklib.vcxproj b/sys/windows/vs/hacklib/hacklib.vcxproj index 23b673271..3c7241496 100644 --- a/sys/windows/vs/hacklib/hacklib.vcxproj +++ b/sys/windows/vs/hacklib/hacklib.vcxproj @@ -6,11 +6,25 @@ + + Win32Proj + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} + hacklib + 10.0 + + + Debug + ARM64 + Debug Win32 + + Release + ARM64 + Release Win32 @@ -43,13 +57,6 @@ - - 17.0 - Win32Proj - {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} - hacklib - 10.0 - StaticLibrary @@ -67,13 +74,24 @@ StaticLibrary true - v143 + Unicode + v145 + + + StaticLibrary + true Unicode StaticLibrary false - v143 + true + Unicode + v145 + + + StaticLibrary + false true Unicode @@ -91,20 +109,26 @@ + + + + + + Level3 true - WIN32;_DEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) + WIN32;_DEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) true $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) stdclatest /w45262 %(AdditionalOptions) - + @@ -117,7 +141,7 @@ true true true - WIN32;NDEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) + WIN32;NDEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) true $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) stdclatest @@ -135,7 +159,23 @@ Level3 true - _DEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) + _DEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) + true + $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) + stdclatest + /w45262 %(AdditionalOptions) + + + + + true + + + + + Level3 + true + _DEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) true $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) stdclatest @@ -153,7 +193,27 @@ true true true - NDEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) + NDEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) + true + $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) + stdclatest + /w45262 %(AdditionalOptions) + + + + + true + true + true + + + + + Level3 + true + true + true + NDEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) true $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) stdclatest @@ -168,15 +228,6 @@ - - - - - - - - - - + \ No newline at end of file diff --git a/sys/windows/vs/lualib/lualib.vcxproj b/sys/windows/vs/lualib/lualib.vcxproj index 48cbb9f35..0849b2893 100644 --- a/sys/windows/vs/lualib/lualib.vcxproj +++ b/sys/windows/vs/lualib/lualib.vcxproj @@ -6,11 +6,25 @@ + + Win32Proj + {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} + lualib + 10.0 + + + Debug + ARM64 + Debug Win32 + + Release + ARM64 + Release Win32 @@ -26,12 +40,12 @@ - /Gs /Oi- /w44774 /w45262 %(AdditionalOptions) + /Gs /w44774 /w45262 %(AdditionalOptions) 4100;4244;4245;4310;4706;4820;4324 Disabled true $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);%(AdditionalIncludeDirectories) - TILES;_WINDOWS;DLB;MSWIN_GRAPHICS;SAFEPROCS;NOTTYGRAPHICS;SND_LIB_WINDSOUND;USER_SOUNDS;HAS_STDINT_H;PDC_WIDE;%(PreprocessorDefinitions) + TILES;_WINDOWS;DLB;MSWIN_GRAPHICS;SAFEPROCS;NOTTYGRAPHICS;SND_LIB_WINDSOUND;USER_SOUNDS;HAS_STDINT_H;HAS_INTTYPES_H;PDC_WIDE;%(PreprocessorDefinitions) stdclatest @@ -69,39 +83,29 @@ - - 17.0 - Win32Proj - {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} - lualib - 10.0 - - + StaticLibrary true - $(DefaultPlatformToolset) Unicode - - StaticLibrary - false - $(DefaultPlatformToolset) - true - Unicode - - - StaticLibrary - true + v143 - Unicode - - StaticLibrary - false + v143 - true - Unicode + + + v145 + + + v143 + + + v143 + + + v145 @@ -117,15 +121,21 @@ + + + + + + Level3 true - WIN32;_DEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) + WIN32;_DEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) true $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) /Gs /Oi- /w44774 %(AdditionalOptions) @@ -142,10 +152,11 @@ true true true - WIN32;NDEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) + WIN32;NDEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) true $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) /Gs /Oi- /w44774 %(AdditionalOptions) + ProgramDatabase @@ -159,7 +170,7 @@ Level3 true - _DEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) + _DEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) true $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) /Gs /Oi- /w44774 %(AdditionalOptions) @@ -170,16 +181,52 @@ true + + + Level3 + true + _DEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) + true + $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) + /Gs /w44774 %(AdditionalOptions) + + + + + true + + Level3 true true true - NDEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) + NDEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) true $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) /Gs /Oi- /w44774 %(AdditionalOptions) + ProgramDatabase + + + + + true + true + true + + + + + Level3 + true + false + true + NDEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) + true + $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) + /Gs /w44774 %(AdditionalOptions) + ProgramDatabase @@ -190,15 +237,6 @@ - - - - - - - - - - + \ No newline at end of file diff --git a/sys/windows/vs/makedefs/aftermakedefs.proj b/sys/windows/vs/makedefs/aftermakedefs.proj index 8aaf1e314..1a9e7d8bc 100644 --- a/sys/windows/vs/makedefs/aftermakedefs.proj +++ b/sys/windows/vs/makedefs/aftermakedefs.proj @@ -2,12 +2,12 @@ - - - - + + + + diff --git a/sys/windows/vs/makedefs/makedefs.vcxproj b/sys/windows/vs/makedefs/makedefs.vcxproj index 5b3c90584..b832056f9 100644 --- a/sys/windows/vs/makedefs/makedefs.vcxproj +++ b/sys/windows/vs/makedefs/makedefs.vcxproj @@ -2,6 +2,32 @@ + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + {BA3DD34C-04B7-40D0-B373-9329AA9E8945} 10.0 @@ -15,19 +41,25 @@ + + + + + + $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) - WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) + WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) stdclatest /w45262 %(AdditionalOptions) @@ -68,4 +100,4 @@ - + \ No newline at end of file diff --git a/sys/windows/vs/nhlua_h/nhlua_h.vcxproj b/sys/windows/vs/nhlua_h/nhlua_h.vcxproj new file mode 100644 index 000000000..842f541d8 --- /dev/null +++ b/sys/windows/vs/nhlua_h/nhlua_h.vcxproj @@ -0,0 +1,59 @@ + + + + + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {A3B1A65E-4B65-4B67-ADF1-0E38567013A5} + 10.0 + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sys/windows/vs/package/package.nmake b/sys/windows/vs/package/package.nmake index fb7f127f6..55b3e8e02 100644 --- a/sys/windows/vs/package/package.nmake +++ b/sys/windows/vs/package/package.nmake @@ -22,6 +22,7 @@ NHV=$(NHV:"=) #SUBMSDIR=submodules # NetHack git submodules PACKAGESDIR=vspackage # put in vspackage to distinguish ROOTDIR=..\..\..\.. # root of NetHack tree relative to project file +PlatformFileName=$(lowercase $(PlatformShortName)) # Directories we might have to collect things from # @@ -41,6 +42,7 @@ X11 = $(ROOTDIR)\win\X11 # X11 support files LIBDIR = $(ROOTDIR)\lib # libraries and external bits SUBMDIR = $(ROOTDIR)\submodules # NetHack git submodules SndWavDir = $(ROOTDIR)\sound\wav # sound files that get integrated +DocDir = $(ROOTDIR)\doc # Directories we might place collected things # @@ -73,8 +75,8 @@ FILESTOZIP=$(VSBINDIR)\Guidebook.txt $(VSBINDIR)\license \ DBGSYMS = NetHack.PDB NetHackW.PDB PDBTOZIP = ..\NetHack\symbols\$(Configuration)\$(Platform)\NetHack.PDB \ ..\NetHackW\symbols\$(Configuration)\$(Platform)\NetHackW.PDB -MAINZIP = $(VSPACKAGEDIR)\nethack-$(NHV)-win-$(PlatformShortName).zip -DBGSYMZIP = $(VSPACKAGEDIR)\nethack-$(NHV)-win-$(PlatformShortName)-debugsymbols.zip +MAINZIP = $(VSPACKAGEDIR)\nethack-$(NHV)-win-$(PlatformFileName).zip +DBGSYMZIP = $(VSPACKAGEDIR)\nethack-$(NHV)-win-$(PlatformFileName)-debugsymbols.zip packageall: packagezip @@ -91,11 +93,11 @@ $(DBGSYMZIP): $(PDBTOZIP) $(VSBINDIR)\license: $(BinDir)\license copy /Y $(BinDir)\license $@ $(VSBINDIR)\Guidebook.txt: $(BinDir)\Guidebook.txt - copy /Y $(BinDir)\Guidebook.txt $@ + copy /Y $(DocDir)\Guidebook.txt $@ $(VSBINDIR)\NetHack.exe: $(BinDir)\NetHack.exe copy /Y $(BinDir)\NetHack.exe $@ $(VSBINDIR)\NetHack.txt: $(BinDir)\NetHack.txt - copy /Y $(BinDir)\NetHack.txt $@ + copy /Y $(DocDir)\NetHack.txt $@ $(VSBINDIR)\NetHackW.exe: $(BinDir)\NetHackW.exe copy /Y $(BinDir)\NetHackW.exe $@ $(VSBINDIR)\opthelp: $(BinDir)\opthelp diff --git a/sys/windows/vs/package/package.vcxproj b/sys/windows/vs/package/package.vcxproj index 7b09e9277..f0c0c660c 100644 --- a/sys/windows/vs/package/package.vcxproj +++ b/sys/windows/vs/package/package.vcxproj @@ -1,10 +1,18 @@ + + Debug + ARM64 + Debug Win32 + + Release + ARM64 + Release Win32 @@ -40,10 +48,18 @@ Makefile true + + Makefile + true + Makefile false + + Makefile + false + @@ -54,9 +70,9 @@ - pushd $(vsDir)package %26%26 nmake -F package.nmake BinDir="$(BinDir)\" Platform="$(Platform)" PlatformShortName="$(PlatformShortName)" Configuration="$(Configuration)" NETHACK_VERSION="$(NETHACK_VERSION)" %26%26 popd - pushd $(vsDir)package %26%26 nmake -F package.nmake clean %26%26 popd - pushd $(vsDir)package %26%26 nmake -F package.nmake rebuild %26%26 popd + pushd $(vsDir)package %26%26 nmake /NOLOGO -F package.nmake BinDir="$(BinDir)\" Platform="$(Platform)" PlatformShortName="$(PlatformShortName)" Configuration="$(Configuration)" NETHACK_VERSION="$(NETHACK_VERSION)" %26%26 popd + pushd $(vsDir)package %26%26 nmake /NOLOGO -F package.nmake clean %26%26 popd + pushd $(vsDir)package %26%26 nmake /NOLOGO -F package.nmake rebuild %26%26 popd diff --git a/sys/windows/vs/recover/recover.vcxproj b/sys/windows/vs/recover/recover.vcxproj index d03553015..1fe6f0a16 100644 --- a/sys/windows/vs/recover/recover.vcxproj +++ b/sys/windows/vs/recover/recover.vcxproj @@ -2,6 +2,32 @@ + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + {2F35F228-6733-4FE5-9B46-B3AA10D4BC2E} 10.0 @@ -19,7 +45,7 @@ $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) - WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;MINIMAL_FOR_RECOVER;%(PreprocessorDefinitions) + WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;HAS_INTTYPES_H;MINIMAL_FOR_RECOVER;%(PreprocessorDefinitions) stdclatest /w45262 %(AdditionalOptions) @@ -31,7 +57,7 @@ - WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;MINIMAL_FOR_RECOVER;%(PreprocessorDefinitions) + WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;HAS_INTTYPES_H;MINIMAL_FOR_RECOVER;%(PreprocessorDefinitions) @@ -53,4 +79,4 @@ - + \ No newline at end of file diff --git a/sys/windows/vs/sfctool.sln b/sys/windows/vs/sfctool.sln index 06ef30f69..3bf333c50 100644 --- a/sys/windows/vs/sfctool.sln +++ b/sys/windows/vs/sfctool.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.13.35913.81 +# Visual Studio Version 18 +VisualStudioVersion = 18.0.11205.157 d18.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sfctool", "sfctool\sfctool.vcxproj", "{3BFA3C14-6DA2-4750-B1C6-028B9BCE825E}" ProjectSection(ProjectDependencies) = postProject @@ -20,40 +20,58 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hacklib", "hacklib\hacklib. EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 + Release|ARM64 = Release|ARM64 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3BFA3C14-6DA2-4750-B1C6-028B9BCE825E}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {3BFA3C14-6DA2-4750-B1C6-028B9BCE825E}.Debug|ARM64.Build.0 = Debug|ARM64 {3BFA3C14-6DA2-4750-B1C6-028B9BCE825E}.Debug|x64.ActiveCfg = Debug|x64 {3BFA3C14-6DA2-4750-B1C6-028B9BCE825E}.Debug|x64.Build.0 = Debug|x64 {3BFA3C14-6DA2-4750-B1C6-028B9BCE825E}.Debug|x86.ActiveCfg = Debug|Win32 {3BFA3C14-6DA2-4750-B1C6-028B9BCE825E}.Debug|x86.Build.0 = Debug|Win32 + {3BFA3C14-6DA2-4750-B1C6-028B9BCE825E}.Release|ARM64.ActiveCfg = Release|ARM64 + {3BFA3C14-6DA2-4750-B1C6-028B9BCE825E}.Release|ARM64.Build.0 = Release|ARM64 {3BFA3C14-6DA2-4750-B1C6-028B9BCE825E}.Release|x64.ActiveCfg = Release|x64 {3BFA3C14-6DA2-4750-B1C6-028B9BCE825E}.Release|x64.Build.0 = Release|x64 {3BFA3C14-6DA2-4750-B1C6-028B9BCE825E}.Release|x86.ActiveCfg = Release|Win32 {3BFA3C14-6DA2-4750-B1C6-028B9BCE825E}.Release|x86.Build.0 = Release|Win32 + {628C594A-7565-4366-9FCA-41DB67C6B615}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {628C594A-7565-4366-9FCA-41DB67C6B615}.Debug|ARM64.Build.0 = Debug|ARM64 {628C594A-7565-4366-9FCA-41DB67C6B615}.Debug|x64.ActiveCfg = Debug|x64 {628C594A-7565-4366-9FCA-41DB67C6B615}.Debug|x64.Build.0 = Debug|x64 {628C594A-7565-4366-9FCA-41DB67C6B615}.Debug|x86.ActiveCfg = Debug|Win32 {628C594A-7565-4366-9FCA-41DB67C6B615}.Debug|x86.Build.0 = Debug|Win32 + {628C594A-7565-4366-9FCA-41DB67C6B615}.Release|ARM64.ActiveCfg = Release|ARM64 + {628C594A-7565-4366-9FCA-41DB67C6B615}.Release|ARM64.Build.0 = Release|ARM64 {628C594A-7565-4366-9FCA-41DB67C6B615}.Release|x64.ActiveCfg = Release|x64 {628C594A-7565-4366-9FCA-41DB67C6B615}.Release|x64.Build.0 = Release|x64 {628C594A-7565-4366-9FCA-41DB67C6B615}.Release|x86.ActiveCfg = Release|Win32 {628C594A-7565-4366-9FCA-41DB67C6B615}.Release|x86.Build.0 = Release|Win32 + {AAE63AD3-8185-4E19-B6A3-13F4CB891AAD}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {AAE63AD3-8185-4E19-B6A3-13F4CB891AAD}.Debug|ARM64.Build.0 = Debug|ARM64 {AAE63AD3-8185-4E19-B6A3-13F4CB891AAD}.Debug|x64.ActiveCfg = Debug|x64 {AAE63AD3-8185-4E19-B6A3-13F4CB891AAD}.Debug|x64.Build.0 = Debug|x64 {AAE63AD3-8185-4E19-B6A3-13F4CB891AAD}.Debug|x86.ActiveCfg = Debug|Win32 {AAE63AD3-8185-4E19-B6A3-13F4CB891AAD}.Debug|x86.Build.0 = Debug|Win32 + {AAE63AD3-8185-4E19-B6A3-13F4CB891AAD}.Release|ARM64.ActiveCfg = Release|ARM64 + {AAE63AD3-8185-4E19-B6A3-13F4CB891AAD}.Release|ARM64.Build.0 = Release|ARM64 {AAE63AD3-8185-4E19-B6A3-13F4CB891AAD}.Release|x64.ActiveCfg = Release|x64 {AAE63AD3-8185-4E19-B6A3-13F4CB891AAD}.Release|x64.Build.0 = Release|x64 {AAE63AD3-8185-4E19-B6A3-13F4CB891AAD}.Release|x86.ActiveCfg = Release|Win32 {AAE63AD3-8185-4E19-B6A3-13F4CB891AAD}.Release|x86.Build.0 = Release|Win32 + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Debug|ARM64.Build.0 = Debug|ARM64 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Debug|x64.ActiveCfg = Debug|x64 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Debug|x64.Build.0 = Debug|x64 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Debug|x86.ActiveCfg = Debug|Win32 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Debug|x86.Build.0 = Debug|Win32 + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Release|ARM64.ActiveCfg = Release|ARM64 + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Release|ARM64.Build.0 = Release|ARM64 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Release|x64.ActiveCfg = Release|x64 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Release|x64.Build.0 = Release|x64 {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Release|x86.ActiveCfg = Release|Win32 diff --git a/sys/windows/vs/sfctool/sfctool.vcxproj b/sys/windows/vs/sfctool/sfctool.vcxproj index 73fee3c05..ad1971969 100644 --- a/sys/windows/vs/sfctool/sfctool.vcxproj +++ b/sys/windows/vs/sfctool/sfctool.vcxproj @@ -11,10 +11,18 @@ $(BinDir) + + Debug + ARM64 + Debug Win32 + + Release + ARM64 + Release Win32 @@ -95,6 +103,12 @@ v143 Unicode + + Application + true + v143 + Unicode + Application false @@ -102,6 +116,13 @@ true Unicode + + Application + false + v143 + true + Unicode + @@ -116,14 +137,20 @@ + + + + + + $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 stdclatest true @@ -139,7 +166,7 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_NDEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_NDEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 stdclatest true @@ -159,7 +186,23 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + Level3 + stdclatest + true + true + + + Console + true + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;userenv.lib;advapi32.lib;%(AdditionalDependencies) + + + + + $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 stdclatest true @@ -175,7 +218,27 @@ $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_NDEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_NDEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + Level3 + stdclatest + true + true + true + true + + + Console + true + true + true + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;userenv.lib;advapi32.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) + WIN32;_NDEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 stdclatest true @@ -195,4 +258,4 @@ - \ No newline at end of file + diff --git a/sys/windows/vs/sftags/sftags.vcxproj b/sys/windows/vs/sftags/sftags.vcxproj index f2979e613..52c63d47a 100644 --- a/sys/windows/vs/sftags/sftags.vcxproj +++ b/sys/windows/vs/sftags/sftags.vcxproj @@ -8,10 +8,18 @@ + + Debug + ARM64 + Debug Win32 + + Release + ARM64 + Release Win32 @@ -55,6 +63,12 @@ v143 Unicode + + Application + true + v143 + Unicode + Application false @@ -62,6 +76,13 @@ true Unicode + + Application + false + v143 + true + Unicode + @@ -76,14 +97,20 @@ + + + + + + $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 true true @@ -91,14 +118,14 @@ Console true - $(ToolsDir);%(AdditionalLibraryDirectories) - hacklib.lib;%(AdditionalDependencies) + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;%(AdditionalDependencies) $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 true true @@ -110,14 +137,14 @@ true true true - $(ToolsDir);%(AdditionalLibraryDirectories) - hacklib.lib;%(AdditionalDependencies) + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;%(AdditionalDependencies) $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 true true @@ -125,14 +152,29 @@ Console true - $(ToolsDir);%(AdditionalLibraryDirectories) - hacklib.lib;%(AdditionalDependencies) + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;%(AdditionalDependencies) + + + + + $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + Level3 + true + true + + + Console + true + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;%(AdditionalDependencies) $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) Level3 true true @@ -145,14 +187,34 @@ true true true - $(ToolsDir);%(AdditionalLibraryDirectories) - hacklib.lib;%(AdditionalDependencies) + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;%(AdditionalDependencies) + + + + + $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);$(UtilDir);%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;WIN32CON;NO_TILE_C;DLB;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;HAS_INTTYPES_H;SFCTOOL;NOPANICTRACE;NOCRASHREPORT;NO_CHRONICLE;%(PreprocessorDefinitions) + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;%(AdditionalDependencies) - + diff --git a/sys/windows/vs/tile2bmp/aftertile2bmp.proj b/sys/windows/vs/tile2bmp/aftertile2bmp.proj index 0d7525d5f..e5576ee61 100644 --- a/sys/windows/vs/tile2bmp/aftertile2bmp.proj +++ b/sys/windows/vs/tile2bmp/aftertile2bmp.proj @@ -2,9 +2,9 @@ - + diff --git a/sys/windows/vs/tile2bmp/tile2bmp.vcxproj b/sys/windows/vs/tile2bmp/tile2bmp.vcxproj index 8207ebceb..6ed724f2b 100644 --- a/sys/windows/vs/tile2bmp/tile2bmp.vcxproj +++ b/sys/windows/vs/tile2bmp/tile2bmp.vcxproj @@ -2,6 +2,32 @@ + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + {642BC75D-ABAF-403E-8224-7C725FD4CB42} 10.0 @@ -15,26 +41,32 @@ + + + + + + $(IncDir);$(SysWindDir);$(SysShareDir);$(LuaDir);%(AdditionalIncludeDirectories) - WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;%(PreprocessorDefinitions) + WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) stdclatest /w45262 %(AdditionalOptions) - $(ToolsDir);%(AdditionalLibraryDirectories) - hacklib.lib;%(AdditionalDependencies) - + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;%(AdditionalDependencies) + @@ -56,4 +88,4 @@ - + \ No newline at end of file diff --git a/sys/windows/vs/tilemap/aftertilemap.proj b/sys/windows/vs/tilemap/aftertilemap.proj index a8795e96a..703f2c46a 100644 --- a/sys/windows/vs/tilemap/aftertilemap.proj +++ b/sys/windows/vs/tilemap/aftertilemap.proj @@ -2,9 +2,9 @@ - + diff --git a/sys/windows/vs/tilemap/tilemap.vcxproj b/sys/windows/vs/tilemap/tilemap.vcxproj index fdbbc92e7..70ce0296b 100644 --- a/sys/windows/vs/tilemap/tilemap.vcxproj +++ b/sys/windows/vs/tilemap/tilemap.vcxproj @@ -2,6 +2,32 @@ + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + {93F10526-209E-41D7-BBEA-775787876895} 10.0 @@ -15,21 +41,28 @@ + + + + + + + $(IncDir);$(SysWindDir);$(SysShareDir);$(LuaDir);%(AdditionalIncludeDirectories) - WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;%(PreprocessorDefinitions) + WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;HAS_INTTYPES_H;%(PreprocessorDefinitions) stdclatest /w45262 %(AdditionalOptions) @@ -96,4 +129,4 @@ - + \ No newline at end of file diff --git a/sys/windows/vs/uudecode/afteruudecode.proj b/sys/windows/vs/uudecode/afteruudecode.proj index 92286f678..32cb45898 100644 --- a/sys/windows/vs/uudecode/afteruudecode.proj +++ b/sys/windows/vs/uudecode/afteruudecode.proj @@ -2,78 +2,78 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sys/windows/vs/uudecode/uudecode.vcxproj b/sys/windows/vs/uudecode/uudecode.vcxproj index 0975cacdf..296d97ab8 100644 --- a/sys/windows/vs/uudecode/uudecode.vcxproj +++ b/sys/windows/vs/uudecode/uudecode.vcxproj @@ -2,6 +2,32 @@ + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + {63F9B82B-F589-4082-ABE5-D4F0682050AB} 10.0 @@ -15,12 +41,18 @@ + + + + + + diff --git a/sys/windows/windmain.c b/sys/windows/windmain.c index a2fe2da5e..a5bf3ae65 100644 --- a/sys/windows/windmain.c +++ b/sys/windows/windmain.c @@ -14,12 +14,7 @@ #include #include -#if !defined(SAFEPROCS) -#error You must #define SAFEPROCS to build windmain.c -#endif - static void nhusage(void); -static void early_options(int argc, char **argv); char *exename(void); boolean fakeconsole(void); void freefakeconsole(void); @@ -68,7 +63,6 @@ char windows_yn_function(const char *, const char *, char); #ifdef WIN32CON extern int windows_console_custom_nhgetch(void); -void safe_routines(void); int tty_self_recover_prompt(void); #endif @@ -107,6 +101,13 @@ void update_file(const char *, const char *, const char *, const char *, BOOL); void windows_raw_print_bold(const char *); +void set_emergency_io(void); +staticfn void stdio_wait_synch(void); +staticfn void stdio_raw_print(const char *str); +staticfn void stdio_nonl_raw_print(const char *str); +staticfn void stdio_raw_print_bold(const char *str); +staticfn int stdio_nhgetch(void); + #ifdef PORT_HELP void port_help(void); #endif @@ -157,22 +158,12 @@ MAIN(int argc, char *argv[]) HWND hwnd; HDC hdc; int bpp; + char *dir = NULL; #ifdef _MSC_VER - _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); + _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); #endif -#ifdef WIN32CON - /* - * Get a set of valid safe windowport function - * pointers during early startup initialization. - */ - safe_routines(); -#endif /* WIN32CON */ - -#ifndef MSWIN_GRAPHICS - early_init(argc, argv); /* already in WinMain for MSWIN_GRAPHICS */ -#endif /* setting iflags.colorcount has to be after early_init() * because it zeros out all of iflags */ hwnd = GetDesktopWindow(); @@ -207,6 +198,44 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ #endif gh.hname = "NetHack"; /* used for syntax messages */ + set_emergency_io(); +#ifndef MSWIN_GRAPHICS + early_init(argc, argv); /* already in WinMain for MSWIN_GRAPHICS */ +#endif + set_default_prefix_locations( + argv[0]); /* must be re-done after initoptions_init() + * which clears out gp.fqn_prefix[] */ + copy_sysconf_content(); + copy_symbols_content(); + /* Now that sysconf has had a chance to set the TROUBLEPREFIX, don't + allow it to be changed from here on out. */ + fqn_prefix_locked[TROUBLEPREFIX] = TRUE; + copy_config_content(); + + // if (iflags.windowtype_deferred && gc.chosen_windowtype[0]) + // windowtype = gc.chosen_windowtype; + // windowtype = gc.chosen_windowtype; + +#if !defined(MSWIN_GRAPHICS) + nethack_enter_consoletty(); + consoletty_open(1); +#endif + +#ifdef EARLY_CONFIGFILE_PASS + rcfile_interface_options(); + if (gc.chosen_windowtype && *gc.chosen_windowtype) + windowtype = gc.chosen_windowtype; +#endif + + if (!windowtype) { +#ifdef MSWIN_GRAPHICS + windowtype = "mswin"; +#else + windowtype = "tty"; +#endif + } + choose_windows( + windowtype); /* sets all the window port function pointers */ #if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) /* Save current directory and make sure it gets restored when @@ -215,29 +244,25 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ if (getcwd(orgdir, sizeof orgdir) == (char *) 0) error("NetHack: current directory path too long"); #endif + getreturn_enabled = TRUE; initoptions_init(); // This allows OPTIONS in syscf on Windows. - set_default_prefix_locations(argv[0]); + set_default_prefix_locations( + argv[0]); /* must be re-done after initoptions_init() + * which clears out gp.fqn_prefix[] */ + // iflags.windowtype_deferred = TRUE; + program_state.early_options = 1; + /* if (GUILaunched || IsDebuggerPresent()) */ + early_options(&argc, &argv, &dir); + program_state.early_options = 0; + + + initoptions(); #if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) chdir(gf.fqn_prefix[HACKPREFIX]); #endif - /* if (GUILaunched || IsDebuggerPresent()) */ - getreturn_enabled = TRUE; - check_recordfile((char *) 0); - iflags.windowtype_deferred = TRUE; - copy_sysconf_content(); - copy_symbols_content(); - early_options(argc, argv); - initoptions(); - - /* Now that sysconf has had a chance to set the TROUBLEPREFIX, don't - allow it to be changed from here on out. */ - fqn_prefix_locked[TROUBLEPREFIX] = TRUE; - - copy_config_content(); - /* did something earlier flag a need to exit without starting a game? */ if (windows_startup_state > 0) { raw_printf("Exiting."); @@ -264,8 +289,21 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ && (strstri(argv[0], "nethackw.exe") || GUILaunched)) iflags.windowtype_locked = TRUE; #endif - windowtype = default_window_sys; +#ifdef WINCHAIN + commit_windowchain(); +#endif + init_nhwindows(&argc, argv); +#ifdef TTY_GRAPHICS + if WINDOWPORT(tty) { + int i; + for (i = 0; i < 20; ++i) { + nh_delay_output(); + } + + /* wait_synch(); */ + } +#endif #ifdef DLB if (!dlb_init()) { pline("%s\n%s\n%s\n%s\n\n", copyright_banner_line(1), @@ -279,18 +317,6 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ } #endif - if (!iflags.windowtype_locked) { -#if defined(TTY_GRAPHICS) - Strcpy(default_window_sys, "tty"); -#else -#if defined(CURSES_GRAPHICS) && !defined(MSWIN_GRAPHICS) - Strcpy(default_window_sys, "curses"); -#endif /* CURSES */ -#endif /* TTY */ - if (iflags.windowtype_deferred && gc.chosen_windowtype[0]) - windowtype = gc.chosen_windowtype; - } - choose_windows(windowtype); #if defined(SND_LIB_FMOD) assign_soundlib(soundlib_fmod); #elif defined(SND_LIB_WINDSOUND) @@ -300,19 +326,11 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ u.uhp = 1; /* prevent RIP on early quits */ u.ux = 0; /* prevent flush_screen() */ - nethack_enter(argc, argv); iflags.use_background_glyph = FALSE; if (WINDOWPORT(mswin)) iflags.use_background_glyph = TRUE; -#ifdef WIN32CON - if (WINDOWPORT(tty)) - consoletty_open(1); -#endif -#ifdef WINCHAIN - commit_windowchain(); -#endif - init_nhwindows(&argc, argv); +// init_nhwindows(&argc, argv); #ifdef WIN32CON if (WINDOWPORT(tty)) @@ -451,6 +469,7 @@ attempt_restore: RESTORE_WARNING_UNREACHABLE_CODE +#if 0 static void early_options(int argc, char *argv[]) { @@ -671,6 +690,7 @@ nhusage(void) (void) printf("%s\n", buf1); #undef ADD_USAGE } +#endif /* 0 */ /* copy file if destination does not exist */ void @@ -813,20 +833,6 @@ copy_hack_content(void) gf.fqn_prefix[DATAPREFIX], OPTIONFILE, FALSE); } -#ifdef WIN32CON -void -safe_routines(void) -{ - /* - * Get a set of valid safe windowport function - * pointers during early startup initialization. - */ - if (!WINDOWPORT(safestartup)) - windowprocs = *get_safe_procs(1); - if (!GUILaunched) - windowprocs.win_nhgetch = windows_console_custom_nhgetch; -} -#endif #ifdef PORT_HELP void @@ -1181,9 +1187,7 @@ tty_self_recover_prompt(void) c = 'n'; ct = 0; saved_procs = windowprocs; - if (!WINDOWPORT(safestartup)) - windowprocs = *get_safe_procs(2); /* arg 2 uses no-newline variant */ - windowprocs.win_nhgetch = windows_console_custom_nhgetch; + raw_print("\n"); raw_print("\n"); raw_print("\n"); @@ -1297,4 +1301,79 @@ other_self_recover_prompt(void) } return retval; } -/*windmain.c*/ + +#ifdef CHDIR +void +chdirx(const char *dir, boolean wr) +{ + static char thisdir[] = "."; + + if (dir && chdir(dir) < 0) { + error("Cannot chdir to %s.", dir); + } + + /* warn the player if we can't write the record file */ + /* perhaps we should also test whether . is writable */ + /* unfortunately the access system-call is worthless */ + if (wr) + check_recordfile(dir ? dir : thisdir); +} +#endif /* CHDIR */ + +void +set_emergency_io(void) +{ + windowprocs.win_raw_print = stdio_raw_print; + windowprocs.win_raw_print_bold = stdio_raw_print_bold; + windowprocs.win_nhgetch = stdio_nhgetch; + windowprocs.win_wait_synch = stdio_wait_synch; +} + + +/* Add to your code: windowprocs.win_raw_print = stdio_wait_synch; */ +void +stdio_wait_synch(void) +{ + char valid[] = { ' ', '\n', '\r', '\033', '\0' }; + + fprintf(stdout, "--More--"); + (void) fflush(stdout); + while (!strchr(valid, nhgetch())) + ; +} + +/* Add to your code: windowprocs.win_raw_print = stdio_raw_print; */ +void +stdio_raw_print(const char *str) +{ + if (str) + fprintf(stdout, "%s\n", str); + return; +} + +/* no newline variation, add to your code: + windowprocs.win_raw_print = stdio_nonl_raw_print; */ +void +stdio_nonl_raw_print(const char *str) +{ + if (str) + fprintf(stdout, "%s", str); + return; +} + +/* Add to your code: windowprocs.win_raw_print_bold = stdio_raw_print_bold; */ +void +stdio_raw_print_bold(const char *str) +{ + stdio_raw_print(str); + return; +} + +/* Add to your code: windowprocs.win_nhgetch = stdio_nhgetch; */ +int +stdio_nhgetch(void) +{ + return getchar(); +} + + /*windmain.c*/ diff --git a/sys/windows/windsys.c b/sys/windows/windsys.c index 6be9b6407..83e63d608 100644 --- a/sys/windows/windsys.c +++ b/sys/windows/windsys.c @@ -25,6 +25,7 @@ #include "wintty.h" #endif #include +#include #ifdef WIN32 #include @@ -72,7 +73,6 @@ static HWND GetConsoleHwnd(void); extern void backsp(void); #endif int windows_console_custom_nhgetch(void); -extern void safe_routines(void); int windows_early_options(const char *window_opt); unsigned long sys_random_seed(void); #if 0 @@ -178,7 +178,7 @@ filesize(char *file) * Chdrive() changes the default drive. */ void -chdrive(char *str) +chdrive(const char *str) { char *ptr; char drive; @@ -260,11 +260,11 @@ VA_DECL(const char *, s) buf[0] = '\n'; (void) vsnprintf(&buf[1], sizeof buf - (1 + sizeof "\n"), s, VA_ARGS); Strcat(buf, "\n"); - msmsg(buf); + msmsg("%s", buf); } else { (void) vsnprintf(buf, sizeof buf - sizeof "\n", s, VA_ARGS); Strcat(buf, "\n"); - raw_printf(buf); + raw_printf("%s",buf); } #ifdef MSWIN_GRAPHICS if (windowprocs.win_raw_print == mswin_raw_print) @@ -292,10 +292,6 @@ win32_abort(void) exit_nhwindows((char *) 0); iflags.window_inited = FALSE; } -#ifdef WIN32CON - if (!WINDOWPORT(mswin) && !WINDOWPORT(safestartup)) - safe_routines(); -#endif if (wizard) { raw_print("Execute debug breakpoint wizard?"); if ((c = nhgetch()) == 'y' || c == 'Y') @@ -521,15 +517,6 @@ nethack_exit(int code) * GUILaunched is defined and set in consoletty.c. */ - -#ifdef WIN32CON - if (!GUILaunched) { - windowprocs = *get_safe_procs(1); - /* use our custom version which works - a little cleaner than the stdio one */ - windowprocs.win_nhgetch = windows_console_custom_nhgetch; - } else -#endif if (getreturn_enabled) { raw_print("\n"); if (iflags.window_inited) @@ -579,10 +566,6 @@ getreturn(const char *str) initializing the window port */ void nethack_enter_windows(void) { -#ifdef WIN32CON - if (WINDOWPORT(tty)) - nethack_enter_consoletty(); -#endif } /* CP437 to Unicode mapping according to the Unicode Consortium */ @@ -827,9 +810,9 @@ get_executable_path(void) path_buffer[length] = '\0'; #endif - char *seperator = strrchr(path_buffer, PATH_SEPARATOR); - if (seperator) - *seperator = '\0'; + char *separator = strrchr(path_buffer, PATH_SEPARATOR); + if (separator) + *separator = '\0'; path_buffer_set = TRUE; return path_buffer; @@ -1337,8 +1320,8 @@ printf("E2: M=%s e=%d\n",msg,errnum); int win32_cr_gettrace(int maxframes USED_IF_BACKTRACE, - char *out USED_IF_BACKTRACE, - int outsize USED_IF_BACKTRACE) + char *out USED_IF_BACKTRACE, + int outsize USED_IF_BACKTRACE) { #ifdef USE_BACKTRACE userstate.error_count = 0; diff --git a/test/test_des.lua b/test/test_des.lua index 4cf7f53fd..151433d58 100644 --- a/test/test_des.lua +++ b/test/test_des.lua @@ -88,6 +88,8 @@ function test_monster() des.monster(); des.monster("gnome") des.monster("S") + des.monster(":", {12,07}); + des.monster("K", 13,07); des.monster("giant eel",11,06); des.monster("hill giant", {08,06}); des.monster({ id = "ogre" }) @@ -100,10 +102,13 @@ function test_monster() des.monster({ class = "H", peaceful = 0 }) des.monster({ id = "giant mimic", appear_as = "obj:boulder" }); des.monster({ id = "giant mimic", appear_as = "ter:altar" }); + des.monster({ id = "giant mimic", appear_as = "mon:giant mimic" }); des.monster({ id = "chameleon", appear_as = "mon:bat" }); + des.monster({ id = "chameleon", appear_as = "mon:yellow light" }); + des.monster({ id = "chameleon", appear_as = "mon:random" }); des.monster({ class = "H", asleep = 1, female = 1, invisible = 1, cancelled = 1, revived = 1, avenge = 1, stunned = 1, confused = 1, fleeing = 20, blinded = 20, paralyzed = 20 }) des.monster({ class = "H", asleep = true, female = true, invisible = true, cancelled = true, revived = true, avenge = true, stunned = true, confused = true }); - des.monster({ id = "ogre", x = 10, y = 15, name = "Fred", + des.monster({ id = "ogre", x = 10, y = 15, name = "Fred", keep_default_invent = true, inventory = function() des.object(); des.object("["); @@ -116,6 +121,14 @@ function test_monster() des.monster({ id = "lurker above", adjacentok = true }); des.monster({ id = "gnome", ignorewater = true }); des.monster({ id = "xan", countbirth = false }); + des.monster({ id = "Angel", align = "law" }); + des.monster({ id = "archeologist" }); + des.monster({ id = "wizard", name = "Rincewind", peaceful = true }); + + for i = nhc.LOW_PM, nhc.HIGH_PM do + des.monster({ id = nh.int_to_pmname(i) }); + end + des.reset_level(); des.level_init(); end @@ -125,7 +138,10 @@ function test_object() des.level_init(); des.object() des.object("*") + des.object("*", 55, 12); + des.object("*", {55, 12}); des.object({ class = "%" }); + des.object({ class = "$" }); des.object({ id = "statue", contents=0 }) des.object("sack") des.object({ x = 41, y = 03 }) @@ -135,6 +151,9 @@ function test_object() des.object("diamond", {68, 04}) des.object({ id = "egg", x = 05, y = 04, montype = "yellow dragon" }); des.object({ id = "egg", x = 06, y = 04, montype = "yellow dragon", laid_by_you = true }); + des.object({ id = "tin", montype = "empty" }); + des.object({ id = "tin", montype = "spinach" }); + des.object({ id = "tin", montype = "floating eye" }); des.object({ id = "corpse", montype = "valkyrie" }) des.object({ id = "statue", montype = "C", historic = true, male = true }); des.object({ id = "statue", montype = "C", historic = true, female = true }); @@ -151,12 +170,37 @@ function test_object() des.object({ id = "oil lamp", lit = 1 }); des.object({ id = "silver dagger", spe = 127, buc = "cursed" }); des.object({ id = "silver dagger", spe = -127, buc = "blessed" }); + des.object({ class = "/", buc = "uncursed" }); + des.object({ class = "(", buc = "not-cursed" }); + des.object({ class = ")", buc = "not-uncursed" }); + des.object({ class = "=", buc = "not-blessed" }); des.object({ id = "bamboo arrow", quantity = 100 }); des.object({ id = "leather armor", eroded = 1 }); des.object({ id = "probing", recharged = 2, spe = 3 }); des.object({ name = "Random object" }); des.object({ class = "*", name = "Random stone" }); des.object({ id ="broadsword", name = "Dragonbane" }) + + for i = nhc.FIRST_OBJECT, nhc.LAST_OBJECT do + local oid, oclass = nh.int_to_objname(i); + if (oid ~= "") then + local o = des.object({ id = oid, class = oclass }); + local o_t = o:totable(); + + -- crysknife reverts to worm tooth on the floor + if not(oid == "crysknife" and o_t.otyp_name == "worm tooth") then + if (o_t.otyp_name ~= oid) then + error("object name \"" .. o_t.otyp_name .. "\" created, wanted \"" .. oid .. "\""); + end + if (o_t.oclass ~= oclass) then + local str = string.format("object class \"%s\" created, wanted \"%s\" (%s)", o_t.oclass, oclass, oid); + error(str); + end + end + + end + end + des.reset_level(); des.level_init(); end @@ -200,6 +244,7 @@ function test_altar() des.altar({ coord = {48, 20 }, type = "altar", align = "law" }); des.altar({ coord = {50, 20 }, type = "shrine", align = "noalign" }); des.altar({ coord = {52, 20 }, type = "sanctum", align = "coaligned" }); + des.altar({ type = "altar", align = "noncoaligned" }); end function test_map() @@ -305,14 +350,17 @@ function test_trap() des.trap("level teleport", {42, 06}); check_trap_at(42, 06, "level teleport"); - des.trap({ type = "falling rock", x = 43, y = 06 }); + des.trap({ type = "falling rock", x = 43, y = 06, victim = false }); check_trap_at(43, 06, "falling rock"); - des.trap({ type = "dart", coord = {44, 06} }); + des.trap({ type = "dart", coord = {44, 06}, seen = true }); check_trap_at(44, 06, "dart"); des.trap(); des.trap("rust"); + des.trap({ type = "web", spider_on_web = false }); + des.trap({ type = "rolling boulder", coord = {40,10}, launchfrom = {39,11} }); + des.trap({ type = "teleport", teledest = {5,5} }); end function test_wall_prop() @@ -384,15 +432,24 @@ function test_room() des.room({ x=4, y=3, w=3,h=3 }); end }); - des.room({ type="ordinary", coord={3, 3}, w=3, h=3 }); - des.room(); + des.room({ xalign="right", yalign="bottom", + contents = function(rm) + des.room({ contents = function(rm) + des.door({ state= "random", wall = "random" }); + end }); + end + }); des.room({ contents = function(rm) des.object(); des.monster(); + des.trap(); des.terrain(0,0, "L"); + des.altar({ coord = {0,0} }); des.terrain(rm.width, rm.height, "T"); end }); + des.room({ type="ordinary", coord={3, 3}, w=3, h=3 }); + des.room(); des.random_corridors(); end @@ -494,6 +551,36 @@ function test_corridor() des.corridor({ srcroom=0, srcwall="south", srcdoor=0, destroom=1, destwall="north", destdoor=0 }); end +function test_gas_cloud() + des.gas_cloud({ x = 5, y = 5 }); + des.gas_cloud({ coord = {10,10} }); + des.gas_cloud({ selection=selection.area(15,5, 17,7), damage = 5 }); + des.gas_cloud({ selection=selection.area(20,5, 22,7), damage = 5, ttl = 10 }); +end + +function test_exclusion() + des.exclusion({ type = "teleport", region = { 0,0, 10,5 } }); + des.exclusion({ type = "teleport-up", region = { 0,0, 10,5 } }); + des.exclusion({ type = "teleport-down", region = { 0,0, 10,5 } }); + des.exclusion({ type = "monster-generation", region = { 0,0, 10,5 } }); +end + +function test_levregion() + des.levregion({ type="stair-up", region={01,00,79,20}, region_islev=1, exclude={0,0,28,12} }) + des.levregion({ type="stair-down", region={01,00,79,20}, region_islev=1, exclude={0,0,28,12} }) + des.levregion({ type="branch", region={01,00,79,20}, region_islev=1, exclude={0,0,28,12} }) + des.levregion({ region={25,11,25,11}, type="portal", name="fakewiz1" }); +end + +function test_drawbridge() + des.terrain(05,08, "L"); + des.terrain(06,08, "|"); + des.drawbridge({ dir="east", state="closed", x=05,y=08 }); + des.terrain(10,08, "L"); + des.terrain(10,09, "-"); + des.drawbridge({ dir="south", state="random", coord={10,08} }) +end + function run_tests() test_level_init(); test_message(); @@ -520,10 +607,14 @@ function run_tests() test_terrain(); test_replace_terrain(); test_corridor(); + test_gas_cloud(); + test_exclusion(); + test_levregion(); + test_drawbridge(); des.reset_level(); des.level_init(); end -nh.debug_flags({mongen = false, hunger = false, overwrite_stairs = true }); +nh.debug_flags({ hunger = false, overwrite_stairs = true }); run_tests(); diff --git a/test/test_obj.lua b/test/test_obj.lua index d56a5ba55..1baad9832 100644 --- a/test/test_obj.lua +++ b/test/test_obj.lua @@ -82,3 +82,65 @@ box:addcontent(o5); local o6 = obj.new("statue"); o6:addcontent(obj.new("spellbook")); + + +-- generate one of each object, check the name and class matches +for i = nhc.FIRST_OBJECT, nhc.LAST_OBJECT do + local oid, oclass = nh.int_to_objname(i); + if (oid ~= "") then + local oi = obj.new({ id = oid, class = oclass }); + local oi_t = oi:totable(); + + if (oi_t.otyp_name ~= oid) then + error("object name \"" .. oi_t.otyp_name .. "\" created, wanted \"" .. oid .. "\""); + end + if (oi_t.oclass ~= oclass) then + local str = string.format("object class \"%s\" created, wanted \"%s\" (%s)", oi_t.oclass, oclass, oid); + error(str); + end + + end +end + + +function test_use_item(action, itemname, otherkeys) + nh.debug_flags({ prevent_pline = true }); + u.clear_inventory(); + u.giveobj(obj.new(itemname)); + local o = u.inventory; + local ot = o:totable(); + + nh.pushkey(action); + nh.pushkey(ot.invlet); + + if (otherkeys ~= nil and type(otherkeys) == "string") then + nh.pushkey(otherkeys); + end + + nh.doturn(); + nh.debug_flags({ prevent_pline = false }); +end + +nh.parse_config("OPTIONS=number_pad:0"); +nh.parse_config("OPTIONS=!timed_delay"); + +-- apply +test_use_item("a", "uncursed tin whistle"); +test_use_item("a", "cursed tin whistle"); +test_use_item("a", "blessed magic whistle"); + +test_use_item("a", "uncursed camera", "h"); +test_use_item("a", "uncursed camera", "j"); +test_use_item("a", "uncursed camera", "k"); +test_use_item("a", "blessed camera", ">"); +test_use_item("a", "+0 blessed camera", ">"); + +test_use_item("a", "blessed stethoscope", "h"); +test_use_item("a", "blessed stethoscope", "j"); +test_use_item("a", "blessed stethoscope", "."); +test_use_item("a", "blessed stethoscope", ">"); +test_use_item("a", "blessed stethoscope", "<"); +obj.new("corpse"):placeobj(u.ux, u.uy); +test_use_item("a", "blessed stethoscope", ">"); +obj.new("statue"):placeobj(u.ux, u.uy); +test_use_item("a", "blessed stethoscope", ">"); diff --git a/test/test_sel.lua b/test/test_sel.lua index e96b9be71..c5739e8fd 100644 --- a/test/test_sel.lua +++ b/test/test_sel.lua @@ -523,6 +523,40 @@ function test_sel_numpoints() end end +function test_sel_room() + des.reset_level(); + des.level_init({ style = "solidfill", fg = " " }); + des.room({ w = 5, h = 3, + contents = function(rm) + local sel = selection.room(); + sel_has_n_points(sel, 5*3, __func__); + end + }); +end + +function test_sel_gradient() + local sela = selection.gradient({ type = "radial", x = 3, y = 5, x2 = 10, y2 = 12, mindist = 4, maxdist = 10}); + local selb = selection.gradient({ type = "square", x = 3, y = 5, x2 = 10, y2 = 12, mindist = 4, maxdist = 10}); + local selc = selection.gradient({ x = 3, y = 5, x2 = 10, y2 = 12, maxdist = 10}); +end + +function test_sel_describe_size() + local sela = selection.fillrect(1,1, 3,3); + if (sela:describe_size() ~= "square 3 by 3") then + error("selection.describe_size returned \"" .. sela:describe_size() "\", not \"square 3 by 3\""); + end + + local selb = selection.fillrect(1,1, 4,3); + if (selb:describe_size() ~= "rectangular 4 by 3") then + error("selection.describe_size returned \"" .. selb:describe_size() "\", not \"rectangular 4 by 3\""); + end + + sela:set(1,1, 0); + if (sela:describe_size() ~= "irregularly shaped 3 by 3") then + error("selection.describe_size returned \"" .. sela:describe_size() "\", not \"irregularly shaped 3 by 3\""); + end +end + nh.debug_flags({mongen = false, hunger = false, overwrite_stairs = true }); test_selection_params(); test_sel_negate(); @@ -544,3 +578,6 @@ test_sel_iterate(); test_sel_bounds(); test_sel_map(); test_sel_numpoints(); +test_sel_room(); +test_sel_gradient(); +test_sel_describe_size(); diff --git a/test/test_src.lua b/test/test_src.lua index 2ae45d0ac..ac3500aa3 100644 --- a/test/test_src.lua +++ b/test/test_src.lua @@ -121,3 +121,22 @@ for func, fval in pairs(tests) do end end end + +function test_getlin() + nh.pushkey("AbC"); + local str = nh.getlin("What?"); + if str ~= "AbC" then + error("nh.getlin fail, got \"" .. str .. "\""); + end +end + +function test_abscoord() + local ax,ay = nh.abscoord(3, 8); + local pt = nh.abscoord({ x = 10, y = 5 }); +end + +test_getlin(); +test_abscoord(); + +nh.flip_level(3); + diff --git a/test/testmove.lua b/test/testmove.lua index da1e0ab83..8ded195d5 100644 --- a/test/testmove.lua +++ b/test/testmove.lua @@ -3,14 +3,14 @@ nh.parse_config("OPTIONS=number_pad:0"); nh.parse_config("OPTIONS=runmode:teleport"); +nh.parse_config("OPTIONS=!timed_delay"); local POS = { x = 10, y = 05 }; local number_pad = 0; function initlev() - nh.debug_flags({mongen = false, hunger = false, overwrite_stairs = true }); - des.level_flags("noflip"); des.reset_level(); + des.level_flags("noflip"); des.level_init({ style = "solidfill", fg = ".", lit = true }); des.teleport_region({ region = {POS.x,POS.y,POS.x,POS.y}, region_islev = true, dir="both" }); des.finalize_level(); @@ -140,6 +140,7 @@ local basicmoves = { }; +nh.debug_flags({mongen = false, hunger = false, overwrite_stairs = true, disable_pline = true }); for k, v in pairs(basicmoves) do initlev(); @@ -179,3 +180,4 @@ for k, v in pairs(basicmoves) do end initlev(); +nh.debug_flags({ disable_pline = false }); diff --git a/util/.gitignore b/util/.gitignore index a16d5c640..3e92fc8a5 100644 --- a/util/.gitignore +++ b/util/.gitignore @@ -35,3 +35,4 @@ sf.tags sfdata.c sfctool sftags +nhlua diff --git a/util/makedefs.c b/util/makedefs.c index bc86614bd..53c90c151 100644 --- a/util/makedefs.c +++ b/util/makedefs.c @@ -152,7 +152,6 @@ void do_oracles(void); void do_date(void); void do_dungeon(void); void do_options(void); -void do_monstr(void); void do_objs(void); void do_permonst(void); void do_questtxt(void); diff --git a/util/sfctool.c b/util/sfctool.c index 8bfc3ccde..9c9423861 100644 --- a/util/sfctool.c +++ b/util/sfctool.c @@ -96,7 +96,7 @@ int util_strncmpi(const char *s1, const char *s2, size_t sz); #ifdef UNIX #define nethack_exit exit ATTRNORETURN void nh_terminate(int) NORETURN; /* bwrite() calls this */ -static void chdirx(const char *); +//static void chdirx(const char *, boolean); #else ATTRNORETURN extern void nethack_exit(int) NORETURN; #ifdef WIN32 @@ -184,7 +184,7 @@ main(int argc, char *argv[]) folderbuf[1] = '/'; folderbuf[2] = '\0'; #ifdef CHDIR - chdirx(HACKDIR); + chdirx(HACKDIR, FALSE); #endif #endif #ifdef UNIX @@ -809,8 +809,8 @@ nethack_exit(int code) #ifdef UNIX #ifdef CHDIR -static void -chdirx(const char *dir) +void +chdirx(const char *dir, boolean wr UNUSED) { if (dir) { #ifdef SECURE diff --git a/util/sftags.c b/util/sftags.c index 39bfe24f9..9d204cdb3 100644 --- a/util/sftags.c +++ b/util/sftags.c @@ -548,8 +548,8 @@ static void parseExtensionFields (struct tagstruct *tmptag, char *buf) if (colon == (char *)0) { tmptag->tagtype = *field; } else { - const char *key = field; - const char *value = colon + 1; + char *key = field; + char *value = colon + 1; *colon = '\0'; if ((strcmp (key, "struct") == 0) || (strcmp (key, "union") == 0)) { @@ -971,7 +971,7 @@ static void output_types(FILE *fp1) for (k = 0; k < SIZE(readtagstypes); ++k) { if (readtagstypes[k].dtclass == NHTYPE_SIMPLE) { - Fprintf(fp1,"\t{NHTYPE_SIMPLE, (char *) \"%s\", sizeof(%s)},\n", + Fprintf(fp1," {NHTYPE_SIMPLE, (char *) \"%s\", sizeof(%s)},\n", readtagstypes[k].dtype, (strncmpi(readtagstypes[k].dtype, "Bitfield", 8) == 0) ? "uint8_t" : @@ -981,10 +981,10 @@ static void output_types(FILE *fp1) "anything" : readtagstypes[k].dtype); /* dtmacro(readtagstypes[k].dtype,0)); */ #if 0 - Fprintf(fp2, "#define %s\t%s%d\n", dtmacro(readtagstypes[k].dtype,1), + Fprintf(fp2, "#define %s %s%d\n", dtmacro(readtagstypes[k].dtype,1), (strlen(readtagstypes[k].dtype) > 12) ? "" : - (strlen(readtagstypes[k].dtype) < 5) ? "\t\t" : - "\t", hcnt++); + (strlen(readtagstypes[k].dtype) < 5) ? " " : + " ", hcnt++); #endif } } @@ -997,7 +997,7 @@ static void output_types(FILE *fp1) } if (cnt > 0) Fprintf(fp1, "%s", ",\n"); - Fprintf(fp1, "\t{NHTYPE_COMPLEX, (char *) \"%s\", sizeof(%s %s)}", + Fprintf(fp1, " {NHTYPE_COMPLEX, (char *) \"%s\", sizeof(%s %s)}", t->tag, (t->tagtype == 's') ? "struct" : "union", t->tag); cnt += 1; @@ -1005,7 +1005,7 @@ static void output_types(FILE *fp1) t = t->next; } Fprintf(fp1, "%s", "\n};\n\n"); - Fprintf(fp1, "int nhdatatypes_size(void)\n{\n\treturn SIZE(nhdatatypes);\n}\n\n"); + Fprintf(fp1, "int nhdatatypes_size(void)\n{\n return SIZE(nhdatatypes);\n}\n\n"); } static void generate_c_files(void) @@ -1357,7 +1357,7 @@ static void generate_c_files(void) "d_%s->%s = bitfield;\n\n", readtagstypes[k].dtype, t->tag); Fprintf(SFDATATMP, - "\t\"%s:%s:%s\",\n", + " \"%s:%s:%s\",\n", sfparent, t->tag, ft); } else { /**************** not a bitfield ****************/ @@ -1571,7 +1571,7 @@ static void generate_c_files(void) strcmp(altbuf, "char") != 0 ? "" : arrbuf); Fprintf(SFI_DATA, "%s", lbuf); Fprintf(SFDATATMP, - "\t\"%s:%s:%s\",\n", + " \"%s:%s:%s\",\n", sfparent, t->tag,fieldfix(ft,ssdef)); kludge_sbrooms = FALSE; array_of_ptrs = FALSE; @@ -1604,7 +1604,7 @@ static void generate_c_files(void) } Fprintf(SFDATATMP,"};\n\n"); - Fprintf(SFDATATMP, "int critical_members_count(void)\n{\n\treturn SIZE(critical_members);\n}\n\n"); + Fprintf(SFDATATMP, "int critical_members_count(void)\n{\n return SIZE(critical_members);\n}\n\n"); fclose(SFO_DATA); fclose(SFI_DATA); @@ -1724,7 +1724,7 @@ dtmacro(const char *str, } else if (strncmpi(c, "const ", 6) == 0) { c = buf + 6; } else if ((strncmpi(c, "struct ", 7) == 0) || - (strncmpi(c, "struct\t", 7) == 0)) { + (strncmpi(c, "struct ", 7) == 0)) { c = buf + 7; } else if (strncmpi(c, "union ", 6) == 0) { c = buf + 6; @@ -1785,7 +1785,7 @@ dtfn(const char *str, } else if (strncmpi(c, "const ", 6) == 0) { c = buf + 6; } else if ((strncmpi(c, "struct ", 7) == 0) || - (strncmpi(c, "struct\t", 7) == 0)) { + (strncmpi(c, "struct ", 7) == 0)) { c = buf + 7; } else if (strncmpi(c, "union ", 6) == 0) { c = buf + 6; @@ -1844,14 +1844,15 @@ static char * bfsize(const char *str) { static char buf[128]; - const char *c1; - char *c2, *subst; + char *copy_str, *c1, *c2, *subst; + char *retval = buf; if (!str) return (char *)0; + copy_str = dupstr(str); /* kludge */ - subst = strstr(str, ",$/"); + subst = strstr(copy_str, ",$/"); if (subst != 0) { subst++; *subst++ = ' '; @@ -1859,7 +1860,7 @@ bfsize(const char *str) } c2 = buf; - c1 = str; + c1 = copy_str; while (*c1) { if (*c1 == ',') break; @@ -1873,9 +1874,10 @@ bfsize(const char *str) } *c2 = '\0'; } else { - return (char *)0; + retval = (char *) 0; } - return buf; + free((genericptr_t) copy_str); + return retval; } /* Read one line from input, up to and including the next newline diff --git a/win/Qt/qt_bind.cpp b/win/Qt/qt_bind.cpp index 58dc96476..fd55061e2 100644 --- a/win/Qt/qt_bind.cpp +++ b/win/Qt/qt_bind.cpp @@ -502,7 +502,7 @@ void NetHackQtBind::qt_update_inventory(int arg UNUSED) /* doesn't work yet if (program_state.something_worth_saving && iflags.perm_invent) - display_inventory(NULL, false); + repopulate_perminvent(); */ } diff --git a/win/X11/NetHack.ad b/win/X11/NetHack.ad index 4c5f9b191..c10d1360f 100644 --- a/win/X11/NetHack.ad +++ b/win/X11/NetHack.ad @@ -117,7 +117,7 @@ NetHack*message*translations: : input() ! Specify the number of rows and columns of the map window. The default ! is the standard 80x21 window. Note: this _does_not_ change the size of -! map levels, only what you see of them. +! level maps, only what you see of them. !NetHack*map*rows: 21 !NetHack*map*columns: 80 diff --git a/win/X11/winX.c b/win/X11/winX.c index 1021c6ce3..f74408269 100644 --- a/win/X11/winX.c +++ b/win/X11/winX.c @@ -1295,7 +1295,7 @@ X11_update_inventory(int arg) if (program_state.in_moveloop || program_state.gameover) { updated_inventory = 1; /* hack to avoid mapping&raising window */ if (!arg) { - (void) display_inventory((char *) 0, FALSE); + repopulate_perminvent(); } else { x11_scroll_perminv(arg); } diff --git a/win/curses/cursinvt.c b/win/curses/cursinvt.c index 4095ceb5a..1aadad9f9 100644 --- a/win/curses/cursinvt.c +++ b/win/curses/cursinvt.c @@ -96,7 +96,7 @@ curs_update_invt(int arg) /* ask core to display full inventory in a PICK_NONE menu; instead of setting up an ordinary menu, it will indirectly call curs_add_invt() for each line (including class headers) */ - display_inventory(NULL, FALSE); + repopulate_perminvent(); curs_invt_updated(win); } else { diff --git a/win/curses/curswins.c b/win/curses/curswins.c index 9fe04edd4..52e21db37 100644 --- a/win/curses/curswins.c +++ b/win/curses/curswins.c @@ -224,6 +224,10 @@ curses_destroy_win(WINDOW *win) delwin(win); if (win == activemenu) activemenu = NULL; + /* during shutdown, RIP window could still be active after mapwin goes + away; so, avoid 'if (mapwin)' above when deleting RIP window later */ + if (win == mapwin) + win = mapwin = NULL; curses_refresh_nethack_windows(); nhUse(dummyht); } diff --git a/win/share/objects.txt b/win/share/objects.txt index 9c1a3e736..53ebdcf13 100644 --- a/win/share/objects.txt +++ b/win/share/objects.txt @@ -2938,7 +2938,7 @@ Z = (195, 195, 195) .....PPPPPAA.... .......PPAA..... } -# tile 152 (small shield) +# tile 152 (wooden shield / small shield) { ................ ................ @@ -2957,7 +2957,45 @@ Z = (195, 195, 195) ................ ................ } -# tile 153 (blue and green shield / elven shield) +# tile 153 (wooden shield / shield of drain resistance) +{ + ................ + ................ + ................ + ................ + ................ + ...C.CJJ.J...... + ...CCKKJJJA..... + ...CKKKJJJA..... + ...CKKJJJJA..... + ...CKKJJJJA..... + ....CKKJJAA..... + .....CKJAA...... + ......CAA....... + .......A........ + ................ + ................ +} +# tile 154 (wooden shield / shield of shock resistance) +{ + ................ + ................ + ................ + ................ + ................ + ...C.CJJ.J...... + ...CCKKJJJA..... + ...CKKKJJJA..... + ...CKKJJJJA..... + ...CKKJJJJA..... + ....CKKJJAA..... + .....CKJAA...... + ......CAA....... + .......A........ + ................ + ................ +} +# tile 155 (blue and green shield / elven shield) { ................ ................ @@ -2976,7 +3014,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 154 (white-handed shield / Uruk-hai shield) +# tile 156 (white-handed shield / Uruk-hai shield) { ................ ...K.KKKJJJ.J... @@ -2995,7 +3033,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 155 (red-eyed shield / orcish shield) +# tile 157 (red-eyed shield / orcish shield) { ................ ................ @@ -3014,7 +3052,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 156 (large shield) +# tile 158 (large shield) { ................ ...N.NNNOOO.O... @@ -3033,7 +3071,7 @@ Z = (195, 195, 195) ........AA...... ................ } -# tile 157 (large round shield / dwarvish roundshield) +# tile 159 (large round shield / dwarvish roundshield) { ................ ................ @@ -3052,7 +3090,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 158 (polished silver shield / shield of reflection) +# tile 160 (polished silver shield / shield of reflection) { ................ ................ @@ -3071,7 +3109,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 159 (old gloves / leather gloves) +# tile 161 (old gloves / leather gloves) { ................ ................ @@ -3090,7 +3128,7 @@ Z = (195, 195, 195) .........AAA.... ................ } -# tile 160 (padded gloves / gauntlets of fumbling) +# tile 162 (padded gloves / gauntlets of fumbling) { ................ ................ @@ -3109,7 +3147,7 @@ Z = (195, 195, 195) .........AAA.... ................ } -# tile 161 (riding gloves / gauntlets of power) +# tile 163 (riding gloves / gauntlets of power) { ................ ................ @@ -3128,7 +3166,7 @@ Z = (195, 195, 195) .........AAA.... ................ } -# tile 162 (fencing gloves / gauntlets of dexterity) +# tile 164 (fencing gloves / gauntlets of dexterity) { ................ ................ @@ -3147,7 +3185,7 @@ Z = (195, 195, 195) .........AAA.... ................ } -# tile 163 (walking shoes / low boots) +# tile 165 (walking shoes / low boots) { ................ ................ @@ -3166,7 +3204,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 164 (hard shoes / iron shoes) +# tile 166 (hard shoes / iron shoes) { ................ ................ @@ -3185,7 +3223,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 165 (jackboots / high boots) +# tile 167 (jackboots / high boots) { .......CCKKKK... ......CKAAAAJJ.. @@ -3204,7 +3242,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 166 (combat boots / speed boots) +# tile 168 (combat boots / speed boots) { ................ ................ @@ -3223,7 +3261,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 167 (jungle boots / water walking boots) +# tile 169 (jungle boots / water walking boots) { ................ ................ @@ -3242,7 +3280,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 168 (hiking boots / jumping boots) +# tile 170 (hiking boots / jumping boots) { ................ ................ @@ -3261,7 +3299,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 169 (mud boots / elven boots) +# tile 171 (mud boots / elven boots) { ................ ................ @@ -3280,7 +3318,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 170 (buckled boots / kicking boots) +# tile 172 (buckled boots / kicking boots) { ................ ................ @@ -3299,7 +3337,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 171 (riding boots / fumble boots) +# tile 173 (riding boots / fumble boots) { ................ ................ @@ -3318,7 +3356,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 172 (snow boots / levitation boots) +# tile 174 (snow boots / levitation boots) { ................ ................ @@ -3337,7 +3375,7 @@ Z = (195, 195, 195) ...A.A.A.A.A.A.. ................ } -# tile 173 (wooden / adornment) +# tile 175 (wooden / adornment) { ................ ................ @@ -3356,7 +3394,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 174 (granite / gain strength) +# tile 176 (granite / gain strength) { ................ ................ @@ -3375,7 +3413,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 175 (opal / gain constitution) +# tile 177 (opal / gain constitution) { ................ ................ @@ -3394,7 +3432,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 176 (clay / increase accuracy) +# tile 178 (clay / increase accuracy) { ................ ................ @@ -3413,7 +3451,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 177 (coral / increase damage) +# tile 179 (coral / increase damage) { ................ ................ @@ -3432,7 +3470,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 178 (black onyx / protection) +# tile 180 (black onyx / protection) { ................ ................ @@ -3451,7 +3489,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 179 (moonstone / regeneration) +# tile 181 (moonstone / regeneration) { ................ ................ @@ -3470,7 +3508,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 180 (tiger eye / searching) +# tile 182 (tiger eye / searching) { ................ ................ @@ -3489,7 +3527,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 181 (jade / stealth) +# tile 183 (jade / stealth) { ................ ................ @@ -3508,7 +3546,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 182 (bronze / sustain ability) +# tile 184 (bronze / sustain ability) { ................ ................ @@ -3527,7 +3565,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 183 (agate / levitation) +# tile 185 (agate / levitation) { ................ ................ @@ -3546,7 +3584,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 184 (topaz / hunger) +# tile 186 (topaz / hunger) { ................ ................ @@ -3565,7 +3603,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 185 (sapphire / aggravate monster) +# tile 187 (sapphire / aggravate monster) { ................ ................ @@ -3584,7 +3622,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 186 (ruby / conflict) +# tile 188 (ruby / conflict) { ................ ................ @@ -3603,7 +3641,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 187 (diamond / warning) +# tile 189 (diamond / warning) { ................ ................ @@ -3622,7 +3660,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 188 (pearl / poison resistance) +# tile 190 (pearl / poison resistance) { ................ ................ @@ -3641,7 +3679,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 189 (iron / fire resistance) +# tile 191 (iron / fire resistance) { ................ ................ @@ -3660,7 +3698,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 190 (brass / cold resistance) +# tile 192 (brass / cold resistance) { ................ ................ @@ -3679,7 +3717,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 191 (copper / shock resistance) +# tile 193 (copper / shock resistance) { ................ ................ @@ -3698,7 +3736,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 192 (twisted / free action) +# tile 194 (twisted / free action) { ................ ................ @@ -3717,7 +3755,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 193 (steel / slow digestion) +# tile 195 (steel / slow digestion) { ................ ................ @@ -3736,7 +3774,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 194 (silver / teleportation) +# tile 196 (silver / teleportation) { ................ ................ @@ -3755,7 +3793,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 195 (gold / teleport control) +# tile 197 (gold / teleport control) { ................ ................ @@ -3774,7 +3812,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 196 (ivory / polymorph) +# tile 198 (ivory / polymorph) { ................ ................ @@ -3793,7 +3831,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 197 (emerald / polymorph control) +# tile 199 (emerald / polymorph control) { ................ ................ @@ -3812,7 +3850,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 198 (wire / invisibility) +# tile 200 (wire / invisibility) { ................ ................ @@ -3831,7 +3869,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 199 (engagement / see invisible) +# tile 201 (engagement / see invisible) { ................ ................ @@ -3850,7 +3888,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 200 (shiny / protection from shape changers) +# tile 202 (shiny / protection from shape changers) { ................ ................ @@ -3869,7 +3907,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 201 (circular / amulet of ESP) +# tile 203 (circular / amulet of ESP) { ................ ......LLLLLAA... @@ -3888,7 +3926,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 202 (spherical / amulet of life saving) +# tile 204 (spherical / amulet of life saving) { ................ ......LLLLLAA... @@ -3907,7 +3945,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 203 (oval / amulet of strangulation) +# tile 205 (oval / amulet of strangulation) { ................ ......LLLLLLAA.. @@ -3926,7 +3964,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 204 (triangular / amulet of restful sleep) +# tile 206 (triangular / amulet of restful sleep) { ................ ......LLLLLAA... @@ -3945,7 +3983,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 205 (pyramidal / amulet versus poison) +# tile 207 (pyramidal / amulet versus poison) { ................ ......LLLLLAA... @@ -3964,7 +4002,7 @@ Z = (195, 195, 195) ....KJJJJJJJJA.. .......AAAAAAA.. } -# tile 206 (square / amulet of change) +# tile 208 (square / amulet of change) { ................ ......LLLLLAA... @@ -3983,7 +4021,7 @@ Z = (195, 195, 195) .......AAAAA.... ................ } -# tile 207 (concave / amulet of unchanging) +# tile 209 (concave / amulet of unchanging) { ................ ......LLLLLAA... @@ -4002,7 +4040,7 @@ Z = (195, 195, 195) .......KCCAA.... ........AAA..... } -# tile 208 (hexagonal / amulet of reflection) +# tile 210 (hexagonal / amulet of reflection) { ................ ......LLLLLAA... @@ -4021,7 +4059,7 @@ Z = (195, 195, 195) ........AAA..... ................ } -# tile 209 (octagonal / amulet of magical breathing) +# tile 211 (octagonal / amulet of magical breathing) { ................ ......LLLLLAA... @@ -4040,7 +4078,7 @@ Z = (195, 195, 195) .......KKKAA.... ........AAA..... } -# tile 210 (perforated / amulet of guarding) +# tile 212 (perforated / amulet of guarding) { ................ ......LLLLLAA... @@ -4059,7 +4097,7 @@ Z = (195, 195, 195) .......KKKAA.... ........AAA..... } -# tile 211 (cubical / amulet of flying) +# tile 213 (cubical / amulet of flying) { ................ ......LLLLLAA... @@ -4078,7 +4116,7 @@ Z = (195, 195, 195) ......CKKKKA.... .......AAAAA.... } -# tile 212 (Amulet of Yendor / cheap plastic imitation of the Amulet of Yendor) +# tile 214 (Amulet of Yendor / cheap plastic imitation of the Amulet of Yendor) { ................ ......HHHHHAA... @@ -4097,7 +4135,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 213 (Amulet of Yendor / Amulet of Yendor) +# tile 215 (Amulet of Yendor / Amulet of Yendor) { ................ ......HHHHHAA... @@ -4116,7 +4154,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 214 (large box) +# tile 216 (large box) { ................ ................ @@ -4135,7 +4173,7 @@ Z = (195, 195, 195) CKKKKKKKKKKJAA.. .AAAAAAAAAAAA... } -# tile 215 (chest) +# tile 217 (chest) { ................ ................ @@ -4154,7 +4192,7 @@ Z = (195, 195, 195) CKKKKKKKKKKJAA.. .AAAAAAAAAAAA... } -# tile 216 (ice box) +# tile 218 (ice box) { ................ ................ @@ -4173,7 +4211,7 @@ Z = (195, 195, 195) NBBBBBBBBBBPAA.. .AAAAAAAAAAAA... } -# tile 217 (bag / sack) +# tile 219 (bag / sack) { ................ ................ @@ -4192,7 +4230,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 218 (bag / oilskin sack) +# tile 220 (bag / oilskin sack) { ................ ................ @@ -4211,7 +4249,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 219 (bag / bag of holding) +# tile 221 (bag / bag of holding) { ................ ................ @@ -4230,7 +4268,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 220 (bag / bag of tricks) +# tile 222 (bag / bag of tricks) { ................ ................ @@ -4249,7 +4287,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 221 (key / skeleton key) +# tile 223 (key / skeleton key) { ................ ................ @@ -4268,7 +4306,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 222 (lock pick) +# tile 224 (lock pick) { ................ ................ @@ -4287,7 +4325,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 223 (credit card) +# tile 225 (credit card) { ................ ................ @@ -4306,7 +4344,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 224 (candle / tallow candle) +# tile 226 (candle / tallow candle) { ................ ................ @@ -4325,7 +4363,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 225 (candle / wax candle) +# tile 227 (candle / wax candle) { ................ ................ @@ -4344,7 +4382,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 226 (brass lantern) +# tile 228 (brass lantern) { ................ ................ @@ -4363,7 +4401,7 @@ Z = (195, 195, 195) .....AAAAAAA.... ................ } -# tile 227 (lamp / oil lamp) +# tile 229 (lamp / oil lamp) { ................ ................ @@ -4382,7 +4420,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 228 (lamp / magic lamp) +# tile 230 (lamp / magic lamp) { ................ ................ @@ -4401,7 +4439,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 229 (expensive camera) +# tile 231 (expensive camera) { ................ ................ @@ -4420,7 +4458,7 @@ Z = (195, 195, 195) ...PPPPPPPPPPPP. ................ } -# tile 230 (looking glass / mirror) +# tile 232 (looking glass / mirror) { ................ ................ @@ -4439,7 +4477,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 231 (glass orb / crystal ball) +# tile 233 (glass orb / crystal ball) { ................ ................ @@ -4458,7 +4496,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 232 (lenses) +# tile 234 (lenses) { ................ ................ @@ -4477,7 +4515,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 233 (blindfold) +# tile 235 (blindfold) { ................ ................ @@ -4496,7 +4534,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 234 (towel) +# tile 236 (towel) { ................ ................ @@ -4515,7 +4553,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 235 (saddle) +# tile 237 (saddle) { ................ ................ @@ -4534,7 +4572,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 236 (leash) +# tile 238 (leash) { ................ ................ @@ -4553,7 +4591,7 @@ Z = (195, 195, 195) .....AAAA....... ................ } -# tile 237 (stethoscope) +# tile 239 (stethoscope) { ................ ................ @@ -4572,7 +4610,7 @@ Z = (195, 195, 195) ........AAA..... ................ } -# tile 238 (tinning kit) +# tile 240 (tinning kit) { ................ ................ @@ -4591,7 +4629,7 @@ Z = (195, 195, 195) ......AAA....... ................ } -# tile 239 (tin opener) +# tile 241 (tin opener) { ................ ................ @@ -4610,7 +4648,7 @@ Z = (195, 195, 195) ........AA...... ................ } -# tile 240 (can of grease) +# tile 242 (can of grease) { ................ ................ @@ -4629,7 +4667,7 @@ Z = (195, 195, 195) .......AAAA..... ................ } -# tile 241 (figurine) +# tile 243 (figurine) { ................ ................ @@ -4648,7 +4686,7 @@ Z = (195, 195, 195) .....JJJJJAA.... ................ } -# tile 242 (magic marker) +# tile 244 (magic marker) { ................ ................ @@ -4667,7 +4705,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 243 (land mine) +# tile 245 (land mine) { ................ ................ @@ -4686,7 +4724,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 244 (beartrap) +# tile 246 (beartrap) { ................ ................ @@ -4705,7 +4743,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 245 (whistle / tin whistle) +# tile 247 (whistle / tin whistle) { ................ ................ @@ -4724,7 +4762,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 246 (whistle / magic whistle) +# tile 248 (whistle / magic whistle) { ................ ................ @@ -4743,7 +4781,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 247 (flute / wooden flute) +# tile 249 (flute / wooden flute) { ................ ................ @@ -4762,7 +4800,7 @@ Z = (195, 195, 195) ..A............. ................ } -# tile 248 (flute / magic flute) +# tile 250 (flute / magic flute) { ................ ................ @@ -4781,7 +4819,7 @@ Z = (195, 195, 195) ..A............. ................ } -# tile 249 (horn / tooled horn) +# tile 251 (horn / tooled horn) { ................ ................ @@ -4800,7 +4838,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 250 (horn / frost horn) +# tile 252 (horn / frost horn) { ................ ................ @@ -4819,7 +4857,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 251 (horn / fire horn) +# tile 253 (horn / fire horn) { ................ ................ @@ -4838,7 +4876,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 252 (horn / horn of plenty) +# tile 254 (horn / horn of plenty) { ................ ................ @@ -4857,7 +4895,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 253 (harp / wooden harp) +# tile 255 (harp / wooden harp) { ................ ................ @@ -4876,7 +4914,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 254 (harp / magic harp) +# tile 256 (harp / magic harp) { ................ ................ @@ -4895,7 +4933,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 255 (bell) +# tile 257 (bell) { ................ .......KA....... @@ -4914,7 +4952,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 256 (bugle) +# tile 258 (bugle) { ................ ................ @@ -4933,7 +4971,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 257 (drum / leather drum) +# tile 259 (drum / leather drum) { ................ ................ @@ -4952,7 +4990,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 258 (drum / drum of earthquake) +# tile 260 (drum / drum of earthquake) { ................ ................ @@ -4971,7 +5009,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 259 (pick-axe) +# tile 261 (pick-axe) { ................ ................ @@ -4990,7 +5028,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 260 (grappling hook) +# tile 262 (grappling hook) { .............N.. ..............P. @@ -5009,7 +5047,7 @@ Z = (195, 195, 195) ..OOA..OOOA..... ....OOOAA....... } -# tile 261 (unicorn horn) +# tile 263 (unicorn horn) { ................ ................ @@ -5028,7 +5066,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 262 (candelabrum / Candelabrum of Invocation) +# tile 264 (candelabrum / Candelabrum of Invocation) { .......N........ .......D........ @@ -5047,7 +5085,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 263 (silver bell / Bell of Opening) +# tile 265 (silver bell / Bell of Opening) { ................ .......OA....... @@ -5066,7 +5104,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 264 (tripe ration) +# tile 266 (tripe ration) { ................ ................ @@ -5085,7 +5123,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 265 (corpse) +# tile 267 (corpse) { ................ .....D.DPLN..... @@ -5104,7 +5142,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 266 (egg) +# tile 268 (egg) { ................ ................ @@ -5123,7 +5161,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 267 (meatball) +# tile 269 (meatball) { ................ ................ @@ -5142,7 +5180,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 268 (meat stick) +# tile 270 (meat stick) { ................ ................ @@ -5161,7 +5199,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 269 (enormous meatball) +# tile 271 (enormous meatball) { ................ ................ @@ -5180,7 +5218,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 270 (meat ring) +# tile 272 (meat ring) { ................ ................ @@ -5199,7 +5237,7 @@ Z = (195, 195, 195) ......AAAAA..... ................ } -# tile 271 (glob of gray ooze) +# tile 273 (glob of gray ooze) { ................ ................ @@ -5218,7 +5256,7 @@ Z = (195, 195, 195) ...AAA.AAAAA.... ................ } -# tile 272 (glob of brown pudding) +# tile 274 (glob of brown pudding) { ................ ................ @@ -5237,7 +5275,7 @@ Z = (195, 195, 195) ...AAA.AAAAA.... ................ } -# tile 273 (glob of green slime) +# tile 275 (glob of green slime) { ................ ................ @@ -5256,7 +5294,7 @@ Z = (195, 195, 195) ...AAA.AAAAA.... ................ } -# tile 274 (glob of black pudding) +# tile 276 (glob of black pudding) { ................ ................ @@ -5275,7 +5313,7 @@ Z = (195, 195, 195) ...AAA.AAAAA.... ................ } -# tile 275 (kelp frond) +# tile 277 (kelp frond) { ....FA.......... ....FFA......... @@ -5294,7 +5332,7 @@ Z = (195, 195, 195) .....FFFFA...... ......FFFFA..... } -# tile 276 (eucalyptus leaf) +# tile 278 (eucalyptus leaf) { ................ ................ @@ -5313,7 +5351,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 277 (apple) +# tile 279 (apple) { ................ ................ @@ -5332,7 +5370,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 278 (orange) +# tile 280 (orange) { ................ ................ @@ -5351,7 +5389,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 279 (pear) +# tile 281 (pear) { ................ ................ @@ -5370,7 +5408,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 280 (melon) +# tile 282 (melon) { ................ ................ @@ -5389,7 +5427,7 @@ Z = (195, 195, 195) ......AAA....... ................ } -# tile 281 (banana) +# tile 283 (banana) { ................ ................ @@ -5408,7 +5446,7 @@ Z = (195, 195, 195) .....AAAAA...... ................ } -# tile 282 (carrot) +# tile 284 (carrot) { ................ ..........F..F.. @@ -5427,7 +5465,7 @@ Z = (195, 195, 195) ...A............ ................ } -# tile 283 (sprig of wolfsbane) +# tile 285 (sprig of wolfsbane) { ................ ................ @@ -5446,7 +5484,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 284 (clove of garlic) +# tile 286 (clove of garlic) { ................ ................ @@ -5465,7 +5503,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 285 (slime mold) +# tile 287 (slime mold) { ................ ................ @@ -5484,7 +5522,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 286 (lump of royal jelly) +# tile 288 (lump of royal jelly) { ................ ................ @@ -5503,7 +5541,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 287 (cream pie) +# tile 289 (cream pie) { ................ ................ @@ -5522,7 +5560,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 288 (candy bar) +# tile 290 (candy bar) { ................ ................ @@ -5541,7 +5579,7 @@ Z = (195, 195, 195) ....A........... ................ } -# tile 289 (fortune cookie) +# tile 291 (fortune cookie) { ................ ................ @@ -5560,7 +5598,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 290 (pancake) +# tile 292 (pancake) { ................ ................ @@ -5579,7 +5617,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 291 (lembas wafer) +# tile 293 (lembas wafer) { ................ ................ @@ -5598,7 +5636,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 292 (cram ration) +# tile 294 (cram ration) { ................ ...JKA.......... @@ -5617,7 +5655,7 @@ Z = (195, 195, 195) .....AAAAAA..... ................ } -# tile 293 (food ration) +# tile 295 (food ration) { ...JJA.......... ...BPA.......... @@ -5636,7 +5674,7 @@ Z = (195, 195, 195) ....KKKKKKKKKA.. .....AAAAAAAA... } -# tile 294 (K-ration) +# tile 296 (K-ration) { ................ ................ @@ -5655,7 +5693,7 @@ Z = (195, 195, 195) ....KKKKKKKKKA.. .....AAAAAAAA... } -# tile 295 (C-ration) +# tile 297 (C-ration) { ................ ................ @@ -5674,7 +5712,7 @@ Z = (195, 195, 195) ....KKKKKKKKKA.. .....AAAAAAAA... } -# tile 296 (tin) +# tile 298 (tin) { ................ ................ @@ -5693,7 +5731,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 297 (ruby / gain ability) +# tile 299 (ruby / gain ability) { ................ ................ @@ -5712,7 +5750,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 298 (pink / restore ability) +# tile 300 (pink / restore ability) { ................ ................ @@ -5731,7 +5769,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 299 (orange / confusion) +# tile 301 (orange / confusion) { ................ ................ @@ -5750,7 +5788,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 300 (yellow / blindness) +# tile 302 (yellow / blindness) { ................ ................ @@ -5769,7 +5807,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 301 (emerald / paralysis) +# tile 303 (emerald / paralysis) { ................ ................ @@ -5788,7 +5826,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 302 (dark green / speed) +# tile 304 (dark green / speed) { ................ ................ @@ -5807,7 +5845,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 303 (cyan / levitation) +# tile 305 (cyan / levitation) { ................ ................ @@ -5826,7 +5864,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 304 (sky blue / hallucination) +# tile 306 (sky blue / hallucination) { ................ ................ @@ -5845,7 +5883,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 305 (brilliant blue / invisibility) +# tile 307 (brilliant blue / invisibility) { ................ ................ @@ -5864,7 +5902,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 306 (magenta / see invisible) +# tile 308 (magenta / see invisible) { ................ ................ @@ -5883,7 +5921,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 307 (purple-red / healing) +# tile 309 (purple-red / healing) { ................ ................ @@ -5902,7 +5940,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 308 (puce / extra healing) +# tile 310 (puce / extra healing) { ................ ................ @@ -5921,7 +5959,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 309 (milky / gain level) +# tile 311 (milky / gain level) { ................ ................ @@ -5940,7 +5978,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 310 (swirly / enlightenment) +# tile 312 (swirly / enlightenment) { ................ ................ @@ -5959,7 +5997,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 311 (bubbly / monster detection) +# tile 313 (bubbly / monster detection) { ................ ................ @@ -5978,7 +6016,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 312 (smoky / object detection) +# tile 314 (smoky / object detection) { ................ ................ @@ -5997,7 +6035,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 313 (cloudy / gain energy) +# tile 315 (cloudy / gain energy) { ................ ................ @@ -6016,7 +6054,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 314 (effervescent / sleeping) +# tile 316 (effervescent / sleeping) { ................ ................ @@ -6035,7 +6073,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 315 (black / full healing) +# tile 317 (black / full healing) { ................ ................ @@ -6054,7 +6092,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 316 (golden / polymorph) +# tile 318 (golden / polymorph) { ................ ................ @@ -6073,7 +6111,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 317 (brown / booze) +# tile 319 (brown / booze) { ................ ................ @@ -6092,7 +6130,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 318 (fizzy / sickness) +# tile 320 (fizzy / sickness) { ................ ................ @@ -6111,7 +6149,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 319 (dark / fruit juice) +# tile 321 (dark / fruit juice) { ................ ................ @@ -6130,7 +6168,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 320 (white / acid) +# tile 322 (white / acid) { ................ ................ @@ -6149,7 +6187,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 321 (murky / oil) +# tile 323 (murky / oil) { ................ ................ @@ -6168,7 +6206,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 322 (clear / water) +# tile 324 (clear / water) { ................ ................ @@ -6187,7 +6225,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 323 (ZELGO MER / enchant armor) +# tile 325 (ZELGO MER / enchant armor) { ................ ................ @@ -6206,7 +6244,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 324 (JUYED AWK YACC / destroy armor) +# tile 326 (JUYED AWK YACC / destroy armor) { ................ ................ @@ -6225,7 +6263,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 325 (NR 9 / confuse monster) +# tile 327 (NR 9 / confuse monster) { ................ ................ @@ -6244,7 +6282,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 326 (XIXAXA XOXAXA XUXAXA / scare monster) +# tile 328 (XIXAXA XOXAXA XUXAXA / scare monster) { ................ ................ @@ -6263,7 +6301,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 327 (PRATYAVAYAH / remove curse) +# tile 329 (PRATYAVAYAH / remove curse) { ................ ................ @@ -6282,7 +6320,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 328 (DAIYEN FOOELS / enchant weapon) +# tile 330 (DAIYEN FOOELS / enchant weapon) { ................ ................ @@ -6301,7 +6339,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 329 (LEP GEX VEN ZEA / create monster) +# tile 331 (LEP GEX VEN ZEA / create monster) { ................ ................ @@ -6320,7 +6358,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 330 (PRIRUTSENIE / taming) +# tile 332 (PRIRUTSENIE / taming) { ................ ................ @@ -6339,7 +6377,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 331 (ELBIB YLOH / genocide) +# tile 333 (ELBIB YLOH / genocide) { ................ ................ @@ -6358,7 +6396,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 332 (VERR YED HORRE / light) +# tile 334 (VERR YED HORRE / light) { ................ ................ @@ -6377,7 +6415,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 333 (VENZAR BORGAVVE / teleportation) +# tile 335 (VENZAR BORGAVVE / teleportation) { ................ ................ @@ -6396,7 +6434,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 334 (THARR / gold detection) +# tile 336 (THARR / gold detection) { ................ ................ @@ -6415,7 +6453,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 335 (YUM YUM / food detection) +# tile 337 (YUM YUM / food detection) { ................ ................ @@ -6434,7 +6472,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 336 (KERNOD WEL / identify) +# tile 338 (KERNOD WEL / identify) { ................ ................ @@ -6453,7 +6491,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 337 (ELAM EBOW / magic mapping) +# tile 339 (ELAM EBOW / magic mapping) { ................ ................ @@ -6472,7 +6510,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 338 (DUAM XNAHT / amnesia) +# tile 340 (DUAM XNAHT / amnesia) { ................ ................ @@ -6491,7 +6529,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 339 (ANDOVA BEGARIN / fire) +# tile 341 (ANDOVA BEGARIN / fire) { ................ ................ @@ -6510,7 +6548,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 340 (KIRJE / earth) +# tile 342 (KIRJE / earth) { ................ ................ @@ -6529,7 +6567,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 341 (VE FORBRYDERNE / punishment) +# tile 343 (VE FORBRYDERNE / punishment) { ................ ................ @@ -6548,7 +6586,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 342 (HACKEM MUCHE / charging) +# tile 344 (HACKEM MUCHE / charging) { ................ ................ @@ -6567,7 +6605,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 343 (VELOX NEB / stinking cloud) +# tile 345 (VELOX NEB / stinking cloud) { ................ ................ @@ -6586,7 +6624,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 344 (FOOBIE BLETCH) +# tile 346 (FOOBIE BLETCH) { ................ ................ @@ -6605,7 +6643,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 345 (TEMOV) +# tile 347 (TEMOV) { ................ ................ @@ -6624,7 +6662,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 346 (GARVEN DEH) +# tile 348 (GARVEN DEH) { ................ ................ @@ -6643,7 +6681,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 347 (READ ME) +# tile 349 (READ ME) { ................ ................ @@ -6662,7 +6700,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 348 (ETAOIN SHRDLU) +# tile 350 (ETAOIN SHRDLU) { ................ ................ @@ -6681,7 +6719,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 349 (LOREM IPSUM) +# tile 351 (LOREM IPSUM) { ................ ................ @@ -6700,7 +6738,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 350 (FNORD) +# tile 352 (FNORD) { ................ ................ @@ -6719,7 +6757,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 351 (KO BATE) +# tile 353 (KO BATE) { ................ ................ @@ -6738,7 +6776,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 352 (ABRA KA DABRA) +# tile 354 (ABRA KA DABRA) { ................ ................ @@ -6757,7 +6795,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 353 (ASHPD SODALG) +# tile 355 (ASHPD SODALG) { ................ ................ @@ -6776,7 +6814,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 354 (ZLORFIK) +# tile 356 (ZLORFIK) { ................ ................ @@ -6795,7 +6833,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 355 (GNIK SISI VLE) +# tile 357 (GNIK SISI VLE) { ................ ................ @@ -6814,7 +6852,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 356 (HAPAX LEGOMENON) +# tile 358 (HAPAX LEGOMENON) { ................ ................ @@ -6833,7 +6871,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 357 (EIRIS SAZUN IDISI) +# tile 359 (EIRIS SAZUN IDISI) { ................ ................ @@ -6852,7 +6890,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 358 (PHOL ENDE WODAN) +# tile 360 (PHOL ENDE WODAN) { ................ ................ @@ -6871,7 +6909,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 359 (GHOTI) +# tile 361 (GHOTI) { ................ ................ @@ -6890,7 +6928,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 360 (MAPIRO MAHAMA DIROMAT) +# tile 362 (MAPIRO MAHAMA DIROMAT) { ................ ................ @@ -6909,7 +6947,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 361 (VAS CORP BET MANI) +# tile 363 (VAS CORP BET MANI) { ................ ................ @@ -6928,7 +6966,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 362 (XOR OTA) +# tile 364 (XOR OTA) { ................ ................ @@ -6947,7 +6985,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 363 (STRC PRST SKRZ KRK) +# tile 365 (STRC PRST SKRZ KRK) { ................ ................ @@ -6966,7 +7004,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 364 (stamped / mail) +# tile 366 (stamped / mail) { ................ ................ @@ -6985,7 +7023,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 365 (unlabeled / blank paper) +# tile 367 (unlabeled / blank paper) { ................ ................ @@ -7004,7 +7042,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 366 (parchment / dig) +# tile 368 (parchment / dig) { ................ ................ @@ -7023,7 +7061,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 367 (vellum / magic missile) +# tile 369 (vellum / magic missile) { ................ ................ @@ -7042,7 +7080,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 368 (ragged / fireball) +# tile 370 (ragged / fireball) { ................ ................ @@ -7061,7 +7099,7 @@ Z = (195, 195, 195) ......OOJJAA.... ................ } -# tile 369 (dog eared / cone of cold) +# tile 371 (dog eared / cone of cold) { ................ ................ @@ -7080,7 +7118,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 370 (mottled / sleep) +# tile 372 (mottled / sleep) { ................ ................ @@ -7099,7 +7137,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 371 (stained / finger of death) +# tile 373 (stained / finger of death) { ................ ................ @@ -7118,7 +7156,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 372 (cloth / light) +# tile 374 (cloth / light) { ................ ................ @@ -7137,7 +7175,7 @@ Z = (195, 195, 195) .......PPPAA.... ................ } -# tile 373 (leathery / detect monsters) +# tile 375 (leathery / detect monsters) { ................ ................ @@ -7156,7 +7194,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 374 (white / healing) +# tile 376 (white / healing) { ................ ................ @@ -7175,7 +7213,7 @@ Z = (195, 195, 195) .......PNNAA.... ................ } -# tile 375 (pink / knock) +# tile 377 (pink / knock) { ................ ................ @@ -7194,7 +7232,7 @@ Z = (195, 195, 195) .......IIIAA.... ................ } -# tile 376 (red / force bolt) +# tile 378 (red / force bolt) { ................ ................ @@ -7213,7 +7251,7 @@ Z = (195, 195, 195) .......DDDAA.... ................ } -# tile 377 (orange / confuse monster) +# tile 379 (orange / confuse monster) { ................ ................ @@ -7232,7 +7270,7 @@ Z = (195, 195, 195) .......CCCAA.... ................ } -# tile 378 (yellow / cure blindness) +# tile 380 (yellow / cure blindness) { ................ ................ @@ -7251,7 +7289,7 @@ Z = (195, 195, 195) .......HHHAA.... ................ } -# tile 379 (velvet / drain life) +# tile 381 (velvet / drain life) { ................ ................ @@ -7270,7 +7308,7 @@ Z = (195, 195, 195) .......EEEAA.... ................ } -# tile 380 (light green / slow monster) +# tile 382 (light green / slow monster) { ................ ................ @@ -7289,7 +7327,7 @@ Z = (195, 195, 195) .......GGGAA.... ................ } -# tile 381 (dark green / wizard lock) +# tile 383 (dark green / wizard lock) { ................ ................ @@ -7308,7 +7346,7 @@ Z = (195, 195, 195) .......FFFAA.... ................ } -# tile 382 (turquoise / create monster) +# tile 384 (turquoise / create monster) { ................ ................ @@ -7327,7 +7365,7 @@ Z = (195, 195, 195) .......FBBAA.... ................ } -# tile 383 (cyan / detect food) +# tile 385 (cyan / detect food) { ................ ................ @@ -7346,7 +7384,7 @@ Z = (195, 195, 195) .......BBBAA.... ................ } -# tile 384 (light blue / cause fear) +# tile 386 (light blue / cause fear) { ................ ................ @@ -7365,7 +7403,7 @@ Z = (195, 195, 195) .......BBBAA.... ................ } -# tile 385 (dark blue / clairvoyance) +# tile 387 (dark blue / clairvoyance) { ................ ................ @@ -7384,7 +7422,7 @@ Z = (195, 195, 195) .......EEEAA.... ................ } -# tile 386 (indigo / cure sickness) +# tile 388 (indigo / cure sickness) { ................ ................ @@ -7403,7 +7441,7 @@ Z = (195, 195, 195) .......EEEAA.... ................ } -# tile 387 (magenta / charm monster) +# tile 389 (magenta / charm monster) { ................ ................ @@ -7422,7 +7460,7 @@ Z = (195, 195, 195) .......IIIAA.... ................ } -# tile 388 (purple / haste self) +# tile 390 (purple / haste self) { ................ ................ @@ -7441,7 +7479,7 @@ Z = (195, 195, 195) .......IIIAA.... ................ } -# tile 389 (violet / detect unseen) +# tile 391 (violet / detect unseen) { ................ ................ @@ -7460,7 +7498,7 @@ Z = (195, 195, 195) .......IIIAA.... ................ } -# tile 390 (tan / levitation) +# tile 392 (tan / levitation) { ................ ................ @@ -7479,7 +7517,7 @@ Z = (195, 195, 195) .......KKKAA.... ................ } -# tile 391 (plaid / extra healing) +# tile 393 (plaid / extra healing) { ................ ................ @@ -7498,7 +7536,7 @@ Z = (195, 195, 195) .......EFDAA.... ................ } -# tile 392 (light brown / restore ability) +# tile 394 (light brown / restore ability) { ................ ................ @@ -7517,7 +7555,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 393 (dark brown / invisibility) +# tile 395 (dark brown / invisibility) { ................ ................ @@ -7536,7 +7574,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 394 (gray / detect treasure) +# tile 396 (gray / detect treasure) { ................ ................ @@ -7555,7 +7593,7 @@ Z = (195, 195, 195) .......PPPAA.... ................ } -# tile 395 (wrinkled / remove curse) +# tile 397 (wrinkled / remove curse) { ................ ................ @@ -7574,7 +7612,7 @@ Z = (195, 195, 195) ......JJKKAA.... ................ } -# tile 396 (dusty / magic mapping) +# tile 398 (dusty / magic mapping) { ................ ................ @@ -7593,7 +7631,7 @@ Z = (195, 195, 195) .KAKA..JJJAA.... ................ } -# tile 397 (bronze / identify) +# tile 399 (bronze / identify) { ................ ................ @@ -7612,7 +7650,7 @@ Z = (195, 195, 195) .......CCCAA.... ................ } -# tile 398 (copper / turn undead) +# tile 400 (copper / turn undead) { ................ ................ @@ -7631,7 +7669,7 @@ Z = (195, 195, 195) .......JCJAA.... ................ } -# tile 399 (silver / polymorph) +# tile 401 (silver / polymorph) { ................ ................ @@ -7650,7 +7688,7 @@ Z = (195, 195, 195) .......PPPAA.... ................ } -# tile 400 (gold / teleport away) +# tile 402 (gold / teleport away) { ................ ................ @@ -7669,7 +7707,7 @@ Z = (195, 195, 195) .......HHHAA.... ................ } -# tile 401 (glittering / create familiar) +# tile 403 (glittering / create familiar) { ................ ................ @@ -7688,7 +7726,7 @@ Z = (195, 195, 195) .......PPPAN.... .......N........ } -# tile 402 (shining / cancellation) +# tile 404 (shining / cancellation) { ....N........... .......N........ @@ -7707,7 +7745,7 @@ Z = (195, 195, 195) .......PPPAA.... ................ } -# tile 403 (dull / protection) +# tile 405 (dull / protection) { ................ ................ @@ -7726,7 +7764,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 404 (thin / jumping) +# tile 406 (thin / jumping) { ................ ................ @@ -7745,7 +7783,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 405 (thick / stone to flesh) +# tile 407 (thick / stone to flesh) { ................ ................ @@ -7764,7 +7802,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 406 (checkered / chain lightning) +# tile 408 (checkered / chain lightning) { ................ ................ @@ -7783,7 +7821,7 @@ Z = (195, 195, 195) .......AAA...... ................ } -# tile 406 (plain / blank paper) +# tile 409 (plain / blank paper) { ................ ................ @@ -7802,7 +7840,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 407 (paperback / novel) +# tile 410 (paperback / novel) { ................ ................ @@ -7821,7 +7859,7 @@ Z = (195, 195, 195) .......EEEAA.... ................ } -# tile 408 (papyrus / Book of the Dead) +# tile 411 (papyrus / Book of the Dead) { ................ ................ @@ -7840,7 +7878,7 @@ Z = (195, 195, 195) .......AAA...... ................ } -# tile 409 (glass / light) +# tile 412 (glass / light) { ................ ................ @@ -7859,7 +7897,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 410 (balsa / secret door detection) +# tile 413 (balsa / secret door detection) { ................ ................ @@ -7878,7 +7916,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 411 (crystal / enlightenment) +# tile 414 (crystal / enlightenment) { ................ ................ @@ -7897,7 +7935,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 412 (maple / create monster) +# tile 415 (maple / create monster) { ................ ................ @@ -7916,7 +7954,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 413 (pine / wishing) +# tile 416 (pine / wishing) { ................ ................ @@ -7935,7 +7973,26 @@ Z = (195, 195, 195) ................ ................ } -# tile 414 (oak / nothing) +# tile 417 (redwood / stasis) +{ + ................ + ................ + ................ + ...........NO... + ..........DDAA.. + .........DDAA... + ........DDAA.... + .......DDAA..... + ......DDAA...... + .....DDAA....... + ....DDAA........ + ...NOAA......... + ....AA.......... + ................ + ................ + ................ +} +# tile 418 (oak / nothing) { ................ ................ @@ -7954,7 +8011,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 415 (ebony / striking) +# tile 419 (ebony / striking) { ................ ................ @@ -7973,7 +8030,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 416 (marble / make invisible) +# tile 420 (marble / make invisible) { ................ ................ @@ -7992,7 +8049,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 417 (tin / slow monster) +# tile 421 (tin / slow monster) { ................ ................ @@ -8011,7 +8068,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 418 (brass / speed monster) +# tile 422 (brass / speed monster) { ................ ................ @@ -8030,7 +8087,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 419 (copper / undead turning) +# tile 423 (copper / undead turning) { ................ ................ @@ -8049,7 +8106,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 420 (silver / polymorph) +# tile 424 (silver / polymorph) { ................ ................ @@ -8068,7 +8125,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 421 (platinum / cancellation) +# tile 425 (platinum / cancellation) { ................ ................ @@ -8087,7 +8144,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 422 (iridium / teleportation) +# tile 426 (iridium / teleportation) { ................ ................ @@ -8106,7 +8163,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 423 (zinc / opening) +# tile 427 (zinc / opening) { ................ ................ @@ -8125,7 +8182,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 424 (aluminum / locking) +# tile 428 (aluminum / locking) { ................ ................ @@ -8144,7 +8201,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 425 (uranium / probing) +# tile 429 (uranium / probing) { ................ ................ @@ -8163,7 +8220,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 426 (iron / digging) +# tile 430 (iron / digging) { ................ ................ @@ -8182,7 +8239,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 427 (steel / magic missile) +# tile 431 (steel / magic missile) { ................ ................ @@ -8201,7 +8258,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 428 (hexagonal / fire) +# tile 432 (hexagonal / fire) { ................ ................ @@ -8220,7 +8277,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 429 (short / cold) +# tile 433 (short / cold) { ................ ................ @@ -8239,7 +8296,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 430 (runed / sleep) +# tile 434 (runed / sleep) { ................ ................ @@ -8258,7 +8315,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 431 (long / death) +# tile 435 (long / death) { ................ .............NO. @@ -8277,7 +8334,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 432 (curved / lightning) +# tile 436 (curved / lightning) { ................ .......NO....... @@ -8296,7 +8353,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 433 (forked) +# tile 437 (forked) { ................ ................ @@ -8315,7 +8372,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 434 (spiked) +# tile 438 (spiked) { ................ ................ @@ -8334,7 +8391,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 435 (jeweled) +# tile 439 (jeweled) { ................ ................ @@ -8353,7 +8410,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 436 (gold piece) +# tile 440 (gold piece) { ................ ................ @@ -8372,7 +8429,7 @@ Z = (195, 195, 195) .........HA..... ...........HA... } -# tile 437 (white / dilithium crystal) +# tile 441 (white / dilithium crystal) { ................ ................ @@ -8391,7 +8448,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 438 (white / diamond) +# tile 442 (white / diamond) { ................ ................ @@ -8410,7 +8467,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 439 (red / ruby) +# tile 443 (red / ruby) { ................ ................ @@ -8429,7 +8486,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 440 (orange / jacinth) +# tile 444 (orange / jacinth) { ................ ................ @@ -8448,7 +8505,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 441 (blue / sapphire) +# tile 445 (blue / sapphire) { ................ ................ @@ -8467,7 +8524,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 442 (black / black opal) +# tile 446 (black / black opal) { ................ ................ @@ -8486,7 +8543,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 443 (green / emerald) +# tile 447 (green / emerald) { ................ ................ @@ -8505,7 +8562,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 444 (green / turquoise) +# tile 448 (green / turquoise) { ................ ................ @@ -8524,7 +8581,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 445 (yellow / citrine) +# tile 449 (yellow / citrine) { ................ ................ @@ -8543,7 +8600,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 446 (green / aquamarine) +# tile 450 (green / aquamarine) { ................ ................ @@ -8562,7 +8619,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 447 (yellowish brown / amber) +# tile 451 (yellowish brown / amber) { ................ ................ @@ -8581,7 +8638,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 448 (yellowish brown / topaz) +# tile 452 (yellowish brown / topaz) { ................ ................ @@ -8600,7 +8657,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 449 (black / jet) +# tile 453 (black / jet) { ................ ................ @@ -8619,7 +8676,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 450 (white / opal) +# tile 454 (white / opal) { ................ ................ @@ -8638,7 +8695,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 451 (yellow / chrysoberyl) +# tile 455 (yellow / chrysoberyl) { ................ ................ @@ -8657,7 +8714,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 452 (red / garnet) +# tile 456 (red / garnet) { ................ ................ @@ -8676,7 +8733,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 453 (violet / amethyst) +# tile 457 (violet / amethyst) { ................ ................ @@ -8695,7 +8752,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 454 (red / jasper) +# tile 458 (red / jasper) { ................ ................ @@ -8714,7 +8771,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 455 (violet / fluorite) +# tile 459 (violet / fluorite) { ................ ................ @@ -8733,7 +8790,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 456 (black / obsidian) +# tile 460 (black / obsidian) { ................ ................ @@ -8752,7 +8809,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 457 (orange / agate) +# tile 461 (orange / agate) { ................ ................ @@ -8771,7 +8828,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 458 (green / jade) +# tile 462 (green / jade) { ................ ................ @@ -8790,7 +8847,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 459 (white / worthless piece of white glass) +# tile 463 (white / worthless piece of white glass) { ................ ................ @@ -8809,7 +8866,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 460 (blue / worthless piece of blue glass) +# tile 464 (blue / worthless piece of blue glass) { ................ ................ @@ -8828,7 +8885,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 461 (red / worthless piece of red glass) +# tile 465 (red / worthless piece of red glass) { ................ ................ @@ -8847,7 +8904,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 462 (yellowish brown / worthless piece of yellowish brown glass) +# tile 466 (yellowish brown / worthless piece of yellowish brown glass) { ................ ................ @@ -8866,7 +8923,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 463 (orange / worthless piece of orange glass) +# tile 467 (orange / worthless piece of orange glass) { ................ ................ @@ -8885,7 +8942,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 464 (yellow / worthless piece of yellow glass) +# tile 468 (yellow / worthless piece of yellow glass) { ................ ................ @@ -8904,7 +8961,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 465 (black / worthless piece of black glass) +# tile 469 (black / worthless piece of black glass) { ................ ................ @@ -8923,7 +8980,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 466 (green / worthless piece of green glass) +# tile 470 (green / worthless piece of green glass) { ................ ................ @@ -8942,7 +8999,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 467 (violet / worthless piece of violet glass) +# tile 471 (violet / worthless piece of violet glass) { ................ ................ @@ -8961,7 +9018,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 468 (gray / luckstone) +# tile 472 (gray / luckstone) { ................ ................ @@ -8980,7 +9037,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 469 (gray / loadstone) +# tile 473 (gray / loadstone) { ................ ................ @@ -8999,7 +9056,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 470 (gray / touchstone) +# tile 474 (gray / touchstone) { ................ ................ @@ -9018,7 +9075,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 471 (gray / flint) +# tile 475 (gray / flint) { ................ ................ @@ -9037,7 +9094,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 472 (rock) +# tile 476 (rock) { ................ ................ @@ -9056,7 +9113,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 473 (boulder) +# tile 477 (boulder) { ................ ................ @@ -9075,7 +9132,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 474 (statue) +# tile 478 (statue) { ................ ........JJ...... @@ -9094,7 +9151,7 @@ Z = (195, 195, 195) .....JJJJJJAA... ................ } -# tile 475 (heavy iron ball) +# tile 479 (heavy iron ball) { ................ ................ @@ -9113,7 +9170,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 476 (iron chain) +# tile 480 (iron chain) { ................ ................ @@ -9132,7 +9189,7 @@ Z = (195, 195, 195) ...........PP.PA ............AA.. } -# tile 477 (splash of venom / splash of blinding venom) +# tile 481 (splash of venom / splash of blinding venom) { ................ ................ @@ -9151,7 +9208,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 478 (splash of venom / splash of acid venom) +# tile 482 (splash of venom / splash of acid venom) { ................ ................ diff --git a/win/share/safeproc.c b/win/share/safeproc.c deleted file mode 100644 index 19e9aa9b6..000000000 --- a/win/share/safeproc.c +++ /dev/null @@ -1,596 +0,0 @@ -/* NetHack 3.7 safeproc.c */ -/* Copyright (c) Michael Allison, 2018 */ -/* NetHack may be freely redistributed. See license for details. */ - -/* must #define SAFEPROCS in xxxconf.h or via CFLAGS or this won't compile */ -#include "hack.h" - -/* - * *********************************************************** - * This is a complete WindowPort implementation that can be - * assigned to the windowproc function pointers very early - * in the startup initialization, perhaps immediately even. - * It requires only the following call: - * windowprocs = *get_safe_procs(0); - * - * The game startup can trigger functions in other modules - * that make assumptions on a WindowPort being available - * and bad things can happen if any function pointers are - * null at that time. - * - * Some ports prior to 3.6.2 made attempts to early init - * various pieces of one of their WindowPorts, but that - * caused conflicts if that particular WindowPort wasn't - * the one that the user ended up selecting in their - * config file later. The WindowPort interfaced was designed - * to allow multiple WindowPorts to be linked into the same - * game binary. - * - * The base functions established by a call to get_safe_procs() - * accomplish the goal of preventing crashes, but not much - * else. - * - * There are also a few additional functions provided in here - * that can be selected optionally to provide some startup - * functionality for getting messages out to the user about - * issues that are being experienced during startup in - * general or during options parsing. The ones in here are - * deliberately free from any platforms or OS specific code. - * Please leave them using stdio C routines as much as - * possible. That isn't to say you can't do fancier functions - * prior to initialization of the primary WindowPort, but you - * can provide those platform-specific functions elsewhere, - * and assign them the same way that these more generic versions - * are assigned. - * - * The additional platform-independent, but more functional - * routines provided in here should be assigned after the - * windowprocs = *get_safe_procs(n) - * call. - * - * Usage: - * - * windowprocs = *get_safe_procs(0); - * initializes a set of winprocs function pointers that ensure - * none of the function pointers are left null, but that's all - * it does. - * - * windowprocs = *get_safe_procs(1); - * initializes a set of winprocs functions pointers that ensure - * none of the function pointers are left null, but also - * provides some basic output and input functionality using - * nothing other than C stdio routines (no platform-specific - * or OS-specific code). - * - * *********************************************************** - */ - -void safe_dismiss_nhwindow(winid); -void safe_putstr(winid, int, const char *); -void win_safe_init(int); -void safe_number_pad(int); - -struct window_procs safe_procs = { - WPID(safestartup), - (0 -#ifdef TTY_PERM_INVENT - | WC_PERM_INVENT -#endif -#ifdef MSDOS - | WC_TILED_MAP | WC_ASCII_MAP -#endif -#if defined(WIN32CON) - | WC_MOUSE_SUPPORT -#endif - | WC_COLOR | WC_HILITE_PET | WC_INVERSE | WC_EIGHT_BIT_IN), - (0 -#if defined(SELECTSAVED) - | WC2_SELECTSAVED -#endif -#if defined(STATUS_HILITES) - | WC2_HILITE_STATUS | WC2_HITPOINTBAR | WC2_FLUSH_STATUS - | WC2_RESET_STATUS -#endif - | WC2_DARKGRAY | WC2_SUPPRESS_HIST | WC2_URGENT_MESG | WC2_STATUSLINES - | WC2_U_UTF8STR | WC2_PETATTR -#if !defined(NO_TERMS) || defined(WIN32CON) - | WC2_EXTRACOLORS -#endif - ), - {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* color availability */ - safe_init_nhwindows, safe_player_selection, safe_askname, - safe_get_nh_event, - safe_exit_nhwindows, safe_suspend_nhwindows, safe_resume_nhwindows, - safe_create_nhwindow, safe_clear_nhwindow, safe_display_nhwindow, - safe_destroy_nhwindow, safe_curs, safe_putstr, safe_putmixed, - safe_display_file, safe_start_menu, safe_add_menu, safe_end_menu, - safe_select_menu, safe_message_menu, - safe_mark_synch, - safe_wait_synch, -#ifdef CLIPPING - safe_cliparound, -#endif -#ifdef POSITIONBAR - safe_update_positionbar, -#endif - safe_print_glyph, safe_raw_print, safe_raw_print_bold, safe_nhgetch, - safe_nh_poskey, safe_nhbell, safe_doprev_message, safe_yn_function, - safe_getlin, safe_get_ext_cmd, safe_number_pad, safe_delay_output, -#ifdef CHANGE_COLOR /* the Mac uses a palette device */ - safe_change_color, -#ifdef MAC - safe_change_background, set_safe_font_name, -#endif - safe_get_color_string, -#endif - safe_outrip, - safe_preference_update, - safe_getmsghistory, safe_putmsghistory, - safe_status_init, - safe_status_finish, safe_status_enablefield, - safe_status_update, - safe_can_suspend, - safe_update_inventory, - safe_ctrl_nhwindow, -}; - -struct window_procs * -get_safe_procs(int optn) -{ - if (optn) { - /* include the slightly more functional stdc versions */ - safe_procs.win_raw_print = stdio_raw_print; - safe_procs.win_raw_print_bold = stdio_raw_print_bold; - safe_procs.win_nhgetch = stdio_nhgetch; - safe_procs.win_wait_synch = stdio_wait_synch; - if (optn == 2) - safe_procs.win_raw_print = stdio_nonl_raw_print; - } - return &safe_procs; -} - -/*ARGSUSED*/ -void -safe_init_nhwindows(int *argcp UNUSED, char **argv UNUSED) -{ - return; -} - -void -safe_player_selection(void) -{ - return; -} - -void -safe_askname(void) -{ - return; -} - -void -safe_get_nh_event(void) -{ - return; -} - -void -safe_suspend_nhwindows(const char *str UNUSED) -{ - return; -} - -void -safe_resume_nhwindows(void) -{ - return; -} - -void -safe_exit_nhwindows(const char *str UNUSED) -{ - return; -} - -winid -safe_create_nhwindow(int type UNUSED) -{ - return WIN_ERR; -} - -void -safe_clear_nhwindow(winid window UNUSED) -{ - return; -} - -/*ARGSUSED*/ -void -safe_display_nhwindow(winid window UNUSED, boolean blocking UNUSED) -{ - return; -} - -void -safe_dismiss_nhwindow(winid window UNUSED) -{ - return; -} - -void -safe_destroy_nhwindow(winid window UNUSED) -{ - return; -} - -void -safe_curs(winid window UNUSED, int x UNUSED, int y UNUSED) -{ - return; -} - -void -safe_putstr(winid window UNUSED, int attr UNUSED, const char *str UNUSED) -{ - return; -} - -void -safe_putmixed(winid window UNUSED, int attr UNUSED, const char *str UNUSED) -{ - return; -} - -void -safe_display_file(const char * fname UNUSED, boolean complain UNUSED) -{ - return; -} - -void -safe_start_menu(winid window UNUSED, unsigned long mbehavior UNUSED) -{ - return; -} - -/*ARGSUSED*/ -/* - * Add a menu item to the beginning of the menu list. This list is reversed - * later. - */ -void -safe_add_menu( - winid window UNUSED, /* window to use, must be of type NHW_MENU */ - const glyph_info *glyphinfo UNUSED, /* glyph plus glyph info */ - const anything *identifier UNUSED, /* what to return if selected */ - char ch UNUSED, /* keyboard accelerator (0 = pick our own) */ - char gch UNUSED, /* group accelerator (0 = no group) */ - int attr UNUSED, /* attribute for string (like safe_putstr()) */ - int clr UNUSED, /* colour for string */ - const char *str UNUSED, /* menu string */ - unsigned int itemflags UNUSED) /* itemflags such as marked as selected */ -{ - return; -} - -/* - * End a menu in this window, window must a type NHW_MENU. - */ -void -safe_end_menu( - winid window UNUSED, /* menu to use */ - const char *prompt UNUSED) /* prompt to for menu */ -{ - return; -} - -int -safe_select_menu( - winid window UNUSED, - int how UNUSED, - menu_item **menu_list UNUSED) -{ - return 0; -} - -/* special hack for treating top line --More-- as a one item menu */ -char -safe_message_menu( - char let UNUSED, - int how UNUSED, - const char *mesg UNUSED) -{ - return '\033'; -} - -void -safe_mark_synch(void) -{ -} - -void -safe_wait_synch(void) -{ -} - -#ifdef CLIPPING -void -safe_cliparound(int x UNUSED, int y UNUSED) -{ -} -#endif /* CLIPPING */ - -/* - * safe_print_glyph - * - * Print the glyph to the output device. Don't flush the output device. - */ -void -safe_print_glyph( - winid window UNUSED, - coordxy x UNUSED, - coordxy y UNUSED, - const glyph_info *glyphinfo UNUSED, - const glyph_info *bkglyphinfo UNUSED) -{ - return; -} - -void -safe_raw_print(const char *str UNUSED) -{ - return; -} - -void -safe_raw_print_bold(const char *str UNUSED) -{ - return; -} - -int -safe_nhgetch(void) -{ - return '\033'; -} - -/* - * return a key, or 0, in which case a mouse button was pressed - * mouse events should be returned as character positions in the map window. - * Since normal tty's don't have mice, just return a key. - */ -/*ARGSUSED*/ -int -safe_nh_poskey(coordxy *x UNUSED, coordxy *y UNUSED, int *mod UNUSED) -{ - return '\033'; -} - -void -win_safe_init(int dir UNUSED) -{ - return; -} - -#ifdef POSITIONBAR -void -safe_update_positionbar(char *posbar UNUSED) -{ - return; -} -#endif /* POSITIONBAR */ - -/* - * safe_status_init() - * -- initialize the port-specific data structures. - */ -void -safe_status_init(void) -{ - return; -} - -boolean -safe_can_suspend(void) -{ - return FALSE; -} - -void -safe_nhbell(void) -{ - return; -} - -int -safe_doprev_message(void) -{ - return 0; -} - -char -safe_yn_function(const char *query UNUSED, - const char *resp UNUSED, char def UNUSED) -{ - return '\033'; -} - -/*ARGSUSED*/ -void -safe_getlin(const char* prompt UNUSED, char *outbuf) -{ - Strcpy(outbuf, "\033"); -} - -int -safe_get_ext_cmd(void) -{ - return '\033'; -} - -void -safe_number_pad(int mode UNUSED) -{ - return; -} - -void -safe_delay_output(void) -{ - return; -} - -void -safe_outrip(winid tmpwin UNUSED, int how UNUSED, time_t when UNUSED) -{ - return; -} - -/*ARGSUSED*/ -void -safe_preference_update(const char *pref UNUSED) -{ - return; -} - -char * -safe_getmsghistory(boolean init UNUSED) -{ - return (char *) 0; -} - -void -safe_putmsghistory( - const char *msg UNUSED, - boolean is_restoring UNUSED) -{ -} - -void -safe_status_finish(void) -{ -} - -void -safe_status_enablefield( - int fieldidx UNUSED, - const char *nm UNUSED, - const char *fmt UNUSED, - boolean enable UNUSED) -{ -} - -/* call once for each field, then call with BL_FLUSH to output the result */ -void -safe_status_update( - int idx UNUSED, - genericptr_t ptr UNUSED, - int chg UNUSED, - int percent UNUSED, - int color UNUSED, - unsigned long *colormasks UNUSED) -{ -} - -void -safe_update_inventory(int arg UNUSED) -{ - return; -} - -#ifdef WIN32CON -extern win_request_info *tty_ctrl_nhwindow(winid window UNUSED, - int request UNUSED, - win_request_info *wri UNUSED); -#endif - -win_request_info * -safe_ctrl_nhwindow( - winid window UNUSED, - int request UNUSED, - win_request_info *wri UNUSED) -{ -#ifdef WIN32CON - return (*tty_ctrl_nhwindow)(window, request, wri); -#else - return (win_request_info *) 0; -#endif -} - -/************************************************************** - * These are some optionally selectable routines that add - * some base functionality over the safe_* versions above. - * The safe_* versions are primarily designed to ensure that - * there are no null function pointers remaining at early - * game startup/initialization time. - * - * The slightly more functional versions in here should be kept - * free of platform-specific code or OS-specific code. If you - * want to use versions that involve platform-specific or - * OS-specific code, go right ahead but use your own replacement - * version of the functions in a platform-specific or - * OS-specific source file, not in here. - ***************************************************************/ - -/* Add to your code: windowprocs.win_raw_print = stdio_wait_synch; */ -void -stdio_wait_synch(void) -{ - char valid[] = {' ', '\n', '\r', '\033', '\0'}; - - fprintf(stdout, "--More--"); - (void) fflush(stdout); - while (!strchr(valid, nhgetch())) - ; -} - -/* Add to your code: windowprocs.win_raw_print = stdio_raw_print; */ -void -stdio_raw_print(const char *str) -{ - if (str) - fprintf(stdout, "%s\n", str); - return; -} - -/* no newline variation, add to your code: - windowprocs.win_raw_print = stdio_nonl_raw_print; */ -void -stdio_nonl_raw_print(const char *str) -{ - if (str) - fprintf(stdout, "%s", str); - return; -} - -/* Add to your code: windowprocs.win_raw_print_bold = stdio_raw_print_bold; */ -void -stdio_raw_print_bold(const char *str) -{ - stdio_raw_print(str); - return; -} - -/* Add to your code: windowprocs.win_nhgetch = stdio_nhgetch; */ -int -stdio_nhgetch(void) -{ - return getchar(); -} - -#ifdef CHANGE_COLOR -void -safe_change_color(int color UNUSED, long rgb UNUSED, int reverse UNUSED) -{ -} - -char * -safe_get_color_string(void) -{ - return (""); -} - - -#endif - -/* safeprocs.c */ diff --git a/win/share/tilemap.c b/win/share/tilemap.c index 832bae769..41583dfa0 100644 --- a/win/share/tilemap.c +++ b/win/share/tilemap.c @@ -1235,11 +1235,11 @@ init_tilemap(void) Snprintf(tilemap[GLYPH_STATUE_MALE_OFF + i].name, sizeof tilemap[0].name, "statue of male %s (mnum=%d)", - tilename(MON_GLYPH, file_entry, 0), file_entry); + tilename(MON_GLYPH, file_entry, 0), i); Snprintf(tilemap[GLYPH_STATUE_MALE_PILETOP_OFF + i].name, sizeof tilemap[0].name, "piletop statue of male %s (mnum=%d)", - tilename(MON_GLYPH, file_entry, 0), file_entry); + tilename(MON_GLYPH, file_entry, 0), i); add_tileref(tilenum, GLYPH_STATUE_MALE_OFF + i, generated, file_entry, tilemap[GLYPH_STATUE_MALE_OFF + i].name, ""); @@ -1258,10 +1258,10 @@ init_tilemap(void) Snprintf(tilemap[GLYPH_STATUE_FEM_OFF + i].name, sizeof tilemap[0].name, "statue of female %s (mnum=%d)", - tilename(MON_GLYPH, file_entry, 0), file_entry); + tilename(MON_GLYPH, file_entry, 0), i); Sprintf(tilemap[GLYPH_STATUE_FEM_PILETOP_OFF + i].name, "piletop statue of female %s (mnum=%d)", - tilename(MON_GLYPH, file_entry, 0), file_entry); + tilename(MON_GLYPH, file_entry, 0), i); add_tileref(tilenum, GLYPH_STATUE_FEM_OFF + i, generated, file_entry, tilemap[GLYPH_STATUE_FEM_OFF + i].name, ""); add_tileref(tilenum, GLYPH_STATUE_FEM_PILETOP_OFF + i, generated, diff --git a/win/shim/winshim.c b/win/shim/winshim.c index ae4caed12..5e701c332 100644 --- a/win/shim/winshim.c +++ b/win/shim/winshim.c @@ -172,11 +172,11 @@ VDECLCB(shim_status_update, "vipiiip", A2P fldidx, P2V ptr, A2P chg, A2P percent, A2P color, P2V colormasks) #ifdef __EMSCRIPTEN__ -/* XXX: calling display_inventory() from shim_update_inventory() causes reentrancy that breaks emscripten Asyncify */ -/* this should be fine since according to windows.doc, the only purpose of shim_update_inventory() is to call display_inventory() */ +/* XXX: calling repopulate_perminvent() from shim_update_inventory() causes reentrancy that breaks emscripten Asyncify */ +/* this should be fine since according to windows.doc, the only purpose of shim_update_inventory() is to call repopulate_perminvent() */ void shim_update_inventory(int a1 UNUSED) { if(iflags.perm_invent) { - display_inventory(NULL, FALSE); + repopulate_perminvent(); } } @@ -191,7 +191,7 @@ win_request_info * shim_ctrl_nhwindow( winid window UNUSED, int request UNUSED, - win_request_info *wri) { + win_request_info *wri UNUSED) { return (win_request_info *) 0; } #else /* !__EMSCRIPTEN__ */ diff --git a/win/tty/termcap.c b/win/tty/termcap.c index 3af6333f9..ad8e25764 100644 --- a/win/tty/termcap.c +++ b/win/tty/termcap.c @@ -39,6 +39,7 @@ struct tc_lcl_data tc_lcl_data = { 0, 0, 0, 0, 0, 0, 0, FALSE }; static char *nh_VI = (char *) 0; /* cursor_invisible */ static char *nh_VE = (char *) 0; /* cursor_normal */ /*static char *nh_VS = (char *) 0;*/ /* cursor_visible (highlighted cursor) */ +static char *nh_Ic = (char *) 0; /* initialize_color */ static char *HO, *CL, *CE, *UP, *XD, *BC, *SO, *SE, *TI, *TE; static char *VS, *VE; @@ -290,6 +291,8 @@ term_startup(int *wid, int *hgt) if (!nh_VI || !nh_VE /*|| !nh_VS*/ ) nh_VI = nh_VE = /*nh_VS =*/ (char *) 0; + nh_Ic = Tgetstr(nhStr("Ic")); + /* Get rid of padding numbers for nh_HI and nh_HE. Hope they * aren't really needed!!! nh_HI and nh_HE are outputted to the * pager as a string - so how can you send it NULs??? @@ -1508,9 +1511,40 @@ term_curs_set(int visibility) #ifdef CHANGE_COLOR void -tty_change_color(int color UNUSED, long rgb UNUSED, int reverse UNUSED) +tty_change_color(int color, long rgb, int reverse UNUSED) { - return; + char buf[BUFSZ]; + int i; + char *c, *fmt; + + /* FIXME: colors are not reset back when exiting NetHack */ + if (nh_Ic && *nh_Ic) { + long clr = color, r, g, b; + + /* color * 3 seems to work correctly? */ + /* this probably depends on the termcap definition */ + r = ((rgb >> 16) & 0xFF) * 3; + g = ((rgb >> 8) & 0xFF) * 3; + b = (rgb & 0xFF) * 3; + + + fmt = tparm(nh_Ic, clr, r, g, b); + + c = fmt; + + i = 0; + while (*c) { + if (*c == '\033') { + buf[i++] = '\\'; + buf[i++] = 'E'; + } else { + buf[i++] = *c; + } + c++; + } + buf[i++] = '\0'; + xputs(fmt); + } } #endif /* CHANGE_COLOR */ diff --git a/win/tty/wintty.c b/win/tty/wintty.c index 61338542b..fb9ec4dbf 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -3559,8 +3559,9 @@ assesstty( short *offx, short *offy, long *rows, long *cols, long *maxcol, long *minrow, long *maxrow) { - boolean inuse_only = (invmode & InvInUse) != 0, - show_gold = (invmode & InvShowGold) != 0 && !inuse_only; + boolean inuse_only = ((int) invmode & (int) InvInUse) != 0, + show_gold = ((int) invmode & (int) InvShowGold) != 0 + && !inuse_only; int perminv_minrow = tty_perminv_minrow + (show_gold ? 1 : 0); if (!ttyDisplay) { diff --git a/win/win32/NetHackW.c b/win/win32/NetHackW.c index 45f8bf709..5a945af5c 100644 --- a/win/win32/NetHackW.c +++ b/win/win32/NetHackW.c @@ -15,10 +15,6 @@ #include "mhmain.h" #include "mhmap.h" -#if !defined(SAFEPROCS) -#error You must #define SAFEPROCS to build NetHackW.c -#endif - /* Borland and MinGW redefine "boolean" in shlwapi.h, so just use the little bit we need */ typedef struct _DLLVERSIONINFO { @@ -70,10 +66,6 @@ int GUILaunched = TRUE; /* We tell shared startup code in windmain.c #define _strdup(s1) strdup(s1) #endif -// Forward declarations of functions included in this code module: -extern boolean main(int, char **); -//static void __cdecl mswin_moveloop(void *); - #define MAX_CMDLINE_PARAM 255 #ifdef _MSC_VER @@ -103,32 +95,6 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, UNREFERENCED_PARAMETER(lpCmdLine); UNREFERENCED_PARAMETER(nCmdShow); - /* - * Get a set of valid safe windowport function - * pointers during early startup initialization. - * - * When get_safe_procs is called with 0 as the param, - * non-functional, but safe function pointers are set - * for all windowport routines. - * - * When get_safe_procs is called with 1 as the param, - * raw_print, raw_print_bold, and wait_synch, and nhgetch - * are set to use C stdio routines via stdio_raw_print, - * stdio_raw_print_bold, stdio_wait_synch, and - * stdio_nhgetch. - */ - windowprocs = *get_safe_procs(0); - - /* - * Now we are going to override a couple - * of the windowprocs functions so that - * error messages are handled in a suitable - * way for the graphical version. - */ - windowprocs.win_raw_print = mswin_raw_print; - windowprocs.win_raw_print_bold = mswin_raw_print_bold; - windowprocs.win_wait_synch = mswin_wait_synch; - win10_init(); early_init(0, NULL); /* Change as needed to support CRASHREPORT */ @@ -191,7 +157,7 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, Sprintf(buf2, "Cannot load common control library.\n%s\n%s", "For further information, refer to the installation notes at", INSTALL_NOTES); - panic(buf2); + panic("%s", buf2); } if (major < MIN_COMCTLMAJOR || (major == MIN_COMCTLMAJOR && minor < MIN_COMCTLMINOR)) { @@ -201,7 +167,7 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, MIN_COMCTLMINOR, "For further information, refer to the installation notes at", INSTALL_NOTES); - panic(buf2); + panic("%s", buf2); } ZeroMemory(&InitCtrls, sizeof(InitCtrls)); InitCtrls.dwSize = sizeof(InitCtrls); @@ -245,6 +211,12 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, free(savefile); } GUILaunched = 1; + /* emergency IO */ + windowprocs.win_raw_print = mswin_raw_print; + windowprocs.win_raw_print_bold = mswin_raw_print_bold; + windowprocs.win_nhgetch = mswin_nhgetch; + windowprocs.win_wait_synch = mswin_wait_synch; + /* let nethackw_main do the argument processing */ nethackw_main(argc, argv); /* not reached */ diff --git a/win/win32/mhmap.c b/win/win32/mhmap.c index d5e6b116e..46a7ee1d0 100644 --- a/win/win32/mhmap.c +++ b/win/win32/mhmap.c @@ -982,13 +982,12 @@ paintGlyph(PNHMapWindow data, int i, int j, RECT * rect) ch = glyphinfo->gm.u->utf32ch; } #endif - if ((glyphinfo->gm.customcolor & NH_BASIC_COLOR) == 0) { - rgbcolor = RGB((glyphinfo->gm.customcolor >> 16) & 0xFF, - (glyphinfo->gm.customcolor >> 8) & 0xFF, - (glyphinfo->gm.customcolor >> 0) & 0xFF); - } else { - color = (int) COLORVAL(glyphinfo->gm.customcolor); - rgbcolor = nhcolor_to_RGB(color); + if (glyphinfo->gm.customcolor != 0 + && (mswin_procs.wincap2 & WC2_EXTRACOLORS) != 0) { + if ((glyphinfo->gm.customcolor & NH_BASIC_COLOR) != 0) { + color = (int) COLORVAL(glyphinfo->gm.customcolor); + rgbcolor = nhcolor_to_RGB(color); + } } if (((data->map[i][j].gm.glyphflags & MG_PET) && iflags.hilite_pet) || ((data->map[i][j].gm.glyphflags & (MG_DETECT | MG_BW_LAVA diff --git a/win/win32/mswproc.c b/win/win32/mswproc.c index 06464c6de..55ba2f696 100644 --- a/win/win32/mswproc.c +++ b/win/win32/mswproc.c @@ -734,9 +734,6 @@ mswin_exit_nhwindows(const char *str) /* Write Window settings to the registry */ mswin_write_reg(); - /* set things back to failsafes */ - windowprocs = *get_safe_procs(0); - /* and make sure there is still a way to communicate something */ windowprocs.win_raw_print = mswin_raw_print; windowprocs.win_raw_print_bold = mswin_raw_print_bold; @@ -1248,7 +1245,7 @@ mswin_select_menu(winid wid, int how, MENU_ITEM_P **selected) /* -- Indicate to the window port that the inventory has been changed. - -- Merely calls display_inventory() for window-ports that leave the + -- Merely calls repopulate_perminvent() for window-ports that leave the window up, otherwise empty. */ void @@ -1257,7 +1254,7 @@ mswin_update_inventory(int arg) logDebug("mswin_update_inventory(%d)\n", arg); if (iflags.perm_invent && program_state.something_worth_saving && iflags.window_inited && WIN_INVEN != WIN_ERR) - display_inventory(NULL, FALSE); + repopulate_perminvent(); } win_request_info *